Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / ext-all-debug.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.DomHelper
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11  * from your DOM building code.</p>
12  *
13  * <p><b><u>DomHelper element specification object</u></b></p>
14  * <p>A specification object is used when creating elements. Attributes of this object
15  * are assumed to be element attributes, except for 4 special attributes:
16  * <div class="mdetail-params"><ul>
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19  * same kind of element definition objects to be created and appended. These can be nested
20  * as deep as you want.</div></li>
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22  * This will end up being either the "class" attribute on a HTML fragment or className
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
25  * </ul></div></p>
26  *
27  * <p><b><u>Insertion methods</u></b></p>
28  * <p>Commonly used insertion methods:
29  * <div class="mdetail-params"><ul>
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
36  * </ul></div></p>
37  *
38  * <p><b><u>Example</u></b></p>
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40  * element with id <tt>'my-div'</tt>:<br>
41  <pre><code>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
44 var spec = {
45     id: 'my-ul',
46     tag: 'ul',
47     cls: 'my-list',
48     // append children after creating
49     children: [     // may also specify 'cn' instead of 'children'
50         {tag: 'li', id: 'item0', html: 'List Item 0'},
51         {tag: 'li', id: 'item1', html: 'List Item 1'},
52         {tag: 'li', id: 'item2', html: 'List Item 2'}
53     ]
54 };
55 var list = dh.append(
56     'my-div', // the context element 'my-div' can either be the id or the actual node
57     spec      // the specification object
58 );
59  </code></pre></p>
60  * <p>Element creation specification parameters in this class may also be passed as an Array of
61  * specification objects. This can be used to insert multiple sibling nodes into an existing
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>
63 dh.append('my-ul', [
64     {tag: 'li', id: 'item3', html: 'List Item 3'},
65     {tag: 'li', id: 'item4', html: 'List Item 4'}
66 ]);
67  * </code></pre></p>
68  *
69  * <p><b><u>Templating</u></b></p>
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72  * insert new elements. Revisiting the example above, we could utilize templating this time:
73  * <pre><code>
74 // create the node
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
76 // get template
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
78
79 for(var i = 0; i < 5, i++){
80     tpl.append(list, [i]); // use template to append to the actual node
81 }
82  * </code></pre></p>
83  * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
85
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
89  * </code></pre></p>
90  *
91  * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
93
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
96     id: 'link1',
97     url: 'http://www.jackslocum.com/',
98     text: "Jack&#39;s Site"
99 });
100 tpl.append('blog-roll', {
101     id: 'link2',
102     url: 'http://www.dustindiaz.com/',
103     text: "Dustin&#39;s Site"
104 });
105  * </code></pre></p>
106  *
107  * <p><b><u>Compiling Templates</u></b></p>
108  * <p>Templates are applied using regular expressions. The performance is great, but if
109  * you are adding a bunch of DOM elements using the same template, you can increase
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112  * broken up at the different variable points and a dynamic function is created and eval'ed.
113  * The generated function performs string concatenation of these parts and the passed
114  * variables instead of using regular expressions.
115  * <pre><code>
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
117
118 var tpl = new Ext.DomHelper.createTemplate(html);
119 tpl.compile();
120
121 //... use template like normal
122  * </code></pre></p>
123  *
124  * <p><b><u>Performance Boost</u></b></p>
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126  * of DOM can significantly boost performance.</p>
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129  * results in the creation of a text node. Usage:</p>
130  * <pre><code>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
132  * </code></pre>
133  * @singleton
134  */
135 Ext.DomHelper = function(){
136     var tempTableEl = null,
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138         tableRe = /^table|tbody|tr|td$/i,
139         pub,
140         // kill repeat to save bytes
141         afterbegin = "afterbegin",
142         afterend = "afterend",
143         beforebegin = "beforebegin",
144         beforeend = "beforeend",
145         ts = '<table>',
146         te = '</table>',
147         tbs = ts+'<tbody>',
148         tbe = '</tbody>'+te,
149         trs = tbs + '<tr>',
150         tre = '</tr>'+tbe;
151
152     // private
153     function doInsert(el, o, returnElement, pos, sibling, append){
154         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
155         return returnElement ? Ext.get(newNode, true) : newNode;
156     }
157
158     // build as innerHTML where available
159     function createHtml(o){
160             var b = "",
161                 attr,
162                 val,
163                 key,
164                 keyVal,
165                 cn;
166
167         if(typeof o == 'string'){
168             b = o;
169         } else if (Ext.isArray(o)) {
170                 Ext.each(o, function(v) {
171                 b += createHtml(v);
172             });
173         } else {
174                 b += "<" + (o.tag = o.tag || "div");
175             Ext.iterate(o, function(attr, val){
176                 if(!/tag|children|cn|html$/i.test(attr)){
177                     if (Ext.isObject(val)) {
178                         b += " " + attr + "='";
179                         Ext.iterate(val, function(key, keyVal){
180                             b += key + ":" + keyVal + ";";
181                         });
182                         b += "'";
183                     }else{
184                         b += " " + ({cls : "class", htmlFor : "for"}[attr] || attr) + "='" + val + "'";
185                     }
186                 }
187             });
188                 // Now either just close the tag or try to add children and close the tag.
189                 if (emptyTags.test(o.tag)) {
190                     b += "/>";
191                 } else {
192                     b += ">";
193                     if ((cn = o.children || o.cn)) {
194                         b += createHtml(cn);
195                     } else if(o.html){
196                         b += o.html;
197                     }
198                     b += "</" + o.tag + ">";
199                 }
200         }
201         return b;
202     }
203
204     function ieTable(depth, s, h, e){
205         tempTableEl.innerHTML = [s, h, e].join('');
206         var i = -1,
207                 el = tempTableEl;
208         while(++i < depth){
209             el = el.firstChild;
210         }
211         return el;
212     }
213
214     /**
215      * @ignore
216      * Nasty code for IE's broken table implementation
217      */
218     function insertIntoTable(tag, where, el, html) {
219             var node,
220                 before;
221
222         tempTableEl = tempTableEl || document.createElement('div');
223
224             if(tag == 'td' && (where == afterbegin || where == beforeend) ||
225                !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
226             return;
227         }
228         before = where == beforebegin ? el :
229                                  where == afterend ? el.nextSibling :
230                                  where == afterbegin ? el.firstChild : null;
231
232         if (where == beforebegin || where == afterend) {
233                 el = el.parentNode;
234         }
235
236         if (tag == 'td' || (tag == "tr" && (where == beforeend || where == afterbegin))) {
237                 node = ieTable(4, trs, html, tre);
238         } else if ((tag == "tbody" && (where == beforeend || where == afterbegin)) ||
239                            (tag == "tr" && (where == beforebegin || where == afterend))) {
240                 node = ieTable(3, tbs, html, tbe);
241         } else {
242                 node = ieTable(2, ts, html, te);
243         }
244         el.insertBefore(node, before);
245         return node;
246     }
247
248
249     pub = {
250             /**
251              * Returns the markup for the passed Element(s) config.
252              * @param {Object} o The DOM object spec (and children)
253              * @return {String}
254              */
255             markup : function(o){
256                 return createHtml(o);
257             },
258
259             /**
260              * Inserts an HTML fragment into the DOM.
261              * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
262              * @param {HTMLElement} el The context element
263              * @param {String} html The HTML fragmenet
264              * @return {HTMLElement} The new node
265              */
266             insertHtml : function(where, el, html){
267                 var hash = {},
268                         hashVal,
269                         setStart,
270                         range,
271                         frag,
272                         rangeEl,
273                         rs;
274
275                 where = where.toLowerCase();
276                 // add these here because they are used in both branches of the condition.
277                 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
278                 hash[afterend] = ['AfterEnd', 'nextSibling'];
279
280                 if (el.insertAdjacentHTML) {
281                     if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
282                         return rs;
283                     }
284                     // add these two to the hash.
285                     hash[afterbegin] = ['AfterBegin', 'firstChild'];
286                     hash[beforeend] = ['BeforeEnd', 'lastChild'];
287                     if ((hashVal = hash[where])) {
288                                 el.insertAdjacentHTML(hashVal[0], html);
289                         return el[hashVal[1]];
290                     }
291                 } else {
292                         range = el.ownerDocument.createRange();
293                         setStart = "setStart" + (/end/i.test(where) ? "After" : "Before");
294                         if (hash[where]) {
295                                 range[setStart](el);
296                                 frag = range.createContextualFragment(html);
297                                 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
298                                 return el[(where == beforebegin ? "previous" : "next") + "Sibling"];
299                         } else {
300                                 rangeEl = (where == afterbegin ? "first" : "last") + "Child";
301                                 if (el.firstChild) {
302                                         range[setStart](el[rangeEl]);
303                                         frag = range.createContextualFragment(html);
304                         if(where == afterbegin){
305                             el.insertBefore(frag, el.firstChild);
306                         }else{
307                             el.appendChild(frag);
308                         }
309                                 } else {
310                                     el.innerHTML = html;
311                             }
312                             return el[rangeEl];
313                         }
314                 }
315                 throw 'Illegal insertion point -> "' + where + '"';
316             },
317
318             /**
319              * Creates new DOM element(s) and inserts them before el.
320              * @param {Mixed} el The context element
321              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
322              * @param {Boolean} returnElement (optional) true to return a Ext.Element
323              * @return {HTMLElement/Ext.Element} The new node
324              */
325             insertBefore : function(el, o, returnElement){
326                 return doInsert(el, o, returnElement, beforebegin);
327             },
328
329             /**
330              * Creates new DOM element(s) and inserts them after el.
331              * @param {Mixed} el The context element
332              * @param {Object} o The DOM object spec (and children)
333              * @param {Boolean} returnElement (optional) true to return a Ext.Element
334              * @return {HTMLElement/Ext.Element} The new node
335              */
336             insertAfter : function(el, o, returnElement){
337                 return doInsert(el, o, returnElement, afterend, "nextSibling");
338             },
339
340             /**
341              * Creates new DOM element(s) and inserts them as the first child of el.
342              * @param {Mixed} el The context element
343              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
344              * @param {Boolean} returnElement (optional) true to return a Ext.Element
345              * @return {HTMLElement/Ext.Element} The new node
346              */
347             insertFirst : function(el, o, returnElement){
348                 return doInsert(el, o, returnElement, afterbegin, "firstChild");
349             },
350
351             /**
352              * Creates new DOM element(s) and appends them to el.
353              * @param {Mixed} el The context element
354              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
355              * @param {Boolean} returnElement (optional) true to return a Ext.Element
356              * @return {HTMLElement/Ext.Element} The new node
357              */
358             append : function(el, o, returnElement){
359                     return doInsert(el, o, returnElement, beforeend, "", true);
360             },
361
362             /**
363              * Creates new DOM element(s) and overwrites the contents of el with them.
364              * @param {Mixed} el The context element
365              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
366              * @param {Boolean} returnElement (optional) true to return a Ext.Element
367              * @return {HTMLElement/Ext.Element} The new node
368              */
369             overwrite : function(el, o, returnElement){
370                 el = Ext.getDom(el);
371                 el.innerHTML = createHtml(o);
372                 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
373             },
374
375             createHtml : createHtml
376     };
377     return pub;
378 }();/**\r
379  * @class Ext.DomHelper\r
380  */\r
381 Ext.apply(Ext.DomHelper,\r
382 function(){\r
383         var pub,\r
384                 afterbegin = 'afterbegin',\r
385         afterend = 'afterend',\r
386         beforebegin = 'beforebegin',\r
387         beforeend = 'beforeend';\r
388 \r
389         // private\r
390     function doInsert(el, o, returnElement, pos, sibling, append){\r
391         el = Ext.getDom(el);\r
392         var newNode;\r
393         if (pub.useDom) {\r
394             newNode = createDom(o, null);\r
395             if (append) {\r
396                     el.appendChild(newNode);\r
397             } else {\r
398                         (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);\r
399             }\r
400         } else {\r
401             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));\r
402         }\r
403         return returnElement ? Ext.get(newNode, true) : newNode;\r
404     }\r
405 \r
406         // build as dom\r
407     /** @ignore */\r
408     function createDom(o, parentNode){\r
409         var el,\r
410                 doc = document,\r
411                 useSet,\r
412                 attr,\r
413                 val,\r
414                 cn;\r
415 \r
416         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted\r
417             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment\r
418                 Ext.each(o, function(v) {\r
419                 createDom(v, el);\r
420             });\r
421         } else if (Ext.isString(o)) {         // Allow a string as a child spec.\r
422             el = doc.createTextNode(o);\r
423         } else {\r
424             el = doc.createElement( o.tag || 'div' );\r
425             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute\r
426             Ext.iterate(o, function(attr, val){\r
427                 if(!/tag|children|cn|html|style/.test(attr)){\r
428                         if(attr == 'cls'){\r
429                             el.className = val;\r
430                         }else{\r
431                         if(useSet){\r
432                             el.setAttribute(attr, val);\r
433                         }else{\r
434                             el[attr] = val;\r
435                         }\r
436                         }\r
437                 }\r
438             });\r
439             pub.applyStyles(el, o.style);\r
440 \r
441             if ((cn = o.children || o.cn)) {\r
442                 createDom(cn, el);\r
443             } else if (o.html) {\r
444                 el.innerHTML = o.html;\r
445             }\r
446         }\r
447         if(parentNode){\r
448            parentNode.appendChild(el);\r
449         }\r
450         return el;\r
451     }\r
452 \r
453         pub = {\r
454                 /**\r
455              * Creates a new Ext.Template from the DOM object spec.\r
456              * @param {Object} o The DOM object spec (and children)\r
457              * @return {Ext.Template} The new template\r
458              */\r
459             createTemplate : function(o){\r
460                 var html = Ext.DomHelper.createHtml(o);\r
461                 return new Ext.Template(html);\r
462             },\r
463 \r
464                 /** True to force the use of DOM instead of html fragments @type Boolean */\r
465             useDom : false,\r
466 \r
467             /**\r
468              * Applies a style specification to an element.\r
469              * @param {String/HTMLElement} el The element to apply styles to\r
470              * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or\r
471              * a function which returns such a specification.\r
472              */\r
473             applyStyles : function(el, styles){\r
474                     if(styles){\r
475                                 var i = 0,\r
476                                 len,\r
477                                 style;\r
478 \r
479                         el = Ext.fly(el);\r
480                                 if(Ext.isFunction(styles)){\r
481                                         styles = styles.call();\r
482                                 }\r
483                                 if(Ext.isString(styles)){\r
484                                         styles = styles.trim().split(/\s*(?::|;)\s*/);\r
485                                         for(len = styles.length; i < len;){\r
486                                                 el.setStyle(styles[i++], styles[i++]);\r
487                                         }\r
488                                 }else if (Ext.isObject(styles)){\r
489                                         el.setStyle(styles);\r
490                                 }\r
491                         }\r
492             },\r
493 \r
494             /**\r
495              * Creates new DOM element(s) and inserts them before el.\r
496              * @param {Mixed} el The context element\r
497              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
498              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
499              * @return {HTMLElement/Ext.Element} The new node\r
500          * @hide (repeat)\r
501              */\r
502             insertBefore : function(el, o, returnElement){\r
503                 return doInsert(el, o, returnElement, beforebegin);\r
504             },\r
505 \r
506             /**\r
507              * Creates new DOM element(s) and inserts them after el.\r
508              * @param {Mixed} el The context element\r
509              * @param {Object} o The DOM object spec (and children)\r
510              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
511              * @return {HTMLElement/Ext.Element} The new node\r
512          * @hide (repeat)\r
513              */\r
514             insertAfter : function(el, o, returnElement){\r
515                 return doInsert(el, o, returnElement, afterend, 'nextSibling');\r
516             },\r
517 \r
518             /**\r
519              * Creates new DOM element(s) and inserts them as the first child of el.\r
520              * @param {Mixed} el The context element\r
521              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
522              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
523              * @return {HTMLElement/Ext.Element} The new node\r
524          * @hide (repeat)\r
525              */\r
526             insertFirst : function(el, o, returnElement){\r
527                 return doInsert(el, o, returnElement, afterbegin, 'firstChild');\r
528             },\r
529 \r
530             /**\r
531              * Creates new DOM element(s) and appends them to el.\r
532              * @param {Mixed} el The context element\r
533              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
534              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
535              * @return {HTMLElement/Ext.Element} The new node\r
536          * @hide (repeat)\r
537              */\r
538             append: function(el, o, returnElement){\r
539             return doInsert(el, o, returnElement, beforeend, '', true);\r
540         },\r
541 \r
542             /**\r
543              * Creates new DOM element(s) without inserting them to the document.\r
544              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
545              * @return {HTMLElement} The new uninserted node\r
546              */\r
547         createDom: createDom\r
548         };\r
549         return pub;\r
550 }());/**
551  * @class Ext.Template
552  * Represents an HTML fragment template. Templates can be precompiled for greater performance.
553  * For a list of available format functions, see {@link Ext.util.Format}.<br />
554  * Usage:
555 <pre><code>
556 var t = new Ext.Template(
557     '&lt;div name="{id}"&gt;',
558         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
559     '&lt;/div&gt;'
560 );
561 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
562 </code></pre>
563  * @constructor
564  * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
565  */
566 Ext.Template = function(html){
567     var me = this,
568         a = arguments,
569         buf = [];
570
571     if (Ext.isArray(html)) {
572         html = html.join("");
573     } else if (a.length > 1) {
574             Ext.each(a, function(v) {
575             if (Ext.isObject(v)) {
576                 Ext.apply(me, v);
577             } else {
578                 buf.push(v);
579             }
580         });
581         html = buf.join('');
582     }
583
584     /**@private*/
585     me.html = html;
586     if (me.compiled) {
587         me.compile();
588     }
589 };
590 Ext.Template.prototype = {
591     /**
592      * Returns an HTML fragment of this template with the specified values applied.
593      * @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'})
594      * @return {String} The HTML fragment
595      */
596     applyTemplate : function(values){
597                 var me = this;
598
599         return me.compiled ?
600                         me.compiled(values) :
601                                 me.html.replace(me.re, function(m, name){
602                                 return values[name] !== undefined ? values[name] : "";
603                         });
604         },
605
606     /**
607      * Sets the HTML used as the template and optionally compiles it.
608      * @param {String} html
609      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
610      * @return {Ext.Template} this
611      */
612     set : function(html, compile){
613             var me = this;
614         me.html = html;
615         me.compiled = null;
616         return compile ? me.compile() : me;
617     },
618
619     /**
620     * The regular expression used to match template variables
621     * @type RegExp
622     * @property
623     */
624     re : /\{([\w-]+)\}/g,
625
626     /**
627      * Compiles the template into an internal function, eliminating the RegEx overhead.
628      * @return {Ext.Template} this
629      */
630     compile : function(){
631         var me = this,
632                 sep = Ext.isGecko ? "+" : ",";
633
634         function fn(m, name){                        
635                 name = "values['" + name + "']";
636                 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
637         }
638                 
639         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
640              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
641              (Ext.isGecko ?  "';};" : "'].join('');};"));
642         return me;
643     },
644
645     /**
646      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
647      * @param {Mixed} el The context element
648      * @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'})
649      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
650      * @return {HTMLElement/Ext.Element} The new node or Element
651      */
652     insertFirst: function(el, values, returnElement){
653         return this.doInsert('afterBegin', el, values, returnElement);
654     },
655
656     /**
657      * Applies the supplied values to the template and inserts the new node(s) before el.
658      * @param {Mixed} el The context element
659      * @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'})
660      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
661      * @return {HTMLElement/Ext.Element} The new node or Element
662      */
663     insertBefore: function(el, values, returnElement){
664         return this.doInsert('beforeBegin', el, values, returnElement);
665     },
666
667     /**
668      * Applies the supplied values to the template and inserts the new node(s) after el.
669      * @param {Mixed} el The context element
670      * @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'})
671      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
672      * @return {HTMLElement/Ext.Element} The new node or Element
673      */
674     insertAfter : function(el, values, returnElement){
675         return this.doInsert('afterEnd', el, values, returnElement);
676     },
677
678     /**
679      * Applies the supplied values to the template and appends the new node(s) to el.
680      * @param {Mixed} el The context element
681      * @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'})
682      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
683      * @return {HTMLElement/Ext.Element} The new node or Element
684      */
685     append : function(el, values, returnElement){
686         return this.doInsert('beforeEnd', el, values, returnElement);
687     },
688
689     doInsert : function(where, el, values, returnEl){
690         el = Ext.getDom(el);
691         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
692         return returnEl ? Ext.get(newNode, true) : newNode;
693     },
694
695     /**
696      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
697      * @param {Mixed} el The context element
698      * @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'})
699      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
700      * @return {HTMLElement/Ext.Element} The new node or Element
701      */
702     overwrite : function(el, values, returnElement){
703         el = Ext.getDom(el);
704         el.innerHTML = this.applyTemplate(values);
705         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
706     }
707 };
708 /**
709  * Alias for {@link #applyTemplate}
710  * Returns an HTML fragment of this template with the specified values applied.
711  * @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'})
712  * @return {String} The HTML fragment
713  * @member Ext.Template
714  * @method apply
715  */
716 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
717
718 /**
719  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
720  * @param {String/HTMLElement} el A DOM element or its id
721  * @param {Object} config A configuration object
722  * @return {Ext.Template} The created template
723  * @static
724  */
725 Ext.Template.from = function(el, config){
726     el = Ext.getDom(el);
727     return new Ext.Template(el.value || el.innerHTML, config || '');
728 };/**\r
729  * @class Ext.Template\r
730  */\r
731 Ext.apply(Ext.Template.prototype, {\r
732     /**\r
733      * Returns an HTML fragment of this template with the specified values applied.\r
734      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})\r
735      * @return {String} The HTML fragment\r
736      * @hide repeat doc\r
737      */\r
738     applyTemplate : function(values){\r
739                 var me = this,\r
740                         useF = me.disableFormats !== true,\r
741                 fm = Ext.util.Format, \r
742                 tpl = me;           \r
743             \r
744         if(me.compiled){\r
745             return me.compiled(values);\r
746         }\r
747         function fn(m, name, format, args){\r
748             if (format && useF) {\r
749                 if (format.substr(0, 5) == "this.") {\r
750                     return tpl.call(format.substr(5), values[name], values);\r
751                 } else {\r
752                     if (args) {\r
753                         // quoted values are required for strings in compiled templates,\r
754                         // but for non compiled we need to strip them\r
755                         // quoted reversed for jsmin\r
756                         var re = /^\s*['"](.*)["']\s*$/;\r
757                         args = args.split(',');\r
758                         for(var i = 0, len = args.length; i < len; i++){\r
759                             args[i] = args[i].replace(re, "$1");\r
760                         }\r
761                         args = [values[name]].concat(args);\r
762                     } else {\r
763                         args = [values[name]];\r
764                     }\r
765                     return fm[format].apply(fm, args);\r
766                 }\r
767             } else {\r
768                 return values[name] !== undefined ? values[name] : "";\r
769             }\r
770         }\r
771         return me.html.replace(me.re, fn);\r
772     },\r
773                 \r
774     /**\r
775      * <tt>true</tt> to disable format functions (defaults to <tt>false</tt>)\r
776      * @type Boolean\r
777      * @property\r
778      */\r
779     disableFormats : false,                             \r
780         \r
781     /**\r
782      * The regular expression used to match template variables\r
783      * @type RegExp\r
784      * @property\r
785      * @hide repeat doc\r
786      */\r
787     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,\r
788     \r
789     /**\r
790      * Compiles the template into an internal function, eliminating the RegEx overhead.\r
791      * @return {Ext.Template} this\r
792      * @hide repeat doc\r
793      */\r
794     compile : function(){\r
795         var me = this,\r
796                 fm = Ext.util.Format,\r
797                 useF = me.disableFormats !== true,\r
798                 sep = Ext.isGecko ? "+" : ",",\r
799                 body;\r
800         \r
801         function fn(m, name, format, args){\r
802             if(format && useF){\r
803                 args = args ? ',' + args : "";\r
804                 if(format.substr(0, 5) != "this."){\r
805                     format = "fm." + format + '(';\r
806                 }else{\r
807                     format = 'this.call("'+ format.substr(5) + '", ';\r
808                     args = ", values";\r
809                 }\r
810             }else{\r
811                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";\r
812             }\r
813             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";\r
814         }\r
815         \r
816         // branched to use + in gecko and [].join() in others\r
817         if(Ext.isGecko){\r
818             body = "this.compiled = function(values){ return '" +\r
819                    me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +\r
820                     "';};";\r
821         }else{\r
822             body = ["this.compiled = function(values){ return ['"];\r
823             body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));\r
824             body.push("'].join('');};");\r
825             body = body.join('');\r
826         }\r
827         eval(body);\r
828         return me;\r
829     },\r
830     \r
831     // private function used to call members\r
832     call : function(fnName, value, allValues){\r
833         return this[fnName](value, allValues);\r
834     }\r
835 });\r
836 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*\r
837  * This is code is also distributed under MIT license for use\r
838  * with jQuery and prototype JavaScript libraries.\r
839  */\r
840 /**\r
841  * @class Ext.DomQuery\r
842 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).\r
843 <p>\r
844 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>\r
845 \r
846 <p>\r
847 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.\r
848 </p>\r
849 <h4>Element Selectors:</h4>\r
850 <ul class="list">\r
851     <li> <b>*</b> any element</li>\r
852     <li> <b>E</b> an element with the tag E</li>\r
853     <li> <b>E F</b> All descendent elements of E that have the tag F</li>\r
854     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>\r
855     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>\r
856     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>\r
857 </ul>\r
858 <h4>Attribute Selectors:</h4>\r
859 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>\r
860 <ul class="list">\r
861     <li> <b>E[foo]</b> has an attribute "foo"</li>\r
862     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>\r
863     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>\r
864     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>\r
865     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>\r
866     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>\r
867     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>\r
868 </ul>\r
869 <h4>Pseudo Classes:</h4>\r
870 <ul class="list">\r
871     <li> <b>E:first-child</b> E is the first child of its parent</li>\r
872     <li> <b>E:last-child</b> E is the last child of its parent</li>\r
873     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>\r
874     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>\r
875     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>\r
876     <li> <b>E:only-child</b> E is the only child of its parent</li>\r
877     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>\r
878     <li> <b>E:first</b> the first E in the resultset</li>\r
879     <li> <b>E:last</b> the last E in the resultset</li>\r
880     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>\r
881     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>\r
882     <li> <b>E:even</b> shortcut for :nth-child(even)</li>\r
883     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>\r
884     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>\r
885     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>\r
886     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>\r
887     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>\r
888     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>\r
889 </ul>\r
890 <h4>CSS Value Selectors:</h4>\r
891 <ul class="list">\r
892     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>\r
893     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>\r
894     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>\r
895     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>\r
896     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>\r
897     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>\r
898 </ul>\r
899  * @singleton\r
900  */\r
901 Ext.DomQuery = function(){\r
902     var cache = {}, \r
903         simpleCache = {}, \r
904         valueCache = {},\r
905         nonSpace = /\S/,\r
906         trimRe = /^\s+|\s+$/g,\r
907         tplRe = /\{(\d+)\}/g,\r
908         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,\r
909         tagTokenRe = /^(#)?([\w-\*]+)/,\r
910         nthRe = /(\d*)n\+?(\d*)/, \r
911         nthRe2 = /\D/,\r
912         // This is for IE MSXML which does not support expandos.\r
913             // IE runs the same speed using setAttribute, however FF slows way down\r
914             // and Safari completely fails so they need to continue to use expandos.\r
915             isIE = window.ActiveXObject ? true : false,\r
916         isOpera = Ext.isOpera,\r
917             key = 30803;\r
918             \r
919     // this eval is stop the compressor from\r
920         // renaming the variable to something shorter\r
921         eval("var batch = 30803;");     \r
922 \r
923     function child(p, index){\r
924         var i = 0,\r
925                 n = p.firstChild;\r
926         while(n){\r
927             if(n.nodeType == 1){\r
928                if(++i == index){\r
929                    return n;\r
930                }\r
931             }\r
932             n = n.nextSibling;\r
933         }\r
934         return null;\r
935     };\r
936 \r
937     function next(n){\r
938         while((n = n.nextSibling) && n.nodeType != 1);\r
939         return n;\r
940     };\r
941 \r
942     function prev(n){\r
943         while((n = n.previousSibling) && n.nodeType != 1);\r
944         return n;\r
945     };\r
946 \r
947     function children(d){\r
948         var n = d.firstChild, ni = -1,\r
949                 nx;\r
950             while(n){\r
951                 nx = n.nextSibling;\r
952                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
953                     d.removeChild(n);\r
954                 }else{\r
955                     n.nodeIndex = ++ni;\r
956                 }\r
957                 n = nx;\r
958             }\r
959             return this;\r
960         };\r
961 \r
962     function byClassName(c, a, v){\r
963         if(!v){\r
964             return c;\r
965         }\r
966         var r = [], ri = -1, cn;\r
967         for(var i = 0, ci; ci = c[i]; i++){\r
968             if((' '+ci.className+' ').indexOf(v) != -1){\r
969                 r[++ri] = ci;\r
970             }\r
971         }\r
972         return r;\r
973     };\r
974 \r
975     function attrValue(n, attr){\r
976         if(!n.tagName && typeof n.length != "undefined"){\r
977             n = n[0];\r
978         }\r
979         if(!n){\r
980             return null;\r
981         }\r
982         if(attr == "for"){\r
983             return n.htmlFor;\r
984         }\r
985         if(attr == "class" || attr == "className"){\r
986             return n.className;\r
987         }\r
988         return n.getAttribute(attr) || n[attr];\r
989 \r
990     };\r
991 \r
992     function getNodes(ns, mode, tagName){\r
993         var result = [], ri = -1, cs;\r
994         if(!ns){\r
995             return result;\r
996         }\r
997         tagName = tagName || "*";\r
998         if(typeof ns.getElementsByTagName != "undefined"){\r
999             ns = [ns];\r
1000         }\r
1001         if(!mode){\r
1002             for(var i = 0, ni; ni = ns[i]; i++){\r
1003                 cs = ni.getElementsByTagName(tagName);\r
1004                 for(var j = 0, ci; ci = cs[j]; j++){\r
1005                     result[++ri] = ci;\r
1006                 }\r
1007             }\r
1008         }else if(mode == "/" || mode == ">"){\r
1009             var utag = tagName.toUpperCase();\r
1010             for(var i = 0, ni, cn; ni = ns[i]; i++){\r
1011                 cn = isOpera ? ni.childNodes : (ni.children || ni.childNodes);\r
1012                 for(var j = 0, cj; cj = cn[j]; j++){\r
1013                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){\r
1014                         result[++ri] = cj;\r
1015                     }\r
1016                 }\r
1017             }\r
1018         }else if(mode == "+"){\r
1019             var utag = tagName.toUpperCase();\r
1020             for(var i = 0, n; n = ns[i]; i++){\r
1021                 while((n = n.nextSibling) && n.nodeType != 1);\r
1022                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){\r
1023                     result[++ri] = n;\r
1024                 }\r
1025             }\r
1026         }else if(mode == "~"){\r
1027             var utag = tagName.toUpperCase();\r
1028             for(var i = 0, n; n = ns[i]; i++){\r
1029                 while((n = n.nextSibling)){\r
1030                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){\r
1031                         result[++ri] = n;\r
1032                     }\r
1033                 }\r
1034             }\r
1035         }\r
1036         return result;\r
1037     };\r
1038 \r
1039     function concat(a, b){\r
1040         if(b.slice){\r
1041             return a.concat(b);\r
1042         }\r
1043         for(var i = 0, l = b.length; i < l; i++){\r
1044             a[a.length] = b[i];\r
1045         }\r
1046         return a;\r
1047     }\r
1048 \r
1049     function byTag(cs, tagName){\r
1050         if(cs.tagName || cs == document){\r
1051             cs = [cs];\r
1052         }\r
1053         if(!tagName){\r
1054             return cs;\r
1055         }\r
1056         var r = [], ri = -1;\r
1057         tagName = tagName.toLowerCase();\r
1058         for(var i = 0, ci; ci = cs[i]; i++){\r
1059             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){\r
1060                 r[++ri] = ci;\r
1061             }\r
1062         }\r
1063         return r;\r
1064     };\r
1065 \r
1066     function byId(cs, attr, id){\r
1067         if(cs.tagName || cs == document){\r
1068             cs = [cs];\r
1069         }\r
1070         if(!id){\r
1071             return cs;\r
1072         }\r
1073         var r = [], ri = -1;\r
1074         for(var i = 0,ci; ci = cs[i]; i++){\r
1075             if(ci && ci.id == id){\r
1076                 r[++ri] = ci;\r
1077                 return r;\r
1078             }\r
1079         }\r
1080         return r;\r
1081     };\r
1082 \r
1083     function byAttribute(cs, attr, value, op, custom){\r
1084         var r = [], \r
1085                 ri = -1, \r
1086                 st = custom=="{",\r
1087                 f = Ext.DomQuery.operators[op];\r
1088         for(var i = 0, ci; ci = cs[i]; i++){\r
1089             if(ci.nodeType != 1){\r
1090                 continue;\r
1091             }\r
1092             var a;\r
1093             if(st){\r
1094                 a = Ext.DomQuery.getStyle(ci, attr);\r
1095             }\r
1096             else if(attr == "class" || attr == "className"){\r
1097                 a = ci.className;\r
1098             }else if(attr == "for"){\r
1099                 a = ci.htmlFor;\r
1100             }else if(attr == "href"){\r
1101                 a = ci.getAttribute("href", 2);\r
1102             }else{\r
1103                 a = ci.getAttribute(attr);\r
1104             }\r
1105             if((f && f(a, value)) || (!f && a)){\r
1106                 r[++ri] = ci;\r
1107             }\r
1108         }\r
1109         return r;\r
1110     };\r
1111 \r
1112     function byPseudo(cs, name, value){\r
1113         return Ext.DomQuery.pseudos[name](cs, value);\r
1114     };\r
1115 \r
1116     function nodupIEXml(cs){\r
1117         var d = ++key, \r
1118                 r;\r
1119         cs[0].setAttribute("_nodup", d);\r
1120         r = [cs[0]];\r
1121         for(var i = 1, len = cs.length; i < len; i++){\r
1122             var c = cs[i];\r
1123             if(!c.getAttribute("_nodup") != d){\r
1124                 c.setAttribute("_nodup", d);\r
1125                 r[r.length] = c;\r
1126             }\r
1127         }\r
1128         for(var i = 0, len = cs.length; i < len; i++){\r
1129             cs[i].removeAttribute("_nodup");\r
1130         }\r
1131         return r;\r
1132     }\r
1133 \r
1134     function nodup(cs){\r
1135         if(!cs){\r
1136             return [];\r
1137         }\r
1138         var len = cs.length, c, i, r = cs, cj, ri = -1;\r
1139         if(!len || typeof cs.nodeType != "undefined" || len == 1){\r
1140             return cs;\r
1141         }\r
1142         if(isIE && typeof cs[0].selectSingleNode != "undefined"){\r
1143             return nodupIEXml(cs);\r
1144         }\r
1145         var d = ++key;\r
1146         cs[0]._nodup = d;\r
1147         for(i = 1; c = cs[i]; i++){\r
1148             if(c._nodup != d){\r
1149                 c._nodup = d;\r
1150             }else{\r
1151                 r = [];\r
1152                 for(var j = 0; j < i; j++){\r
1153                     r[++ri] = cs[j];\r
1154                 }\r
1155                 for(j = i+1; cj = cs[j]; j++){\r
1156                     if(cj._nodup != d){\r
1157                         cj._nodup = d;\r
1158                         r[++ri] = cj;\r
1159                     }\r
1160                 }\r
1161                 return r;\r
1162             }\r
1163         }\r
1164         return r;\r
1165     }\r
1166 \r
1167     function quickDiffIEXml(c1, c2){\r
1168         var d = ++key,\r
1169                 r = [];\r
1170         for(var i = 0, len = c1.length; i < len; i++){\r
1171             c1[i].setAttribute("_qdiff", d);\r
1172         }        \r
1173         for(var i = 0, len = c2.length; i < len; i++){\r
1174             if(c2[i].getAttribute("_qdiff") != d){\r
1175                 r[r.length] = c2[i];\r
1176             }\r
1177         }\r
1178         for(var i = 0, len = c1.length; i < len; i++){\r
1179            c1[i].removeAttribute("_qdiff");\r
1180         }\r
1181         return r;\r
1182     }\r
1183 \r
1184     function quickDiff(c1, c2){\r
1185         var len1 = c1.length,\r
1186                 d = ++key,\r
1187                 r = [];\r
1188         if(!len1){\r
1189             return c2;\r
1190         }\r
1191         if(isIE && c1[0].selectSingleNode){\r
1192             return quickDiffIEXml(c1, c2);\r
1193         }        \r
1194         for(var i = 0; i < len1; i++){\r
1195             c1[i]._qdiff = d;\r
1196         }        \r
1197         for(var i = 0, len = c2.length; i < len; i++){\r
1198             if(c2[i]._qdiff != d){\r
1199                 r[r.length] = c2[i];\r
1200             }\r
1201         }\r
1202         return r;\r
1203     }\r
1204 \r
1205     function quickId(ns, mode, root, id){\r
1206         if(ns == root){\r
1207            var d = root.ownerDocument || root;\r
1208            return d.getElementById(id);\r
1209         }\r
1210         ns = getNodes(ns, mode, "*");\r
1211         return byId(ns, null, id);\r
1212     }\r
1213 \r
1214     return {\r
1215         getStyle : function(el, name){\r
1216             return Ext.fly(el).getStyle(name);\r
1217         },\r
1218         /**\r
1219          * Compiles a selector/xpath query into a reusable function. The returned function\r
1220          * takes one parameter "root" (optional), which is the context node from where the query should start.\r
1221          * @param {String} selector The selector/xpath query\r
1222          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match\r
1223          * @return {Function}\r
1224          */\r
1225         compile : function(path, type){\r
1226             type = type || "select";\r
1227 \r
1228             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],\r
1229                 q = path, mode, lq,\r
1230                 tk = Ext.DomQuery.matchers,\r
1231                 tklen = tk.length,\r
1232                 mm,\r
1233                 // accept leading mode switch\r
1234                 lmode = q.match(modeRe);\r
1235             \r
1236             if(lmode && lmode[1]){\r
1237                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';\r
1238                 q = q.replace(lmode[1], "");\r
1239             }\r
1240             // strip leading slashes\r
1241             while(path.substr(0, 1)=="/"){\r
1242                 path = path.substr(1);\r
1243             }\r
1244 \r
1245             while(q && lq != q){\r
1246                 lq = q;\r
1247                 var tm = q.match(tagTokenRe);\r
1248                 if(type == "select"){\r
1249                     if(tm){\r
1250                         if(tm[1] == "#"){\r
1251                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';\r
1252                         }else{\r
1253                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';\r
1254                         }\r
1255                         q = q.replace(tm[0], "");\r
1256                     }else if(q.substr(0, 1) != '@'){\r
1257                         fn[fn.length] = 'n = getNodes(n, mode, "*");';\r
1258                     }\r
1259                 }else{\r
1260                     if(tm){\r
1261                         if(tm[1] == "#"){\r
1262                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';\r
1263                         }else{\r
1264                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';\r
1265                         }\r
1266                         q = q.replace(tm[0], "");\r
1267                     }\r
1268                 }\r
1269                 while(!(mm = q.match(modeRe))){\r
1270                     var matched = false;\r
1271                     for(var j = 0; j < tklen; j++){\r
1272                         var t = tk[j];\r
1273                         var m = q.match(t.re);\r
1274                         if(m){\r
1275                             fn[fn.length] = t.select.replace(tplRe, function(x, i){\r
1276                                                     return m[i];\r
1277                                                 });\r
1278                             q = q.replace(m[0], "");\r
1279                             matched = true;\r
1280                             break;\r
1281                         }\r
1282                     }\r
1283                     // prevent infinite loop on bad selector\r
1284                     if(!matched){\r
1285                         throw 'Error parsing selector, parsing failed at "' + q + '"';\r
1286                     }\r
1287                 }\r
1288                 if(mm[1]){\r
1289                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';\r
1290                     q = q.replace(mm[1], "");\r
1291                 }\r
1292             }\r
1293             fn[fn.length] = "return nodup(n);\n}";\r
1294             eval(fn.join(""));\r
1295             return f;\r
1296         },\r
1297 \r
1298         /**\r
1299          * Selects a group of elements.\r
1300          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)\r
1301          * @param {Node} root (optional) The start of the query (defaults to document).\r
1302          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1303          * no matches, and empty Array is returned.\r
1304          */\r
1305         select : function(path, root, type){\r
1306             if(!root || root == document){\r
1307                 root = document;\r
1308             }\r
1309             if(typeof root == "string"){\r
1310                 root = document.getElementById(root);\r
1311             }\r
1312             var paths = path.split(","),\r
1313                 results = [];\r
1314             for(var i = 0, len = paths.length; i < len; i++){\r
1315                 var p = paths[i].replace(trimRe, "");\r
1316                 if(!cache[p]){\r
1317                     cache[p] = Ext.DomQuery.compile(p);\r
1318                     if(!cache[p]){\r
1319                         throw p + " is not a valid selector";\r
1320                     }\r
1321                 }\r
1322                 var result = cache[p](root);\r
1323                 if(result && result != document){\r
1324                     results = results.concat(result);\r
1325                 }\r
1326             }\r
1327             if(paths.length > 1){\r
1328                 return nodup(results);\r
1329             }\r
1330             return results;\r
1331         },\r
1332 \r
1333         /**\r
1334          * Selects a single element.\r
1335          * @param {String} selector The selector/xpath query\r
1336          * @param {Node} root (optional) The start of the query (defaults to document).\r
1337          * @return {Element} The DOM element which matched the selector.\r
1338          */\r
1339         selectNode : function(path, root){\r
1340             return Ext.DomQuery.select(path, root)[0];\r
1341         },\r
1342 \r
1343         /**\r
1344          * Selects the value of a node, optionally replacing null with the defaultValue.\r
1345          * @param {String} selector The selector/xpath query\r
1346          * @param {Node} root (optional) The start of the query (defaults to document).\r
1347          * @param {String} defaultValue\r
1348          * @return {String}\r
1349          */\r
1350         selectValue : function(path, root, defaultValue){\r
1351             path = path.replace(trimRe, "");\r
1352             if(!valueCache[path]){\r
1353                 valueCache[path] = Ext.DomQuery.compile(path, "select");\r
1354             }\r
1355             var n = valueCache[path](root),\r
1356                 v;\r
1357             n = n[0] ? n[0] : n;\r
1358             v = (n && n.firstChild ? n.firstChild.nodeValue : null);\r
1359             return ((v === null||v === undefined||v==='') ? defaultValue : v);\r
1360         },\r
1361 \r
1362         /**\r
1363          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.\r
1364          * @param {String} selector The selector/xpath query\r
1365          * @param {Node} root (optional) The start of the query (defaults to document).\r
1366          * @param {Number} defaultValue\r
1367          * @return {Number}\r
1368          */\r
1369         selectNumber : function(path, root, defaultValue){\r
1370             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);\r
1371             return parseFloat(v);\r
1372         },\r
1373 \r
1374         /**\r
1375          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)\r
1376          * @param {String/HTMLElement/Array} el An element id, element or array of elements\r
1377          * @param {String} selector The simple selector to test\r
1378          * @return {Boolean}\r
1379          */\r
1380         is : function(el, ss){\r
1381             if(typeof el == "string"){\r
1382                 el = document.getElementById(el);\r
1383             }\r
1384             var isArray = Ext.isArray(el),\r
1385                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);\r
1386             return isArray ? (result.length == el.length) : (result.length > 0);\r
1387         },\r
1388 \r
1389         /**\r
1390          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)\r
1391          * @param {Array} el An array of elements to filter\r
1392          * @param {String} selector The simple selector to test\r
1393          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match\r
1394          * the selector instead of the ones that match\r
1395          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1396          * no matches, and empty Array is returned.\r
1397          */\r
1398         filter : function(els, ss, nonMatches){\r
1399             ss = ss.replace(trimRe, "");\r
1400             if(!simpleCache[ss]){\r
1401                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");\r
1402             }\r
1403             var result = simpleCache[ss](els);\r
1404             return nonMatches ? quickDiff(result, els) : result;\r
1405         },\r
1406 \r
1407         /**\r
1408          * Collection of matching regular expressions and code snippets.\r
1409          */\r
1410         matchers : [{\r
1411                 re: /^\.([\w-]+)/,\r
1412                 select: 'n = byClassName(n, null, " {1} ");'\r
1413             }, {\r
1414                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,\r
1415                 select: 'n = byPseudo(n, "{1}", "{2}");'\r
1416             },{\r
1417                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,\r
1418                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'\r
1419             }, {\r
1420                 re: /^#([\w-]+)/,\r
1421                 select: 'n = byId(n, null, "{1}");'\r
1422             },{\r
1423                 re: /^@([\w-]+)/,\r
1424                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'\r
1425             }\r
1426         ],\r
1427 \r
1428         /**\r
1429          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.\r
1430          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.\r
1431          */\r
1432         operators : {\r
1433             "=" : function(a, v){\r
1434                 return a == v;\r
1435             },\r
1436             "!=" : function(a, v){\r
1437                 return a != v;\r
1438             },\r
1439             "^=" : function(a, v){\r
1440                 return a && a.substr(0, v.length) == v;\r
1441             },\r
1442             "$=" : function(a, v){\r
1443                 return a && a.substr(a.length-v.length) == v;\r
1444             },\r
1445             "*=" : function(a, v){\r
1446                 return a && a.indexOf(v) !== -1;\r
1447             },\r
1448             "%=" : function(a, v){\r
1449                 return (a % v) == 0;\r
1450             },\r
1451             "|=" : function(a, v){\r
1452                 return a && (a == v || a.substr(0, v.length+1) == v+'-');\r
1453             },\r
1454             "~=" : function(a, v){\r
1455                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;\r
1456             }\r
1457         },\r
1458 \r
1459         /**\r
1460          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)\r
1461          * and the argument (if any) supplied in the selector.\r
1462          */\r
1463         pseudos : {\r
1464             "first-child" : function(c){\r
1465                 var r = [], ri = -1, n;\r
1466                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1467                     while((n = n.previousSibling) && n.nodeType != 1);\r
1468                     if(!n){\r
1469                         r[++ri] = ci;\r
1470                     }\r
1471                 }\r
1472                 return r;\r
1473             },\r
1474 \r
1475             "last-child" : function(c){\r
1476                 var r = [], ri = -1, n;\r
1477                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1478                     while((n = n.nextSibling) && n.nodeType != 1);\r
1479                     if(!n){\r
1480                         r[++ri] = ci;\r
1481                     }\r
1482                 }\r
1483                 return r;\r
1484             },\r
1485 \r
1486             "nth-child" : function(c, a) {\r
1487                 var r = [], ri = -1,\r
1488                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),\r
1489                         f = (m[1] || 1) - 0, l = m[2] - 0;\r
1490                 for(var i = 0, n; n = c[i]; i++){\r
1491                     var pn = n.parentNode;\r
1492                     if (batch != pn._batch) {\r
1493                         var j = 0;\r
1494                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){\r
1495                             if(cn.nodeType == 1){\r
1496                                cn.nodeIndex = ++j;\r
1497                             }\r
1498                         }\r
1499                         pn._batch = batch;\r
1500                     }\r
1501                     if (f == 1) {\r
1502                         if (l == 0 || n.nodeIndex == l){\r
1503                             r[++ri] = n;\r
1504                         }\r
1505                     } else if ((n.nodeIndex + l) % f == 0){\r
1506                         r[++ri] = n;\r
1507                     }\r
1508                 }\r
1509 \r
1510                 return r;\r
1511             },\r
1512 \r
1513             "only-child" : function(c){\r
1514                 var r = [], ri = -1;;\r
1515                 for(var i = 0, ci; ci = c[i]; i++){\r
1516                     if(!prev(ci) && !next(ci)){\r
1517                         r[++ri] = ci;\r
1518                     }\r
1519                 }\r
1520                 return r;\r
1521             },\r
1522 \r
1523             "empty" : function(c){\r
1524                 var r = [], ri = -1;\r
1525                 for(var i = 0, ci; ci = c[i]; i++){\r
1526                     var cns = ci.childNodes, j = 0, cn, empty = true;\r
1527                     while(cn = cns[j]){\r
1528                         ++j;\r
1529                         if(cn.nodeType == 1 || cn.nodeType == 3){\r
1530                             empty = false;\r
1531                             break;\r
1532                         }\r
1533                     }\r
1534                     if(empty){\r
1535                         r[++ri] = ci;\r
1536                     }\r
1537                 }\r
1538                 return r;\r
1539             },\r
1540 \r
1541             "contains" : function(c, v){\r
1542                 var r = [], ri = -1;\r
1543                 for(var i = 0, ci; ci = c[i]; i++){\r
1544                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){\r
1545                         r[++ri] = ci;\r
1546                     }\r
1547                 }\r
1548                 return r;\r
1549             },\r
1550 \r
1551             "nodeValue" : function(c, v){\r
1552                 var r = [], ri = -1;\r
1553                 for(var i = 0, ci; ci = c[i]; i++){\r
1554                     if(ci.firstChild && ci.firstChild.nodeValue == v){\r
1555                         r[++ri] = ci;\r
1556                     }\r
1557                 }\r
1558                 return r;\r
1559             },\r
1560 \r
1561             "checked" : function(c){\r
1562                 var r = [], ri = -1;\r
1563                 for(var i = 0, ci; ci = c[i]; i++){\r
1564                     if(ci.checked == true){\r
1565                         r[++ri] = ci;\r
1566                     }\r
1567                 }\r
1568                 return r;\r
1569             },\r
1570 \r
1571             "not" : function(c, ss){\r
1572                 return Ext.DomQuery.filter(c, ss, true);\r
1573             },\r
1574 \r
1575             "any" : function(c, selectors){\r
1576                 var ss = selectors.split('|'),\r
1577                         r = [], ri = -1, s;\r
1578                 for(var i = 0, ci; ci = c[i]; i++){\r
1579                     for(var j = 0; s = ss[j]; j++){\r
1580                         if(Ext.DomQuery.is(ci, s)){\r
1581                             r[++ri] = ci;\r
1582                             break;\r
1583                         }\r
1584                     }\r
1585                 }\r
1586                 return r;\r
1587             },\r
1588 \r
1589             "odd" : function(c){\r
1590                 return this["nth-child"](c, "odd");\r
1591             },\r
1592 \r
1593             "even" : function(c){\r
1594                 return this["nth-child"](c, "even");\r
1595             },\r
1596 \r
1597             "nth" : function(c, a){\r
1598                 return c[a-1] || [];\r
1599             },\r
1600 \r
1601             "first" : function(c){\r
1602                 return c[0] || [];\r
1603             },\r
1604 \r
1605             "last" : function(c){\r
1606                 return c[c.length-1] || [];\r
1607             },\r
1608 \r
1609             "has" : function(c, ss){\r
1610                 var s = Ext.DomQuery.select,\r
1611                         r = [], ri = -1;\r
1612                 for(var i = 0, ci; ci = c[i]; i++){\r
1613                     if(s(ss, ci).length > 0){\r
1614                         r[++ri] = ci;\r
1615                     }\r
1616                 }\r
1617                 return r;\r
1618             },\r
1619 \r
1620             "next" : function(c, ss){\r
1621                 var is = Ext.DomQuery.is,\r
1622                         r = [], ri = -1;\r
1623                 for(var i = 0, ci; ci = c[i]; i++){\r
1624                     var n = next(ci);\r
1625                     if(n && is(n, ss)){\r
1626                         r[++ri] = ci;\r
1627                     }\r
1628                 }\r
1629                 return r;\r
1630             },\r
1631 \r
1632             "prev" : function(c, ss){\r
1633                 var is = Ext.DomQuery.is,\r
1634                         r = [], ri = -1;\r
1635                 for(var i = 0, ci; ci = c[i]; i++){\r
1636                     var n = prev(ci);\r
1637                     if(n && is(n, ss)){\r
1638                         r[++ri] = ci;\r
1639                     }\r
1640                 }\r
1641                 return r;\r
1642             }\r
1643         }\r
1644     };\r
1645 }();\r
1646 \r
1647 /**\r
1648  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}\r
1649  * @param {String} path The selector/xpath query\r
1650  * @param {Node} root (optional) The start of the query (defaults to document).\r
1651  * @return {Array}\r
1652  * @member Ext\r
1653  * @method query\r
1654  */\r
1655 Ext.query = Ext.DomQuery.select;\r
1656 (function(){
1657
1658 var EXTUTIL = Ext.util,
1659     TOARRAY = Ext.toArray,
1660     EACH = Ext.each,
1661     ISOBJECT = Ext.isObject,
1662     TRUE = true,
1663     FALSE = false;
1664 /**
1665  * @class Ext.util.Observable
1666  * Base class that provides a common interface for publishing events. Subclasses are expected to
1667  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1668  * with configured listeners defined.<br>
1669  * For example:
1670  * <pre><code>
1671 Employee = Ext.extend(Ext.util.Observable, {
1672     constructor: function(config){
1673         this.name = config.name;
1674         this.addEvents({
1675             "fired" : true,
1676             "quit" : true
1677         });
1678
1679         // Copy configured listeners into *this* object so that the base class&#39;s
1680         // constructor will add them.
1681         this.listeners = config.listeners;
1682
1683         // Call our superclass constructor to complete construction process.
1684         Employee.superclass.constructor.call(config)
1685     }
1686 });
1687 </code></pre>
1688  * This could then be used like this:<pre><code>
1689 var newEmployee = new Employee({
1690     name: employeeName,
1691     listeners: {
1692         quit: function() {
1693             // By default, "this" will be the object that fired the event.
1694             alert(this.name + " has quit!");
1695         }
1696     }
1697 });
1698 </code></pre>
1699  */
1700 EXTUTIL.Observable = function(){
1701     /**
1702      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1703      * object during initialization.  This should be a valid listeners config object as specified in the
1704      * {@link #addListener} example for attaching multiple handlers at once.</p>
1705      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1706      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1707      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1708      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1709      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1710      * has been rendered. A plugin can simplify this step:<pre><code>
1711 // Plugin is configured with a listeners config object.
1712 // The Component is appended to the argument list of all handler functions.
1713 Ext.DomObserver = Ext.extend(Object, {
1714     constructor: function(config) {
1715         this.listeners = config.listeners ? config.listeners : config;
1716     },
1717
1718     // Component passes itself into plugin&#39;s init method
1719     init: function(c) {
1720         var p, l = this.listeners;
1721         for (p in l) {
1722             if (Ext.isFunction(l[p])) {
1723                 l[p] = this.createHandler(l[p], c);
1724             } else {
1725                 l[p].fn = this.createHandler(l[p].fn, c);
1726             }
1727         }
1728
1729         // Add the listeners to the Element immediately following the render call
1730         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1731             var e = c.getEl();
1732             if (e) {
1733                 e.on(l);
1734             }
1735         });
1736     },
1737
1738     createHandler: function(fn, c) {
1739         return function(e) {
1740             fn.call(this, e, c);
1741         };
1742     }
1743 });
1744
1745 var combo = new Ext.form.ComboBox({
1746
1747     // Collapse combo when its element is clicked on
1748     plugins: [ new Ext.DomObserver({
1749         click: function(evt, comp) {
1750             comp.collapse();
1751         }
1752     })],
1753     store: myStore,
1754     typeAhead: true,
1755     mode: 'local',
1756     triggerAction: 'all'
1757 });
1758      * </code></pre></p>
1759      */
1760     var me = this, e = me.events;
1761     if(me.listeners){
1762         me.on(me.listeners);
1763         delete me.listeners;
1764     }
1765     me.events = e || {};
1766 };
1767
1768 EXTUTIL.Observable.prototype = function(){
1769     var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
1770         return s.toLowerCase();
1771     };
1772
1773     return {
1774         /**
1775          * <p>Fires the specified event with the passed parameters (minus the event name).</p>
1776          * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
1777          * by calling {@link #enableBubble}.</p>
1778          * @param {String} eventName The name of the event to fire.
1779          * @param {Object...} args Variable number of parameters are passed to handlers.
1780          * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
1781          */
1782
1783         fireEvent : function(){
1784             var a = TOARRAY(arguments),
1785                 ename = toLower(a[0]),
1786                 me = this,
1787                 ret = TRUE,
1788                 ce = me.events[ename],
1789                 q,
1790                 c;
1791             if (me.eventsSuspended === TRUE) {
1792                 if (q = me.suspendedEventsQueue) {
1793                     q.push(a);
1794                 }
1795             }
1796             else if(ISOBJECT(ce) && ce.bubble){
1797                 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
1798                     return FALSE;
1799                 }
1800                 c = me.getBubbleTarget && me.getBubbleTarget();
1801                 if(c && c.enableBubble) {
1802                     c.enableBubble(ename);
1803                     return c.fireEvent.apply(c, a);
1804                 }
1805             }
1806             else {
1807                 if (ISOBJECT(ce)) {
1808                     a.shift();
1809                     ret = ce.fire.apply(ce, a);
1810                 }
1811             }
1812             return ret;
1813         },
1814
1815         /**
1816          * Appends an event handler to this object.
1817          * @param {String}   eventName The name of the event to listen for.
1818          * @param {Function} handler The method the event invokes.
1819          * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1820          * <b>If omitted, defaults to the object which fired the event.</b>
1821          * @param {Object}   options (optional) An object containing handler configuration.
1822          * properties. This may contain any of the following properties:<ul>
1823          * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1824          * <b>If omitted, defaults to the object which fired the event.</b></div></li>
1825          * <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>
1826          * <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>
1827          * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1828          * by the specified number of milliseconds. If the event fires again within that time, the original
1829          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1830          * <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>
1831          * if the event was bubbled up from a child Observable.</div></li>
1832          * </ul><br>
1833          * <p>
1834          * <b>Combining Options</b><br>
1835          * Using the options argument, it is possible to combine different types of listeners:<br>
1836          * <br>
1837          * A delayed, one-time listener.
1838          * <pre><code>
1839 myDataView.on('click', this.onClick, this, {
1840     single: true,
1841     delay: 100
1842 });</code></pre>
1843          * <p>
1844          * <b>Attaching multiple handlers in 1 call</b><br>
1845          * The method also allows for a single argument to be passed which is a config object containing properties
1846          * which specify multiple handlers.
1847          * <p>
1848          * <pre><code>
1849 myGridPanel.on({
1850     'click' : {
1851         fn: this.onClick,
1852         scope: this,
1853         delay: 100
1854     },
1855     'mouseover' : {
1856         fn: this.onMouseOver,
1857         scope: this
1858     },
1859     'mouseout' : {
1860         fn: this.onMouseOut,
1861         scope: this
1862     }
1863 });</code></pre>
1864      * <p>
1865      * Or a shorthand syntax:<br>
1866      * <pre><code>
1867 myGridPanel.on({
1868     'click' : this.onClick,
1869     'mouseover' : this.onMouseOver,
1870     'mouseout' : this.onMouseOut,
1871      scope: this
1872 });</code></pre>
1873          */
1874         addListener : function(eventName, fn, scope, o){
1875             var me = this,
1876                 e,
1877                 oe,
1878                 isF,
1879             ce;
1880             if (ISOBJECT(eventName)) {
1881                 o = eventName;
1882                 for (e in o){
1883                     oe = o[e];
1884                     if (!filterOptRe.test(e)) {
1885                         me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
1886                     }
1887                 }
1888             } else {
1889                 eventName = toLower(eventName);
1890                 ce = me.events[eventName] || TRUE;
1891                 if (typeof ce == "boolean") {
1892                     me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
1893                 }
1894                 ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
1895             }
1896         },
1897
1898         /**
1899          * Removes an event handler.
1900          * @param {String}   eventName The type of event the handler was associated with.
1901          * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
1902          * @param {Object}   scope     (optional) The scope originally specified for the handler.
1903          */
1904         removeListener : function(eventName, fn, scope){
1905             var ce = this.events[toLower(eventName)];
1906             if (ISOBJECT(ce)) {
1907                 ce.removeListener(fn, scope);
1908             }
1909         },
1910
1911         /**
1912          * Removes all listeners for this object
1913          */
1914         purgeListeners : function(){
1915             var events = this.events,
1916                 evt,
1917                 key;
1918             for(key in events){
1919                 evt = events[key];
1920                 if(ISOBJECT(evt)){
1921                     evt.clearListeners();
1922                 }
1923             }
1924         },
1925
1926         /**
1927          * Used to define events on this Observable
1928          * @param {Object} object The object with the events defined
1929          */
1930         addEvents : function(o){
1931             var me = this;
1932             me.events = me.events || {};
1933             if (typeof o == 'string') {
1934                 EACH(arguments, function(a) {
1935                     me.events[a] = me.events[a] || TRUE;
1936                 });
1937             } else {
1938                 Ext.applyIf(me.events, o);
1939             }
1940         },
1941
1942         /**
1943          * Checks to see if this object has any listeners for a specified event
1944          * @param {String} eventName The name of the event to check for
1945          * @return {Boolean} True if the event is being listened for, else false
1946          */
1947         hasListener : function(eventName){
1948             var e = this.events[eventName];
1949             return ISOBJECT(e) && e.listeners.length > 0;
1950         },
1951
1952         /**
1953          * Suspend the firing of all events. (see {@link #resumeEvents})
1954          * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
1955          * after the {@link #resumeEvents} call instead of discarding all suspended events;
1956          */
1957         suspendEvents : function(queueSuspended){
1958             this.eventsSuspended = TRUE;
1959             if (queueSuspended){
1960                 this.suspendedEventsQueue = [];
1961             }
1962         },
1963
1964         /**
1965          * Resume firing events. (see {@link #suspendEvents})
1966          * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
1967          * events fired during event suspension will be sent to any listeners now.
1968          */
1969         resumeEvents : function(){
1970             var me = this;
1971             me.eventsSuspended = !delete me.suspendedEventQueue;
1972             EACH(me.suspendedEventsQueue, function(e) {
1973                 me.fireEvent.apply(me, e);
1974             });
1975         }
1976     }
1977 }();
1978
1979 var OBSERVABLE = EXTUTIL.Observable.prototype;
1980 /**
1981  * Appends an event handler to this object (shorthand for {@link #addListener}.)
1982  * @param {String}   eventName     The type of event to listen for
1983  * @param {Function} handler       The method the event invokes
1984  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1985  * <b>If omitted, defaults to the object which fired the event.</b>
1986  * @param {Object}   options       (optional) An object containing handler configuration.
1987  * @method
1988  */
1989 OBSERVABLE.on = OBSERVABLE.addListener;
1990 /**
1991  * Removes an event handler (shorthand for {@link #removeListener}.)
1992  * @param {String}   eventName     The type of event the handler was associated with.
1993  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
1994  * @param {Object}   scope         (optional) The scope originally specified for the handler.
1995  * @method
1996  */
1997 OBSERVABLE.un = OBSERVABLE.removeListener;
1998
1999 /**
2000  * Removes <b>all</b> added captures from the Observable.
2001  * @param {Observable} o The Observable to release
2002  * @static
2003  */
2004 EXTUTIL.Observable.releaseCapture = function(o){
2005     o.fireEvent = OBSERVABLE.fireEvent;
2006 };
2007
2008 function createTargeted(h, o, scope){
2009     return function(){
2010         if(o.target == arguments[0]){
2011             h.apply(scope, TOARRAY(arguments));
2012         }
2013     };
2014 };
2015
2016 function createBuffered(h, o, scope){
2017     var task = new EXTUTIL.DelayedTask();
2018     return function(){
2019         task.delay(o.buffer, h, scope, TOARRAY(arguments));
2020     };
2021 }
2022
2023 function createSingle(h, e, fn, scope){
2024     return function(){
2025         e.removeListener(fn, scope);
2026         return h.apply(scope, arguments);
2027     };
2028 }
2029
2030 function createDelayed(h, o, scope){
2031     return function(){
2032         var args = TOARRAY(arguments);
2033         (function(){
2034             h.apply(scope, args);
2035         }).defer(o.delay || 10);
2036     };
2037 };
2038
2039 EXTUTIL.Event = function(obj, name){
2040     this.name = name;
2041     this.obj = obj;
2042     this.listeners = [];
2043 };
2044
2045 EXTUTIL.Event.prototype = {
2046     addListener : function(fn, scope, options){
2047         var me = this,
2048             l;
2049         scope = scope || me.obj;
2050         if(!me.isListening(fn, scope)){
2051             l = me.createListener(fn, scope, options);
2052             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2053                 me.listeners = me.listeners.slice(0);
2054             }
2055             me.listeners.push(l);
2056         }
2057     },
2058
2059     createListener: function(fn, scope, o){
2060         o = o || {}, scope = scope || this.obj;
2061         var l = {
2062             fn: fn,
2063             scope: scope,
2064             options: o
2065         }, h = fn;
2066         if(o.target){
2067             h = createTargeted(h, o, scope);
2068         }
2069         if(o.delay){
2070             h = createDelayed(h, o, scope);
2071         }
2072         if(o.single){
2073             h = createSingle(h, this, fn, scope);
2074         }
2075         if(o.buffer){
2076             h = createBuffered(h, o, scope);
2077         }
2078         l.fireFn = h;
2079         return l;
2080     },
2081
2082     findListener : function(fn, scope){
2083         var s, ret = -1;
2084         EACH(this.listeners, function(l, i) {
2085             s = l.scope;
2086             if(l.fn == fn && (s == scope || s == this.obj)){
2087                 ret = i;
2088                 return FALSE;
2089             }
2090         },
2091         this);
2092         return ret;
2093     },
2094
2095     isListening : function(fn, scope){
2096         return this.findListener(fn, scope) != -1;
2097     },
2098
2099     removeListener : function(fn, scope){
2100         var index,
2101             me = this,
2102             ret = FALSE;
2103         if((index = me.findListener(fn, scope)) != -1){
2104             if (me.firing) {
2105                 me.listeners = me.listeners.slice(0);
2106             }
2107             me.listeners.splice(index, 1);
2108             ret = TRUE;
2109         }
2110         return ret;
2111     },
2112
2113     clearListeners : function(){
2114         this.listeners = [];
2115     },
2116
2117     fire : function(){
2118         var me = this,
2119             args = TOARRAY(arguments),
2120             ret = TRUE;
2121
2122         EACH(me.listeners, function(l) {
2123             me.firing = TRUE;
2124             if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2125                 return ret = me.firing = FALSE;
2126             }
2127         });
2128         me.firing = FALSE;
2129         return ret;
2130     }
2131 };
2132 })();/**\r
2133  * @class Ext.util.Observable\r
2134  */\r
2135 Ext.apply(Ext.util.Observable.prototype, function(){    \r
2136     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)\r
2137     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2138     // private\r
2139     function getMethodEvent(method){\r
2140         var e = (this.methodEvents = this.methodEvents ||\r
2141         {})[method], returnValue, v, cancel, obj = this;\r
2142         \r
2143         if (!e) {\r
2144             this.methodEvents[method] = e = {};\r
2145             e.originalFn = this[method];\r
2146             e.methodName = method;\r
2147             e.before = [];\r
2148             e.after = [];\r
2149             \r
2150             var makeCall = function(fn, scope, args){\r
2151                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {\r
2152                     if (Ext.isObject(v)) {\r
2153                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;\r
2154                         cancel = !!v.cancel;\r
2155                     }\r
2156                     else \r
2157                         if (v === false) {\r
2158                             cancel = true;\r
2159                         }\r
2160                         else {\r
2161                             returnValue = v;\r
2162                         }\r
2163                 }\r
2164             };\r
2165             \r
2166             this[method] = function(){\r
2167                 var args = Ext.toArray(arguments);\r
2168                 returnValue = v = undefined;\r
2169                 cancel = false;\r
2170                 \r
2171                 Ext.each(e.before, function(b){\r
2172                     makeCall(b.fn, b.scope, args);\r
2173                     if (cancel) {\r
2174                         return returnValue;\r
2175                     }\r
2176                 });\r
2177                 \r
2178                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {\r
2179                     returnValue = v;\r
2180                 }\r
2181                 Ext.each(e.after, function(a){\r
2182                     makeCall(a.fn, a.scope, args);\r
2183                     if (cancel) {\r
2184                         return returnValue;\r
2185                     }\r
2186                 });\r
2187                 return returnValue;\r
2188             };\r
2189         }\r
2190         return e;\r
2191     }\r
2192     \r
2193     return {\r
2194         // these are considered experimental\r
2195         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2196         // adds an "interceptor" called before the original method\r
2197         beforeMethod: function(method, fn, scope){\r
2198             getMethodEvent.call(this, method).before.push({\r
2199                 fn: fn,\r
2200                 scope: scope\r
2201             });\r
2202         },\r
2203         \r
2204         // adds a "sequence" called after the original method\r
2205         afterMethod: function(method, fn, scope){\r
2206             getMethodEvent.call(this, method).after.push({\r
2207                 fn: fn,\r
2208                 scope: scope\r
2209             });\r
2210         },\r
2211         \r
2212         removeMethodListener: function(method, fn, scope){\r
2213             var e = getMethodEvent.call(this, method), found = false;\r
2214             Ext.each(e.before, function(b, i, arr){\r
2215                 if (b.fn == fn && b.scope == scope) {\r
2216                     arr.splice(i, 1);\r
2217                     found = true;\r
2218                     return false;\r
2219                 }\r
2220             });\r
2221             if (!found) {\r
2222                 Ext.each(e.after, function(a, i, arr){\r
2223                     if (a.fn == fn && a.scope == scope) {\r
2224                         arr.splice(i, 1);\r
2225                         return false;\r
2226                     }\r
2227                 });\r
2228             }\r
2229         },\r
2230         \r
2231         /**\r
2232          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.\r
2233          * @param {Object} o The Observable whose events this object is to relay.\r
2234          * @param {Array} events Array of event names to relay.\r
2235          */\r
2236         relayEvents: function(o, events){\r
2237             var me = this;\r
2238             function createHandler(ename){\r
2239                 return function(){\r
2240                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));\r
2241                 };\r
2242             }\r
2243             Ext.each(events, function(ename){\r
2244                 me.events[ename] = me.events[ename] || true;\r
2245                 o.on(ename, createHandler(ename), me);\r
2246             });\r
2247         },\r
2248         \r
2249         /**\r
2250          * Used to enable bubbling of events\r
2251          * @param {Object} events\r
2252          */\r
2253         enableBubble: function(events){\r
2254             var me = this;\r
2255             events = Ext.isArray(events) ? events : Ext.toArray(arguments);\r
2256             Ext.each(events, function(ename){\r
2257                 ename = ename.toLowerCase();\r
2258                 var ce = me.events[ename] || true;\r
2259                 if (typeof ce == "boolean") {\r
2260                     ce = new Ext.util.Event(me, ename);\r
2261                     me.events[ename] = ce;\r
2262                 }\r
2263                 ce.bubble = true;\r
2264             });\r
2265         }\r
2266     };\r
2267 }());\r
2268 \r
2269 \r
2270 /**\r
2271  * Starts capture on the specified Observable. All events will be passed\r
2272  * to the supplied function with the event name + standard signature of the event\r
2273  * <b>before</b> the event is fired. If the supplied function returns false,\r
2274  * the event will not fire.\r
2275  * @param {Observable} o The Observable to capture\r
2276  * @param {Function} fn The function to call\r
2277  * @param {Object} scope (optional) The scope (this object) for the fn\r
2278  * @static\r
2279  */\r
2280 Ext.util.Observable.capture = function(o, fn, scope){\r
2281     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);\r
2282 };\r
2283 \r
2284 \r
2285 /**\r
2286  * Sets observability on the passed class constructor.<p>\r
2287  * <p>This makes any event fired on any instance of the passed class also fire a single event through\r
2288  * the <i>class</i> allowing for central handling of events on many instances at once.</p>\r
2289  * <p>Usage:</p><pre><code>\r
2290 Ext.util.Observable.observeClass(Ext.data.Connection);\r
2291 Ext.data.Connection.on('beforerequest', function(con, options) {\r
2292     console.log("Ajax request made to " + options.url);\r
2293 });</code></pre>\r
2294  * @param {Function} c The class constructor to make observable.\r
2295  * @static\r
2296  */\r
2297 Ext.util.Observable.observeClass = function(c){\r
2298     Ext.apply(c, new Ext.util.Observable());\r
2299     c.prototype.fireEvent = function(){\r
2300         return (c.fireEvent.apply(c, arguments) !== false) &&\r
2301         (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false);\r
2302     };\r
2303 };/**
2304  * @class Ext.EventManager
2305  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2306  * several useful events directly.
2307  * See {@link Ext.EventObject} for more details on normalized event objects.
2308  * @singleton
2309  */
2310 Ext.EventManager = function(){
2311     var docReadyEvent, 
2312         docReadyProcId, 
2313         docReadyState = false,          
2314         E = Ext.lib.Event,
2315         D = Ext.lib.Dom,
2316         DOC = document,
2317         WINDOW = window,
2318         IEDEFERED = "ie-deferred-loader",
2319         DOMCONTENTLOADED = "DOMContentLoaded",
2320         elHash = {},
2321         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
2322
2323     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2324     function addListener(el, ename, fn, wrap, scope){       
2325         var id = Ext.id(el),
2326                 es = elHash[id] = elHash[id] || {};             
2327        
2328         (es[ename] = es[ename] || []).push([fn, wrap, scope]);
2329         E.on(el, ename, wrap);
2330
2331         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2332         // without breaking ExtJS.
2333         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
2334                 var args = ["DOMMouseScroll", wrap, false];
2335                 el.addEventListener.apply(el, args);
2336             E.on(window, 'unload', function(){
2337                     el.removeEventListener.apply(el, args);                
2338             });
2339         }
2340         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
2341             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2342         }
2343     };
2344     
2345     function fireDocReady(){
2346         if(!docReadyState){            
2347             Ext.isReady = docReadyState = true;
2348             if(docReadyProcId){
2349                 clearInterval(docReadyProcId);
2350             }
2351             if(Ext.isGecko || Ext.isOpera) {
2352                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2353             }
2354             if(Ext.isIE){
2355                 var defer = DOC.getElementById(IEDEFERED);
2356                 if(defer){
2357                     defer.onreadystatechange = null;
2358                     defer.parentNode.removeChild(defer);
2359                 }
2360             }
2361             if(docReadyEvent){
2362                 docReadyEvent.fire();
2363                 docReadyEvent.clearListeners();
2364             }
2365         }
2366     };
2367
2368     function initDocReady(){
2369             var COMPLETE = "complete";
2370                 
2371         docReadyEvent = new Ext.util.Event();
2372         if (Ext.isGecko || Ext.isOpera) {
2373             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2374         } else if (Ext.isIE){
2375             DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");            
2376             DOC.getElementById(IEDEFERED).onreadystatechange = function(){
2377                 if(this.readyState == COMPLETE){
2378                     fireDocReady();
2379                 }
2380             };
2381         } else if (Ext.isWebKit){
2382             docReadyProcId = setInterval(function(){                
2383                 if(DOC.readyState == COMPLETE) {
2384                     fireDocReady();
2385                  }
2386             }, 10);
2387         }
2388         // no matter what, make sure it fires on load
2389         E.on(WINDOW, "load", fireDocReady);
2390     };
2391
2392     function createTargeted(h, o){
2393         return function(){
2394                 var args = Ext.toArray(arguments);
2395             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2396                 h.apply(this, args);
2397             }
2398         };
2399     };    
2400     
2401     function createBuffered(h, o){
2402         var task = new Ext.util.DelayedTask(h);
2403         return function(e){
2404             // create new event object impl so new events don't wipe out properties            
2405             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2406         };
2407     };
2408
2409     function createSingle(h, el, ename, fn, scope){
2410         return function(e){
2411             Ext.EventManager.removeListener(el, ename, fn, scope);
2412             h(e);
2413         };
2414     };
2415
2416     function createDelayed(h, o){
2417         return function(e){
2418             // create new event object impl so new events don't wipe out properties   
2419             e = new Ext.EventObjectImpl(e);
2420             setTimeout(function(){
2421                 h(e);
2422             }, o.delay || 10);
2423         };
2424     };
2425
2426     function listen(element, ename, opt, fn, scope){
2427         var o = !Ext.isObject(opt) ? {} : opt,
2428                 el = Ext.getDom(element);
2429                 
2430         fn = fn || o.fn; 
2431         scope = scope || o.scope;
2432         
2433         if(!el){
2434             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2435         }
2436         function h(e){
2437             // prevent errors while unload occurring
2438             if(!Ext){// !window[xname]){  ==> can't we do this? 
2439                 return;
2440             }
2441             e = Ext.EventObject.setEvent(e);
2442             var t;
2443             if (o.delegate) {
2444                 if(!(t = e.getTarget(o.delegate, el))){
2445                     return;
2446                 }
2447             } else {
2448                 t = e.target;
2449             }            
2450             if (o.stopEvent) {
2451                 e.stopEvent();
2452             }
2453             if (o.preventDefault) {
2454                e.preventDefault();
2455             }
2456             if (o.stopPropagation) {
2457                 e.stopPropagation();
2458             }
2459             if (o.normalized) {
2460                 e = e.browserEvent;
2461             }
2462             
2463             fn.call(scope || el, e, t, o);
2464         };
2465         if(o.target){
2466             h = createTargeted(h, o);
2467         }
2468         if(o.delay){
2469             h = createDelayed(h, o);
2470         }
2471         if(o.single){
2472             h = createSingle(h, el, ename, fn, scope);
2473         }
2474         if(o.buffer){
2475             h = createBuffered(h, o);
2476         }
2477
2478         addListener(el, ename, fn, h, scope);
2479         return h;
2480     };
2481
2482     var pub = {
2483             /**
2484              * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2485              * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2486              * @param {String/HTMLElement} el The html element or id to assign the event handler to
2487              * @param {String} eventName The type of event to listen for
2488              * @param {Function} handler The handler function the event invokes This function is passed
2489              * the following parameters:<ul>
2490              * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2491              * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2492              * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2493              * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2494              * </ul>
2495              * @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>.
2496              * @param {Object} options (optional) An object containing handler configuration properties.
2497              * This may contain any of the following properties:<ul>
2498              * <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>
2499              * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2500              * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2501              * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2502              * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2503              * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2504              * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2505              * <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>
2506              * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2507              * by the specified number of milliseconds. If the event fires again within that time, the original
2508              * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2509              * <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>
2510              * </ul><br>
2511              * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2512              */
2513                 addListener : function(element, eventName, fn, scope, options){                                              
2514             if(Ext.isObject(eventName)){                
2515                     var o = eventName, e, val;
2516                 for(e in o){
2517                         val = o[e];
2518                     if(!propRe.test(e)){                                                 
2519                             if(Ext.isFunction(val)){
2520                                 // shared options
2521                                 listen(element, e, o, val, o.scope);
2522                             }else{
2523                                 // individual options
2524                                 listen(element, e, val);
2525                             }
2526                     }
2527                 }
2528             } else {
2529                 listen(element, eventName, options, fn, scope);
2530                 }
2531         },
2532         
2533         /**
2534          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
2535          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2536          * @param {String/HTMLElement} el The id or html element from which to remove the event
2537          * @param {String} eventName The type of event
2538          * @param {Function} fn The handler function to remove
2539          */
2540         removeListener : function(element, eventName, fn, scope){            
2541             var el = Ext.getDom(element),
2542                 id = Ext.id(el),
2543                     wrap;      
2544                 
2545                 Ext.each((elHash[id] || {})[eventName], function (v,i,a) {
2546                             if (Ext.isArray(v) && v[0] == fn && (!scope || v[2] == scope)) {                                                    
2547                                 E.un(el, eventName, wrap = v[1]);
2548                                 a.splice(i,1);
2549                                 return false;                           
2550                         }
2551                 });     
2552
2553             // jQuery workaround that should be removed from Ext Core
2554                 if(eventName == "mousewheel" && el.addEventListener && wrap){
2555                     el.removeEventListener("DOMMouseScroll", wrap, false);
2556                 }
2557                         
2558                 if(eventName == "mousedown" && el == DOC && wrap){ // fix stopped mousedowns on the document
2559                     Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
2560                 }
2561         },
2562         
2563         /**
2564          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
2565          * directly on an Element in favor of calling this version.
2566          * @param {String/HTMLElement} el The id or html element from which to remove the event
2567          */
2568         removeAll : function(el){
2569                 var id = Ext.id(el = Ext.getDom(el)), 
2570                                 es = elHash[id],                                
2571                                 ename;
2572                
2573                 for(ename in es){
2574                     if(es.hasOwnProperty(ename)){                           
2575                         Ext.each(es[ename], function(v) {
2576                             E.un(el, ename, v.wrap);                    
2577                         });
2578                     }            
2579                 }
2580                 elHash[id] = null;       
2581         },
2582
2583         /**
2584          * Fires when the document is ready (before onload and before images are loaded). Can be
2585          * accessed shorthanded as Ext.onReady().
2586          * @param {Function} fn The method the event invokes
2587          * @param {Object} scope (optional) An object that becomes the scope of the handler
2588          * @param {boolean} options (optional) An object containing standard {@link #addListener} options
2589          */
2590         onDocumentReady : function(fn, scope, options){
2591             if(docReadyState){ // if it already fired
2592                 docReadyEvent.addListener(fn, scope, options);
2593                 docReadyEvent.fire();
2594                 docReadyEvent.clearListeners();               
2595             } else {
2596                 if(!docReadyEvent) initDocReady();
2597                 options = options || {};
2598                     options.delay = options.delay || 1;             
2599                     docReadyEvent.addListener(fn, scope, options);
2600             }
2601         },
2602         
2603         elHash : elHash   
2604     };
2605      /**
2606      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
2607      * @param {String/HTMLElement} el The html element or id to assign the event handler to
2608      * @param {String} eventName The type of event to listen for
2609      * @param {Function} handler The handler function the event invokes
2610      * @param {Object} scope (optional) The scope in which to execute the handler
2611      * function (the handler function's "this" context)
2612      * @param {Object} options (optional) An object containing standard {@link #addListener} options
2613      * @member Ext.EventManager
2614      * @method on
2615      */
2616     pub.on = pub.addListener;
2617     /**
2618      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
2619      * @param {String/HTMLElement} el The id or html element from which to remove the event
2620      * @param {String} eventName The type of event
2621      * @param {Function} fn The handler function to remove
2622      * @return {Boolean} True if a listener was actually removed, else false
2623      * @member Ext.EventManager
2624      * @method un
2625      */
2626     pub.un = pub.removeListener;
2627
2628     pub.stoppedMouseDownEvent = new Ext.util.Event();
2629     return pub;
2630 }();
2631 /**
2632   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Ext.EventManager#onDocumentReady}.
2633   * @param {Function} fn The method the event invokes
2634   * @param {Object} scope An object that becomes the scope of the handler
2635   * @param {boolean} options (optional) An object containing standard {@link #addListener} options
2636   * @member Ext
2637   * @method onReady
2638  */
2639 Ext.onReady = Ext.EventManager.onDocumentReady;
2640
2641
2642 //Initialize doc classes
2643 (function(){
2644     
2645     var initExtCss = function(){
2646         // find the body element
2647         var bd = document.body || document.getElementsByTagName('body')[0];
2648         if(!bd){ return false; }
2649         var cls = [' ',
2650                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
2651                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
2652                 : Ext.isOpera ? "ext-opera"
2653                 : Ext.isWebKit ? "ext-webkit" : ""];
2654
2655         if(Ext.isSafari){
2656             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
2657         }else if(Ext.isChrome){
2658             cls.push("ext-chrome");
2659         }
2660
2661         if(Ext.isMac){
2662             cls.push("ext-mac");
2663         }
2664         if(Ext.isLinux){
2665             cls.push("ext-linux");
2666         }
2667
2668         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
2669             var p = bd.parentNode;
2670             if(p){
2671                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
2672             }
2673         }
2674         bd.className += cls.join(' ');
2675         return true;
2676     }
2677
2678     if(!initExtCss()){
2679         Ext.onReady(initExtCss);
2680     }
2681 })();
2682
2683
2684 /**
2685  * @class Ext.EventObject
2686  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject 
2687  * wraps the browser's native event-object normalizing cross-browser differences,
2688  * such as which mouse button is clicked, keys pressed, mechanisms to stop
2689  * event-propagation along with a method to prevent default actions from taking place.
2690  * <p>For example:</p>
2691  * <pre><code>
2692 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
2693     e.preventDefault();
2694     var target = e.getTarget(); // same as t (the target HTMLElement)
2695     ...
2696 }
2697 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
2698 myDiv.on(         // 'on' is shorthand for addListener
2699     "click",      // perform an action on click of myDiv
2700     handleClick   // reference to the action handler
2701 );  
2702 // other methods to do the same:
2703 Ext.EventManager.on("myDiv", 'click', handleClick);
2704 Ext.EventManager.addListener("myDiv", 'click', handleClick);
2705  </code></pre>
2706  * @singleton
2707  */
2708 Ext.EventObject = function(){
2709     var E = Ext.lib.Event,
2710         // safari keypress events for special keys return bad keycodes
2711         safariKeys = {
2712                 3 : 13, // enter
2713                 63234 : 37, // left
2714                 63235 : 39, // right
2715                 63232 : 38, // up
2716                 63233 : 40, // down
2717                 63276 : 33, // page up
2718                 63277 : 34, // page down
2719                 63272 : 46, // delete
2720                 63273 : 36, // home
2721                 63275 : 35  // end
2722         },
2723         // normalize button clicks
2724         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
2725                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
2726
2727     Ext.EventObjectImpl = function(e){
2728         if(e){
2729             this.setEvent(e.browserEvent || e);
2730         }
2731     };
2732
2733     Ext.EventObjectImpl.prototype = {
2734            /** @private */
2735         setEvent : function(e){
2736                 var me = this;
2737             if(e == me || (e && e.browserEvent)){ // already wrapped
2738                 return e;
2739             }
2740             me.browserEvent = e;
2741             if(e){
2742                 // normalize buttons
2743                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
2744                 if(e.type == 'click' && me.button == -1){
2745                     me.button = 0;
2746                 }
2747                 me.type = e.type;
2748                 me.shiftKey = e.shiftKey;
2749                 // mac metaKey behaves like ctrlKey
2750                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
2751                 me.altKey = e.altKey;
2752                 // in getKey these will be normalized for the mac
2753                 me.keyCode = e.keyCode;
2754                 me.charCode = e.charCode;
2755                 // cache the target for the delayed and or buffered events
2756                 me.target = E.getTarget(e);
2757                 // same for XY
2758                 me.xy = E.getXY(e);
2759             }else{
2760                 me.button = -1;
2761                 me.shiftKey = false;
2762                 me.ctrlKey = false;
2763                 me.altKey = false;
2764                 me.keyCode = 0;
2765                 me.charCode = 0;
2766                 me.target = null;
2767                 me.xy = [0, 0];
2768             }
2769             return me;
2770         },
2771
2772         /**
2773          * Stop the event (preventDefault and stopPropagation)
2774          */
2775         stopEvent : function(){
2776                 var me = this;
2777             if(me.browserEvent){
2778                 if(me.browserEvent.type == 'mousedown'){
2779                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
2780                 }
2781                 E.stopEvent(me.browserEvent);
2782             }
2783         },
2784
2785         /**
2786          * Prevents the browsers default handling of the event.
2787          */
2788         preventDefault : function(){
2789             if(this.browserEvent){
2790                 E.preventDefault(this.browserEvent);
2791             }
2792         },        
2793
2794         /**
2795          * Cancels bubbling of the event.
2796          */
2797         stopPropagation : function(){
2798                 var me = this;
2799             if(me.browserEvent){
2800                 if(me.browserEvent.type == 'mousedown'){
2801                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
2802                 }
2803                 E.stopPropagation(me.browserEvent);
2804             }
2805         },
2806
2807         /**
2808          * Gets the character code for the event.
2809          * @return {Number}
2810          */
2811         getCharCode : function(){
2812             return this.charCode || this.keyCode;
2813         },
2814
2815         /**
2816          * Returns a normalized keyCode for the event.
2817          * @return {Number} The key code
2818          */
2819         getKey : function(){
2820             return this.normalizeKey(this.keyCode || this.charCode)
2821         },
2822                 
2823                 // private
2824                 normalizeKey: function(k){
2825                         return Ext.isSafari ? (safariKeys[k] || k) : k; 
2826                 },
2827
2828         /**
2829          * Gets the x coordinate of the event.
2830          * @return {Number}
2831          */
2832         getPageX : function(){
2833             return this.xy[0];
2834         },
2835
2836         /**
2837          * Gets the y coordinate of the event.
2838          * @return {Number}
2839          */
2840         getPageY : function(){
2841             return this.xy[1];
2842         },
2843
2844         /**
2845          * Gets the page coordinates of the event.
2846          * @return {Array} The xy values like [x, y]
2847          */
2848         getXY : function(){
2849             return this.xy;
2850         },
2851
2852         /**
2853          * Gets the target for the event.
2854          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
2855          * @param {Number/Mixed} maxDepth (optional) The max depth to
2856                 search as a number or element (defaults to 10 || document.body)
2857          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
2858          * @return {HTMLelement}
2859          */
2860         getTarget : function(selector, maxDepth, returnEl){
2861             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
2862         },
2863
2864         /**
2865          * Gets the related target.
2866          * @return {HTMLElement}
2867          */
2868         getRelatedTarget : function(){
2869             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
2870         },
2871
2872         /**
2873          * Normalizes mouse wheel delta across browsers
2874          * @return {Number} The delta
2875          */
2876         getWheelDelta : function(){
2877             var e = this.browserEvent;
2878             var delta = 0;
2879             if(e.wheelDelta){ /* IE/Opera. */
2880                 delta = e.wheelDelta/120;
2881             }else if(e.detail){ /* Mozilla case. */
2882                 delta = -e.detail/3;
2883             }
2884             return delta;
2885         },
2886                 
2887                 /**
2888                 * 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.
2889                 * Example usage:<pre><code>
2890                 // Handle click on any child of an element
2891                 Ext.getBody().on('click', function(e){
2892                         if(e.within('some-el')){
2893                                 alert('Clicked on a child of some-el!');
2894                         }
2895                 });
2896                 
2897                 // Handle click directly on an element, ignoring clicks on child nodes
2898                 Ext.getBody().on('click', function(e,t){
2899                         if((t.id == 'some-el') && !e.within(t, true)){
2900                                 alert('Clicked directly on some-el!');
2901                         }
2902                 });
2903                 </code></pre>
2904                  * @param {Mixed} el The id, DOM element or Ext.Element to check
2905                  * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
2906                  * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
2907                  * @return {Boolean}
2908                  */
2909                 within : function(el, related, allowEl){
2910             if(el){
2911                             var t = this[related ? "getRelatedTarget" : "getTarget"]();
2912                             return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
2913             }
2914             return false;
2915                 }
2916          };
2917
2918     return new Ext.EventObjectImpl();
2919 }();/**\r
2920  * @class Ext.EventManager\r
2921  */\r
2922 Ext.apply(Ext.EventManager, function(){\r
2923         var resizeEvent, \r
2924         resizeTask, \r
2925         textEvent, \r
2926         textSize,\r
2927         D = Ext.lib.Dom,\r
2928         E = Ext.lib.Event,\r
2929         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,\r
2930         curWidth = 0,\r
2931         curHeight = 0,\r
2932         // note 1: IE fires ONLY the keydown event on specialkey autorepeat\r
2933         // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat\r
2934         // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)\r
2935         useKeydown = Ext.isSafari ? \r
2936                     Ext.num(navigator.userAgent.toLowerCase().match(/version\/(\d+\.\d)/)[1] || 2) >= 3.1 :\r
2937                     !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);\r
2938         \r
2939         return { \r
2940                 // private\r
2941             doResizeEvent: function(){\r
2942             var h = D.getViewHeight(),\r
2943                 w = D.getViewWidth();\r
2944             \r
2945             //whacky problem in IE where the resize event will fire even though the w/h are the same.\r
2946             if(curHeight != h || curWidth != w){\r
2947                 resizeEvent.fire(curWidth = w, curHeight = h);\r
2948             }\r
2949             },\r
2950             \r
2951             /**\r
2952              * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.\r
2953              * @param {Function} fn        The method the event invokes\r
2954              * @param {Object}   scope    An object that becomes the scope of the handler\r
2955              * @param {boolean}  options\r
2956              */\r
2957             onWindowResize : function(fn, scope, options){\r
2958                 if(!resizeEvent){\r
2959                     resizeEvent = new Ext.util.Event();\r
2960                     resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);\r
2961                     E.on(window, "resize", this.fireWindowResize, this);\r
2962                 }\r
2963                 resizeEvent.addListener(fn, scope, options);\r
2964             },\r
2965         \r
2966             // exposed only to allow manual firing\r
2967             fireWindowResize : function(){\r
2968                 if(resizeEvent){\r
2969                     if((Ext.isIE||Ext.isAir) && resizeTask){\r
2970                         resizeTask.delay(50);\r
2971                     }else{\r
2972                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
2973                     }\r
2974                 }\r
2975             },\r
2976         \r
2977             /**\r
2978              * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.\r
2979              * @param {Function} fn        The method the event invokes\r
2980              * @param {Object}   scope    An object that becomes the scope of the handler\r
2981              * @param {boolean}  options\r
2982              */\r
2983             onTextResize : function(fn, scope, options){\r
2984                 if(!textEvent){\r
2985                     textEvent = new Ext.util.Event();\r
2986                     var textEl = new Ext.Element(document.createElement('div'));\r
2987                     textEl.dom.className = 'x-text-resize';\r
2988                     textEl.dom.innerHTML = 'X';\r
2989                     textEl.appendTo(document.body);\r
2990                     textSize = textEl.dom.offsetHeight;\r
2991                     setInterval(function(){\r
2992                         if(textEl.dom.offsetHeight != textSize){\r
2993                             textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);\r
2994                         }\r
2995                     }, this.textResizeInterval);\r
2996                 }\r
2997                 textEvent.addListener(fn, scope, options);\r
2998             },\r
2999         \r
3000             /**\r
3001              * Removes the passed window resize listener.\r
3002              * @param {Function} fn        The method the event invokes\r
3003              * @param {Object}   scope    The scope of handler\r
3004              */\r
3005             removeResizeListener : function(fn, scope){\r
3006                 if(resizeEvent){\r
3007                     resizeEvent.removeListener(fn, scope);\r
3008                 }\r
3009             },\r
3010         \r
3011             // private\r
3012             fireResize : function(){\r
3013                 if(resizeEvent){\r
3014                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
3015                 }\r
3016             },\r
3017             \r
3018              /**\r
3019              * The frequency, in milliseconds, to check for text resize events (defaults to 50)\r
3020              */\r
3021             textResizeInterval : 50,\r
3022             \r
3023             /**\r
3024          * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)\r
3025          */\r
3026         ieDeferSrc : false,\r
3027         \r
3028         // protected for use inside the framework\r
3029         // detects whether we should use keydown or keypress based on the browser.\r
3030         useKeydown: useKeydown\r
3031     };\r
3032 }());\r
3033 \r
3034 Ext.EventManager.on = Ext.EventManager.addListener;\r
3035 \r
3036 \r
3037 Ext.apply(Ext.EventObjectImpl.prototype, {\r
3038     /** Key constant @type Number */\r
3039     BACKSPACE: 8,\r
3040     /** Key constant @type Number */\r
3041     TAB: 9,\r
3042     /** Key constant @type Number */\r
3043     NUM_CENTER: 12,\r
3044     /** Key constant @type Number */\r
3045     ENTER: 13,\r
3046     /** Key constant @type Number */\r
3047     RETURN: 13,\r
3048     /** Key constant @type Number */\r
3049     SHIFT: 16,\r
3050     /** Key constant @type Number */\r
3051     CTRL: 17,\r
3052     CONTROL : 17, // legacy\r
3053     /** Key constant @type Number */\r
3054     ALT: 18,\r
3055     /** Key constant @type Number */\r
3056     PAUSE: 19,\r
3057     /** Key constant @type Number */\r
3058     CAPS_LOCK: 20,\r
3059     /** Key constant @type Number */\r
3060     ESC: 27,\r
3061     /** Key constant @type Number */\r
3062     SPACE: 32,\r
3063     /** Key constant @type Number */\r
3064     PAGE_UP: 33,\r
3065     PAGEUP : 33, // legacy\r
3066     /** Key constant @type Number */\r
3067     PAGE_DOWN: 34,\r
3068     PAGEDOWN : 34, // legacy\r
3069     /** Key constant @type Number */\r
3070     END: 35,\r
3071     /** Key constant @type Number */\r
3072     HOME: 36,\r
3073     /** Key constant @type Number */\r
3074     LEFT: 37,\r
3075     /** Key constant @type Number */\r
3076     UP: 38,\r
3077     /** Key constant @type Number */\r
3078     RIGHT: 39,\r
3079     /** Key constant @type Number */\r
3080     DOWN: 40,\r
3081     /** Key constant @type Number */\r
3082     PRINT_SCREEN: 44,\r
3083     /** Key constant @type Number */\r
3084     INSERT: 45,\r
3085     /** Key constant @type Number */\r
3086     DELETE: 46,\r
3087     /** Key constant @type Number */\r
3088     ZERO: 48,\r
3089     /** Key constant @type Number */\r
3090     ONE: 49,\r
3091     /** Key constant @type Number */\r
3092     TWO: 50,\r
3093     /** Key constant @type Number */\r
3094     THREE: 51,\r
3095     /** Key constant @type Number */\r
3096     FOUR: 52,\r
3097     /** Key constant @type Number */\r
3098     FIVE: 53,\r
3099     /** Key constant @type Number */\r
3100     SIX: 54,\r
3101     /** Key constant @type Number */\r
3102     SEVEN: 55,\r
3103     /** Key constant @type Number */\r
3104     EIGHT: 56,\r
3105     /** Key constant @type Number */\r
3106     NINE: 57,\r
3107     /** Key constant @type Number */\r
3108     A: 65,\r
3109     /** Key constant @type Number */\r
3110     B: 66,\r
3111     /** Key constant @type Number */\r
3112     C: 67,\r
3113     /** Key constant @type Number */\r
3114     D: 68,\r
3115     /** Key constant @type Number */\r
3116     E: 69,\r
3117     /** Key constant @type Number */\r
3118     F: 70,\r
3119     /** Key constant @type Number */\r
3120     G: 71,\r
3121     /** Key constant @type Number */\r
3122     H: 72,\r
3123     /** Key constant @type Number */\r
3124     I: 73,\r
3125     /** Key constant @type Number */\r
3126     J: 74,\r
3127     /** Key constant @type Number */\r
3128     K: 75,\r
3129     /** Key constant @type Number */\r
3130     L: 76,\r
3131     /** Key constant @type Number */\r
3132     M: 77,\r
3133     /** Key constant @type Number */\r
3134     N: 78,\r
3135     /** Key constant @type Number */\r
3136     O: 79,\r
3137     /** Key constant @type Number */\r
3138     P: 80,\r
3139     /** Key constant @type Number */\r
3140     Q: 81,\r
3141     /** Key constant @type Number */\r
3142     R: 82,\r
3143     /** Key constant @type Number */\r
3144     S: 83,\r
3145     /** Key constant @type Number */\r
3146     T: 84,\r
3147     /** Key constant @type Number */\r
3148     U: 85,\r
3149     /** Key constant @type Number */\r
3150     V: 86,\r
3151     /** Key constant @type Number */\r
3152     W: 87,\r
3153     /** Key constant @type Number */\r
3154     X: 88,\r
3155     /** Key constant @type Number */\r
3156     Y: 89,\r
3157     /** Key constant @type Number */\r
3158     Z: 90,\r
3159     /** Key constant @type Number */\r
3160     CONTEXT_MENU: 93,\r
3161     /** Key constant @type Number */\r
3162     NUM_ZERO: 96,\r
3163     /** Key constant @type Number */\r
3164     NUM_ONE: 97,\r
3165     /** Key constant @type Number */\r
3166     NUM_TWO: 98,\r
3167     /** Key constant @type Number */\r
3168     NUM_THREE: 99,\r
3169     /** Key constant @type Number */\r
3170     NUM_FOUR: 100,\r
3171     /** Key constant @type Number */\r
3172     NUM_FIVE: 101,\r
3173     /** Key constant @type Number */\r
3174     NUM_SIX: 102,\r
3175     /** Key constant @type Number */\r
3176     NUM_SEVEN: 103,\r
3177     /** Key constant @type Number */\r
3178     NUM_EIGHT: 104,\r
3179     /** Key constant @type Number */\r
3180     NUM_NINE: 105,\r
3181     /** Key constant @type Number */\r
3182     NUM_MULTIPLY: 106,\r
3183     /** Key constant @type Number */\r
3184     NUM_PLUS: 107,\r
3185     /** Key constant @type Number */\r
3186     NUM_MINUS: 109,\r
3187     /** Key constant @type Number */\r
3188     NUM_PERIOD: 110,\r
3189     /** Key constant @type Number */\r
3190     NUM_DIVISION: 111,\r
3191     /** Key constant @type Number */\r
3192     F1: 112,\r
3193     /** Key constant @type Number */\r
3194     F2: 113,\r
3195     /** Key constant @type Number */\r
3196     F3: 114,\r
3197     /** Key constant @type Number */\r
3198     F4: 115,\r
3199     /** Key constant @type Number */\r
3200     F5: 116,\r
3201     /** Key constant @type Number */\r
3202     F6: 117,\r
3203     /** Key constant @type Number */\r
3204     F7: 118,\r
3205     /** Key constant @type Number */\r
3206     F8: 119,\r
3207     /** Key constant @type Number */\r
3208     F9: 120,\r
3209     /** Key constant @type Number */\r
3210     F10: 121,\r
3211     /** Key constant @type Number */\r
3212     F11: 122,\r
3213     /** Key constant @type Number */\r
3214     F12: 123,   \r
3215     \r
3216     /** @private */\r
3217     isNavKeyPress : function(){\r
3218         var me = this,\r
3219                 k = this.normalizeKey(me.keyCode);              \r
3220         return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down\r
3221                 k == me.RETURN ||\r
3222                 k == me.TAB ||\r
3223                 k == me.ESC;\r
3224     },\r
3225 \r
3226     isSpecialKey : function(){\r
3227         var k = this.normalizeKey(this.keyCode);\r
3228         return (this.type == 'keypress' && this.ctrlKey) ||\r
3229                 this.isNavKeyPress() ||\r
3230         (k == this.BACKSPACE) || // Backspace\r
3231                 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock\r
3232                 (k >= 44 && k <= 45);   // Print Screen, Insert\r
3233     },\r
3234         \r
3235         getPoint : function(){\r
3236             return new Ext.lib.Point(this.xy[0], this.xy[1]);\r
3237         },\r
3238 \r
3239     /**\r
3240      * Returns true if the control, meta, shift or alt key was pressed during this event.\r
3241      * @return {Boolean}\r
3242      */\r
3243     hasModifier : function(){\r
3244         return ((this.ctrlKey || this.altKey) || this.shiftKey);\r
3245     }\r
3246 });/**\r
3247  * @class Ext.Element\r
3248  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>\r
3249  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>\r
3250  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To\r
3251  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older\r
3252  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>\r
3253  * Usage:<br>\r
3254 <pre><code>\r
3255 // by id\r
3256 var el = Ext.get("my-div");\r
3257 \r
3258 // by DOM element reference\r
3259 var el = Ext.get(myDivElement);\r
3260 </code></pre>\r
3261  * <b>Animations</b><br />\r
3262  * <p>When an element is manipulated, by default there is no animation.</p>\r
3263  * <pre><code>\r
3264 var el = Ext.get("my-div");\r
3265 \r
3266 // no animation\r
3267 el.setWidth(100);\r
3268  * </code></pre>\r
3269  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This\r
3270  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>\r
3271  * <pre><code>\r
3272 // default animation\r
3273 el.setWidth(100, true);\r
3274  * </code></pre>\r
3275  * \r
3276  * <p>To configure the effects, an object literal with animation options to use as the Element animation\r
3277  * configuration object can also be specified. Note that the supported Element animation configuration\r
3278  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported\r
3279  * Element animation configuration options are:</p>\r
3280 <pre>\r
3281 Option    Default   Description\r
3282 --------- --------  ---------------------------------------------\r
3283 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds\r
3284 {@link Ext.Fx#easing easing}    easeOut   The easing method\r
3285 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes\r
3286 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function\r
3287 </pre>\r
3288  * \r
3289  * <pre><code>\r
3290 // Element animation options object\r
3291 var opt = {\r
3292     {@link Ext.Fx#duration duration}: 1,\r
3293     {@link Ext.Fx#easing easing}: 'elasticIn',\r
3294     {@link Ext.Fx#callback callback}: this.foo,\r
3295     {@link Ext.Fx#scope scope}: this\r
3296 };\r
3297 // animation with some options set\r
3298 el.setWidth(100, opt);\r
3299  * </code></pre>\r
3300  * <p>The Element animation object being used for the animation will be set on the options\r
3301  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>\r
3302  * <pre><code>\r
3303 // using the "anim" property to get the Anim object\r
3304 if(opt.anim.isAnimated()){\r
3305     opt.anim.stop();\r
3306 }\r
3307  * </code></pre>\r
3308  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>\r
3309  * <p><b> Composite (Collections of) Elements</b></p>\r
3310  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>\r
3311  * @constructor Create a new Element directly.\r
3312  * @param {String/HTMLElement} element\r
3313  * @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).\r
3314  */\r
3315 (function(){\r
3316 var DOC = document;\r
3317 \r
3318 Ext.Element = function(element, forceNew){\r
3319     var dom = typeof element == "string" ?\r
3320               DOC.getElementById(element) : element,\r
3321         id;\r
3322 \r
3323     if(!dom) return null;\r
3324 \r
3325     id = dom.id;\r
3326 \r
3327     if(!forceNew && id && Ext.Element.cache[id]){ // element object already exists\r
3328         return Ext.Element.cache[id];\r
3329     }\r
3330 \r
3331     /**\r
3332      * The DOM element\r
3333      * @type HTMLElement\r
3334      */\r
3335     this.dom = dom;\r
3336 \r
3337     /**\r
3338      * The DOM element ID\r
3339      * @type String\r
3340      */\r
3341     this.id = id || Ext.id(dom);\r
3342 };\r
3343 \r
3344 var D = Ext.lib.Dom,\r
3345     DH = Ext.DomHelper,\r
3346     E = Ext.lib.Event,\r
3347     A = Ext.lib.Anim,\r
3348     El = Ext.Element;\r
3349 \r
3350 El.prototype = {\r
3351     /**\r
3352      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)\r
3353      * @param {Object} o The object with the attributes\r
3354      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.\r
3355      * @return {Ext.Element} this\r
3356      */\r
3357     set : function(o, useSet){\r
3358         var el = this.dom,\r
3359             attr,\r
3360             val;        \r
3361        \r
3362         for(attr in o){\r
3363             val = o[attr];\r
3364             if (attr != "style" && !Ext.isFunction(val)) {\r
3365                 if (attr == "cls" ) {\r
3366                     el.className = val;\r
3367                 } else if (o.hasOwnProperty(attr)) {\r
3368                     if (useSet || !!el.setAttribute) el.setAttribute(attr, val);\r
3369                     else el[attr] = val;\r
3370                 }\r
3371             }\r
3372         }\r
3373         if(o.style){\r
3374             Ext.DomHelper.applyStyles(el, o.style);\r
3375         }\r
3376         return this;\r
3377     },\r
3378     \r
3379 //  Mouse events\r
3380     /**\r
3381      * @event click\r
3382      * Fires when a mouse click is detected within the element.\r
3383      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3384      * @param {HtmlElement} t The target of the event.\r
3385      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3386      */\r
3387     /**\r
3388      * @event dblclick\r
3389      * Fires when a mouse double click is detected within the element.\r
3390      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3391      * @param {HtmlElement} t The target of the event.\r
3392      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3393      */\r
3394     /**\r
3395      * @event mousedown\r
3396      * Fires when a mousedown is detected within the element.\r
3397      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3398      * @param {HtmlElement} t The target of the event.\r
3399      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3400      */\r
3401     /**\r
3402      * @event mouseup\r
3403      * Fires when a mouseup is detected within the element.\r
3404      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3405      * @param {HtmlElement} t The target of the event.\r
3406      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3407      */\r
3408     /**\r
3409      * @event mouseover\r
3410      * Fires when a mouseover is detected within the element.\r
3411      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3412      * @param {HtmlElement} t The target of the event.\r
3413      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3414      */\r
3415     /**\r
3416      * @event mousemove\r
3417      * Fires when a mousemove is detected with the element.\r
3418      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3419      * @param {HtmlElement} t The target of the event.\r
3420      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3421      */\r
3422     /**\r
3423      * @event mouseout\r
3424      * Fires when a mouseout is detected with the element.\r
3425      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3426      * @param {HtmlElement} t The target of the event.\r
3427      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3428      */\r
3429     /**\r
3430      * @event mouseenter\r
3431      * Fires when the mouse enters the element.\r
3432      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3433      * @param {HtmlElement} t The target of the event.\r
3434      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3435      */\r
3436     /**\r
3437      * @event mouseleave\r
3438      * Fires when the mouse leaves the element.\r
3439      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3440      * @param {HtmlElement} t The target of the event.\r
3441      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3442      */\r
3443     \r
3444 //  Keyboard events\r
3445     /**\r
3446      * @event keypress\r
3447      * Fires when a keypress is detected within the element.\r
3448      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3449      * @param {HtmlElement} t The target of the event.\r
3450      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3451      */\r
3452     /**\r
3453      * @event keydown\r
3454      * Fires when a keydown is detected within the element.\r
3455      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3456      * @param {HtmlElement} t The target of the event.\r
3457      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3458      */\r
3459     /**\r
3460      * @event keyup\r
3461      * Fires when a keyup is detected within the element.\r
3462      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3463      * @param {HtmlElement} t The target of the event.\r
3464      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3465      */\r
3466 \r
3467 \r
3468 //  HTML frame/object events\r
3469     /**\r
3470      * @event load\r
3471      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.\r
3472      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3473      * @param {HtmlElement} t The target of the event.\r
3474      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3475      */\r
3476     /**\r
3477      * @event unload\r
3478      * 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.\r
3479      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3480      * @param {HtmlElement} t The target of the event.\r
3481      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3482      */\r
3483     /**\r
3484      * @event abort\r
3485      * Fires when an object/image is stopped from loading before completely loaded.\r
3486      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3487      * @param {HtmlElement} t The target of the event.\r
3488      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3489      */\r
3490     /**\r
3491      * @event error\r
3492      * Fires when an object/image/frame cannot be loaded properly.\r
3493      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3494      * @param {HtmlElement} t The target of the event.\r
3495      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3496      */\r
3497     /**\r
3498      * @event resize\r
3499      * Fires when a document view is resized.\r
3500      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3501      * @param {HtmlElement} t The target of the event.\r
3502      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3503      */\r
3504     /**\r
3505      * @event scroll\r
3506      * Fires when a document view is scrolled.\r
3507      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3508      * @param {HtmlElement} t The target of the event.\r
3509      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3510      */\r
3511 \r
3512 //  Form events\r
3513     /**\r
3514      * @event select\r
3515      * Fires when a user selects some text in a text field, including input and textarea.\r
3516      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3517      * @param {HtmlElement} t The target of the event.\r
3518      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3519      */\r
3520     /**\r
3521      * @event change\r
3522      * Fires when a control loses the input focus and its value has been modified since gaining focus.\r
3523      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3524      * @param {HtmlElement} t The target of the event.\r
3525      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3526      */\r
3527     /**\r
3528      * @event submit\r
3529      * Fires when a form is submitted.\r
3530      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3531      * @param {HtmlElement} t The target of the event.\r
3532      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3533      */\r
3534     /**\r
3535      * @event reset\r
3536      * Fires when a form is reset.\r
3537      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3538      * @param {HtmlElement} t The target of the event.\r
3539      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3540      */\r
3541     /**\r
3542      * @event focus\r
3543      * Fires when an element receives focus either via the pointing device or by tab navigation.\r
3544      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3545      * @param {HtmlElement} t The target of the event.\r
3546      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3547      */\r
3548     /**\r
3549      * @event blur\r
3550      * Fires when an element loses focus either via the pointing device or by tabbing navigation.\r
3551      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3552      * @param {HtmlElement} t The target of the event.\r
3553      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3554      */\r
3555 \r
3556 //  User Interface events\r
3557     /**\r
3558      * @event DOMFocusIn\r
3559      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.\r
3560      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3561      * @param {HtmlElement} t The target of the event.\r
3562      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3563      */\r
3564     /**\r
3565      * @event DOMFocusOut\r
3566      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.\r
3567      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3568      * @param {HtmlElement} t The target of the event.\r
3569      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3570      */\r
3571     /**\r
3572      * @event DOMActivate\r
3573      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.\r
3574      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3575      * @param {HtmlElement} t The target of the event.\r
3576      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3577      */\r
3578 \r
3579 //  DOM Mutation events\r
3580     /**\r
3581      * @event DOMSubtreeModified\r
3582      * Where supported. Fires when the subtree is modified.\r
3583      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3584      * @param {HtmlElement} t The target of the event.\r
3585      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3586      */\r
3587     /**\r
3588      * @event DOMNodeInserted\r
3589      * Where supported. Fires when a node has been added as a child of another node.\r
3590      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3591      * @param {HtmlElement} t The target of the event.\r
3592      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3593      */\r
3594     /**\r
3595      * @event DOMNodeRemoved\r
3596      * Where supported. Fires when a descendant node of the element is removed.\r
3597      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3598      * @param {HtmlElement} t The target of the event.\r
3599      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3600      */\r
3601     /**\r
3602      * @event DOMNodeRemovedFromDocument\r
3603      * Where supported. Fires when a node is being removed from a document.\r
3604      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3605      * @param {HtmlElement} t The target of the event.\r
3606      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3607      */\r
3608     /**\r
3609      * @event DOMNodeInsertedIntoDocument\r
3610      * Where supported. Fires when a node is being inserted into a document.\r
3611      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3612      * @param {HtmlElement} t The target of the event.\r
3613      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3614      */\r
3615     /**\r
3616      * @event DOMAttrModified\r
3617      * Where supported. Fires when an attribute has been modified.\r
3618      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3619      * @param {HtmlElement} t The target of the event.\r
3620      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3621      */\r
3622     /**\r
3623      * @event DOMCharacterDataModified\r
3624      * Where supported. Fires when the character data has been modified.\r
3625      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3626      * @param {HtmlElement} t The target of the event.\r
3627      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3628      */\r
3629 \r
3630     /**\r
3631      * The default unit to append to CSS values where a unit isn't provided (defaults to px).\r
3632      * @type String\r
3633      */\r
3634     defaultUnit : "px",\r
3635 \r
3636     /**\r
3637      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)\r
3638      * @param {String} selector The simple selector to test\r
3639      * @return {Boolean} True if this element matches the selector, else false\r
3640      */\r
3641     is : function(simpleSelector){\r
3642         return Ext.DomQuery.is(this.dom, simpleSelector);\r
3643     },\r
3644 \r
3645     /**\r
3646      * Tries to focus the element. Any exceptions are caught and ignored.\r
3647      * @param {Number} defer (optional) Milliseconds to defer the focus\r
3648      * @return {Ext.Element} this\r
3649      */\r
3650     focus : function(defer, /* private */ dom) {\r
3651         var me = this,\r
3652             dom = dom || me.dom;\r
3653         try{\r
3654             if(Number(defer)){\r
3655                 me.focus.defer(defer, null, [null, dom]);\r
3656             }else{\r
3657                 dom.focus();\r
3658             }\r
3659         }catch(e){}\r
3660         return me;\r
3661     },\r
3662 \r
3663     /**\r
3664      * Tries to blur the element. Any exceptions are caught and ignored.\r
3665      * @return {Ext.Element} this\r
3666      */\r
3667     blur : function() {\r
3668         try{\r
3669             this.dom.blur();\r
3670         }catch(e){}\r
3671         return this;\r
3672     },\r
3673 \r
3674     /**\r
3675      * Returns the value of the "value" attribute\r
3676      * @param {Boolean} asNumber true to parse the value as a number\r
3677      * @return {String/Number}\r
3678      */\r
3679     getValue : function(asNumber){\r
3680         var val = this.dom.value;\r
3681         return asNumber ? parseInt(val, 10) : val;\r
3682     },\r
3683 \r
3684     /**\r
3685      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.\r
3686      * @param {String} eventName The type of event to handle\r
3687      * @param {Function} fn The handler function the event invokes. This function is passed\r
3688      * the following parameters:<ul>\r
3689      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
3690      * <li><b>el</b> : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.\r
3691      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
3692      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
3693      * </ul>\r
3694      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.\r
3695      * <b>If omitted, defaults to this Element.</b>.\r
3696      * @param {Object} options (optional) An object containing handler configuration properties.\r
3697      * This may contain any of the following properties:<ul>\r
3698      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.\r
3699      * <b>If omitted, defaults to this Element.</b></div></li>\r
3700      * <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>\r
3701      * <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>\r
3702      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>\r
3703      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>\r
3704      * <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>\r
3705      * <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>\r
3706      * <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>\r
3707      * <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>\r
3708      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed\r
3709      * by the specified number of milliseconds. If the event fires again within that time, the original\r
3710      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>\r
3711      * </ul><br>\r
3712      * <p>\r
3713      * <b>Combining Options</b><br>\r
3714      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose\r
3715      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different\r
3716      * types of listeners:<br>\r
3717      * <br>\r
3718      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the\r
3719      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">\r
3720      * Code:<pre><code>\r
3721 el.on('click', this.onClick, this, {\r
3722     single: true,\r
3723     delay: 100,\r
3724     stopEvent : true,\r
3725     forumId: 4\r
3726 });</code></pre></p>\r
3727      * <p>\r
3728      * <b>Attaching multiple handlers in 1 call</b><br>\r
3729      * The method also allows for a single argument to be passed which is a config object containing properties\r
3730      * which specify multiple handlers.</p>\r
3731      * <p>\r
3732      * Code:<pre><code>\r
3733 el.on({\r
3734     'click' : {\r
3735         fn: this.onClick,\r
3736         scope: this,\r
3737         delay: 100\r
3738     },\r
3739     'mouseover' : {\r
3740         fn: this.onMouseOver,\r
3741         scope: this\r
3742     },\r
3743     'mouseout' : {\r
3744         fn: this.onMouseOut,\r
3745         scope: this\r
3746     }\r
3747 });</code></pre>\r
3748      * <p>\r
3749      * Or a shorthand syntax:<br>\r
3750      * Code:<pre><code></p>\r
3751 el.on({\r
3752     'click' : this.onClick,\r
3753     'mouseover' : this.onMouseOver,\r
3754     'mouseout' : this.onMouseOut,\r
3755     scope: this\r
3756 });\r
3757      * </code></pre></p>\r
3758      * <p><b>delegate</b></p>\r
3759      * <p>This is a configuration option that you can pass along when registering a handler for\r
3760      * an event to assist with event delegation. Event delegation is a technique that is used to\r
3761      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event\r
3762      * for a container element as opposed to each element within a container. By setting this\r
3763      * configuration option to a simple selector, the target element will be filtered to look for\r
3764      * a descendant of the target.\r
3765      * For example:<pre><code>\r
3766 // using this markup:\r
3767 &lt;div id='elId'>\r
3768     &lt;p id='p1'>paragraph one&lt;/p>\r
3769     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>\r
3770     &lt;p id='p3'>paragraph three&lt;/p>\r
3771 &lt;/div>\r
3772 // utilize event delegation to registering just one handler on the container element: \r
3773 el = Ext.get('elId');\r
3774 el.on(\r
3775     'click',\r
3776     function(e,t) {\r
3777         // handle click\r
3778         console.info(t.id); // 'p2'\r
3779     },\r
3780     this,\r
3781     {\r
3782         // filter the target element to be a descendant with the class 'clickable'\r
3783         delegate: '.clickable' \r
3784     }\r
3785 );\r
3786      * </code></pre></p>\r
3787      * @return {Ext.Element} this\r
3788      */\r
3789     addListener : function(eventName, fn, scope, options){\r
3790         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);\r
3791         return this;\r
3792     },\r
3793 \r
3794     /**\r
3795      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.\r
3796      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the\r
3797      * listener, the same scope must be specified here.\r
3798      * Example:\r
3799      * <pre><code>\r
3800 el.removeListener('click', this.handlerFn);\r
3801 // or\r
3802 el.un('click', this.handlerFn);\r
3803 </code></pre>\r
3804      * @param {String} eventName the type of event to remove\r
3805      * @param {Function} fn the method the event invokes\r
3806      * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults\r
3807      * to this Element.\r
3808      * @return {Ext.Element} this\r
3809      */\r
3810     removeListener : function(eventName, fn, scope){\r
3811         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);\r
3812         return this;\r
3813     },\r
3814 \r
3815     /**\r
3816      * Removes all previous added listeners from this element\r
3817      * @return {Ext.Element} this\r
3818      */\r
3819     removeAllListeners : function(){\r
3820         Ext.EventManager.removeAll(this.dom);\r
3821         return this;\r
3822     },\r
3823 \r
3824     /**\r
3825      * @private Test if size has a unit, otherwise appends the default\r
3826      */\r
3827     addUnits : function(size){\r
3828         if(size === "" || size == "auto" || size === undefined){\r
3829             size = size || '';\r
3830         } else if(!isNaN(size) || !unitPattern.test(size)){\r
3831             size = size + (this.defaultUnit || 'px');\r
3832         }\r
3833         return size;\r
3834     },\r
3835 \r
3836     /**\r
3837      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element\r
3838      * 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>\r
3839      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>\r
3840      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying\r
3841      * exactly how to request the HTML.\r
3842      * @return {Ext.Element} this\r
3843      */\r
3844     load : function(url, params, cb){\r
3845         Ext.Ajax.request(Ext.apply({\r
3846             params: params,\r
3847             url: url.url || url,\r
3848             callback: cb,\r
3849             el: this.dom,\r
3850             indicatorText: url.indicatorText || ''\r
3851         }, Ext.isObject(url) ? url : {}));\r
3852         return this;\r
3853     },\r
3854 \r
3855     /**\r
3856      * Tests various css rules/browsers to determine if this element uses a border box\r
3857      * @return {Boolean}\r
3858      */\r
3859     isBorderBox : function(){\r
3860         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;\r
3861     },\r
3862 \r
3863     /**\r
3864      * Removes this element from the DOM and deletes it from the cache\r
3865      */\r
3866     remove : function(){\r
3867         var me = this,\r
3868             dom = me.dom;\r
3869         \r
3870         me.removeAllListeners();\r
3871         delete El.cache[dom.id];\r
3872         delete El.dataCache[dom.id]\r
3873         Ext.removeNode(dom);\r
3874     },\r
3875 \r
3876     /**\r
3877      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.\r
3878      * @param {Function} overFn The function to call when the mouse enters the Element.\r
3879      * @param {Function} outFn The function to call when the mouse leaves the Element.\r
3880      * @param {Object} scope (optional) The scope (<tt>this</tt> reference) in which the functions are executed. Defaults to the Element's DOM element.\r
3881      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.\r
3882      * @return {Ext.Element} this\r
3883      */\r
3884     hover : function(overFn, outFn, scope, options){\r
3885         var me = this;\r
3886         me.on('mouseenter', overFn, scope || me.dom, options);\r
3887         me.on('mouseleave', outFn, scope || me.dom, options);\r
3888         return me;\r
3889     },\r
3890 \r
3891     /**\r
3892      * Returns true if this element is an ancestor of the passed element\r
3893      * @param {HTMLElement/String} el The element to check\r
3894      * @return {Boolean} True if this element is an ancestor of el, else false\r
3895      */\r
3896     contains : function(el){\r
3897         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);\r
3898     },\r
3899 \r
3900     /**\r
3901      * Returns the value of a namespaced attribute from the element's underlying DOM node.\r
3902      * @param {String} namespace The namespace in which to look for the attribute\r
3903      * @param {String} name The attribute name\r
3904      * @return {String} The attribute value\r
3905      * @deprecated\r
3906      */\r
3907     getAttributeNS : function(ns, name){\r
3908         return this.getAttribute(name, ns); \r
3909     },\r
3910     \r
3911     /**\r
3912      * Returns the value of an attribute from the element's underlying DOM node.\r
3913      * @param {String} name The attribute name\r
3914      * @param {String} namespace (optional) The namespace in which to look for the attribute\r
3915      * @return {String} The attribute value\r
3916      */\r
3917     getAttribute : Ext.isIE ? function(name, ns){\r
3918         var d = this.dom,\r
3919             type = typeof d[ns + ":" + name];\r
3920 \r
3921         if(['undefined', 'unknown'].indexOf(type) == -1){\r
3922             return d[ns + ":" + name];\r
3923         }\r
3924         return d[name];\r
3925     } : function(name, ns){\r
3926         var d = this.dom;\r
3927         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];\r
3928     },\r
3929     \r
3930     /**\r
3931     * Update the innerHTML of this element\r
3932     * @param {String} html The new HTML\r
3933     * @return {Ext.Element} this\r
3934      */\r
3935     update : function(html) {\r
3936         this.dom.innerHTML = html;\r
3937         return this;\r
3938     }\r
3939 };\r
3940 \r
3941 var ep = El.prototype;\r
3942 \r
3943 El.addMethods = function(o){\r
3944    Ext.apply(ep, o);\r
3945 };\r
3946 \r
3947 /**\r
3948  * Appends an event handler (shorthand for {@link #addListener}).\r
3949  * @param {String} eventName The type of event to handle\r
3950  * @param {Function} fn The handler function the event invokes\r
3951  * @param {Object} scope (optional) The scope (this element) of the handler function\r
3952  * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
3953  * @member Ext.Element\r
3954  * @method on\r
3955  */\r
3956 ep.on = ep.addListener;\r
3957 \r
3958 /**\r
3959  * Removes an event handler from this element (see {@link #removeListener} for additional notes).\r
3960  * @param {String} eventName the type of event to remove\r
3961  * @param {Function} fn the method the event invokes\r
3962  * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults\r
3963  * to this Element.\r
3964  * @return {Ext.Element} this\r
3965  * @member Ext.Element\r
3966  * @method un\r
3967  */\r
3968 ep.un = ep.removeListener;\r
3969 \r
3970 /**\r
3971  * true to automatically adjust width and height settings for box-model issues (default to true)\r
3972  */\r
3973 ep.autoBoxAdjust = true;\r
3974 \r
3975 // private\r
3976 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,\r
3977     docEl;\r
3978 \r
3979 /**\r
3980  * @private\r
3981  */\r
3982 El.cache = {};\r
3983 El.dataCache = {};\r
3984 \r
3985 /**\r
3986  * Retrieves Ext.Element objects.\r
3987  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method\r
3988  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by\r
3989  * its ID, use {@link Ext.ComponentMgr#get}.</p>\r
3990  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an\r
3991  * object was recreated with the same id via AJAX or DOM.</p>\r
3992  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
3993  * @return {Element} The Element object (or null if no matching element was found)\r
3994  * @static\r
3995  * @member Ext.Element\r
3996  * @method get\r
3997  */\r
3998 El.get = function(el){\r
3999     var ex,\r
4000         elm,\r
4001         id;\r
4002     if(!el){ return null; }\r
4003     if (typeof el == "string") { // element id\r
4004         if (!(elm = DOC.getElementById(el))) {\r
4005             return null;\r
4006         }\r
4007         if (ex = El.cache[el]) {\r
4008             ex.dom = elm;\r
4009         } else {\r
4010             ex = El.cache[el] = new El(elm);\r
4011         }\r
4012         return ex;\r
4013     } else if (el.tagName) { // dom element\r
4014         if(!(id = el.id)){\r
4015             id = Ext.id(el);\r
4016         }\r
4017         if(ex = El.cache[id]){\r
4018             ex.dom = el;\r
4019         }else{\r
4020             ex = El.cache[id] = new El(el);\r
4021         }\r
4022         return ex;\r
4023     } else if (el instanceof El) {\r
4024         if(el != docEl){\r
4025             el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,\r
4026                                                           // catch case where it hasn't been appended\r
4027             El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it\r
4028         }\r
4029         return el;\r
4030     } else if(el.isComposite) {\r
4031         return el;\r
4032     } else if(Ext.isArray(el)) {\r
4033         return El.select(el);\r
4034     } else if(el == DOC) {\r
4035         // create a bogus element object representing the document object\r
4036         if(!docEl){\r
4037             var f = function(){};\r
4038             f.prototype = El.prototype;\r
4039             docEl = new f();\r
4040             docEl.dom = DOC;\r
4041         }\r
4042         return docEl;\r
4043     }\r
4044     return null;\r
4045 };\r
4046 \r
4047 // private method for getting and setting element data\r
4048 El.data = function(el, key, value){\r
4049     var c = El.dataCache[el.id];\r
4050     if(!c){\r
4051         c = El.dataCache[el.id] = {};\r
4052     }\r
4053     if(arguments.length == 2){\r
4054         return c[key];    \r
4055     }else{\r
4056         c[key] = value;\r
4057     }\r
4058 };\r
4059 \r
4060 // private\r
4061 // Garbage collection - uncache elements/purge listeners on orphaned elements\r
4062 // so we don't hold a reference and cause the browser to retain them\r
4063 function garbageCollect(){\r
4064     if(!Ext.enableGarbageCollector){\r
4065         clearInterval(El.collectorThread);\r
4066     } else {\r
4067         var eid,\r
4068             el,\r
4069             d;\r
4070 \r
4071         for(eid in El.cache){\r
4072             el = El.cache[eid];\r
4073             d = el.dom;\r
4074             // -------------------------------------------------------\r
4075             // Determining what is garbage:\r
4076             // -------------------------------------------------------\r
4077             // !d\r
4078             // dom node is null, definitely garbage\r
4079             // -------------------------------------------------------\r
4080             // !d.parentNode\r
4081             // no parentNode == direct orphan, definitely garbage\r
4082             // -------------------------------------------------------\r
4083             // !d.offsetParent && !document.getElementById(eid)\r
4084             // display none elements have no offsetParent so we will\r
4085             // also try to look it up by it's id. However, check\r
4086             // offsetParent first so we don't do unneeded lookups.\r
4087             // This enables collection of elements that are not orphans\r
4088             // directly, but somewhere up the line they have an orphan\r
4089             // parent.\r
4090             // -------------------------------------------------------\r
4091             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){\r
4092                 delete El.cache[eid];\r
4093                 if(d && Ext.enableListenerCollection){\r
4094                     Ext.EventManager.removeAll(d);\r
4095                 }\r
4096             }\r
4097         }\r
4098     }\r
4099 }\r
4100 El.collectorThreadId = setInterval(garbageCollect, 30000);\r
4101 \r
4102 var flyFn = function(){};\r
4103 flyFn.prototype = El.prototype;\r
4104 \r
4105 // dom is optional\r
4106 El.Flyweight = function(dom){\r
4107     this.dom = dom;\r
4108 };\r
4109 \r
4110 El.Flyweight.prototype = new flyFn();\r
4111 El.Flyweight.prototype.isFlyweight = true;\r
4112 El._flyweights = {};\r
4113 \r
4114 /**\r
4115  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
4116  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>\r
4117  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by\r
4118  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}\r
4119  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>\r
4120  * @param {String/HTMLElement} el The dom node or id\r
4121  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts\r
4122  * (e.g. internally Ext uses "_global")\r
4123  * @return {Element} The shared Element object (or null if no matching element was found)\r
4124  * @member Ext.Element\r
4125  * @method fly\r
4126  */\r
4127 El.fly = function(el, named){\r
4128     var ret = null;\r
4129     named = named || '_global';\r
4130 \r
4131     if (el = Ext.getDom(el)) {\r
4132         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;\r
4133         ret = El._flyweights[named];\r
4134     }\r
4135     return ret;\r
4136 };\r
4137 \r
4138 /**\r
4139  * Retrieves Ext.Element objects.\r
4140  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method\r
4141  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by\r
4142  * its ID, use {@link Ext.ComponentMgr#get}.</p>\r
4143  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an\r
4144  * object was recreated with the same id via AJAX or DOM.</p>\r
4145  * Shorthand of {@link Ext.Element#get}\r
4146  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
4147  * @return {Element} The Element object (or null if no matching element was found)\r
4148  * @member Ext\r
4149  * @method get\r
4150  */\r
4151 Ext.get = El.get;\r
4152 \r
4153 /**\r
4154  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
4155  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>\r
4156  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by\r
4157  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}\r
4158  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>\r
4159  * @param {String/HTMLElement} el The dom node or id\r
4160  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts\r
4161  * (e.g. internally Ext uses "_global")\r
4162  * @return {Element} The shared Element object (or null if no matching element was found)\r
4163  * @member Ext\r
4164  * @method fly\r
4165  */\r
4166 Ext.fly = El.fly;\r
4167 \r
4168 // speedy lookup for elements never to box adjust\r
4169 var noBoxAdjust = Ext.isStrict ? {\r
4170     select:1\r
4171 } : {\r
4172     input:1, select:1, textarea:1\r
4173 };\r
4174 if(Ext.isIE || Ext.isGecko){\r
4175     noBoxAdjust['button'] = 1;\r
4176 }\r
4177 \r
4178 \r
4179 Ext.EventManager.on(window, 'unload', function(){\r
4180     delete El.cache;\r
4181     delete El.dataCache;\r
4182     delete El._flyweights;\r
4183 });\r
4184 })();\r
4185 /**\r
4186  * @class Ext.Element\r
4187  */\r
4188 Ext.Element.addMethods({    \r
4189     /**\r
4190      * Stops the specified event(s) from bubbling and optionally prevents the default action\r
4191      * @param {String/Array} eventName an event / array of events to stop from bubbling\r
4192      * @param {Boolean} preventDefault (optional) true to prevent the default action too\r
4193      * @return {Ext.Element} this\r
4194      */\r
4195     swallowEvent : function(eventName, preventDefault){\r
4196             var me = this;\r
4197         function fn(e){\r
4198             e.stopPropagation();\r
4199             if(preventDefault){\r
4200                 e.preventDefault();\r
4201             }\r
4202         }\r
4203         if(Ext.isArray(eventName)){            \r
4204                 Ext.each(eventName, function(e) {\r
4205                  me.on(e, fn);\r
4206             });\r
4207             return me;\r
4208         }\r
4209         me.on(eventName, fn);\r
4210         return me;\r
4211     },\r
4212     \r
4213     /**\r
4214      * Create an event handler on this element such that when the event fires and is handled by this element,\r
4215      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).\r
4216      * @param {String} eventName The type of event to relay\r
4217      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context\r
4218      * for firing the relayed event\r
4219      */\r
4220     relayEvent : function(eventName, observable){\r
4221         this.on(eventName, function(e){\r
4222             observable.fireEvent(eventName, e);\r
4223         });\r
4224     },\r
4225     \r
4226         /**\r
4227      * Removes worthless text nodes\r
4228      * @param {Boolean} forceReclean (optional) By default the element\r
4229      * keeps track if it has been cleaned already so\r
4230      * you can call this over and over. However, if you update the element and\r
4231      * need to force a reclean, you can pass true.\r
4232      */\r
4233     clean : function(forceReclean){\r
4234         var me = this, \r
4235             dom = me.dom,\r
4236                 n = dom.firstChild, \r
4237                 ni = -1;\r
4238                 \r
4239             if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){\r
4240             return me;\r
4241         }      \r
4242                 \r
4243             while(n){\r
4244                 var nx = n.nextSibling;\r
4245             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){\r
4246                 dom.removeChild(n);\r
4247             }else{\r
4248                 n.nodeIndex = ++ni;\r
4249             }\r
4250                 n = nx;\r
4251             }\r
4252         Ext.Element.data(dom, 'isCleaned', true);\r
4253             return me;\r
4254         },\r
4255     \r
4256     /**\r
4257      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object\r
4258      * parameter as {@link Ext.Updater#update}\r
4259      * @return {Ext.Element} this\r
4260      */\r
4261     load : function(){\r
4262         var um = this.getUpdater();\r
4263         um.update.apply(um, arguments);\r
4264         return this;\r
4265     },\r
4266 \r
4267     /**\r
4268     * Gets this element's {@link Ext.Updater Updater}\r
4269     * @return {Ext.Updater} The Updater\r
4270     */\r
4271     getUpdater : function(){\r
4272         return this.updateManager || (this.updateManager = new Ext.Updater(this));\r
4273     },\r
4274     \r
4275         /**\r
4276     * Update the innerHTML of this element, optionally searching for and processing scripts\r
4277     * @param {String} html The new HTML\r
4278     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)\r
4279     * @param {Function} callback (optional) For async script loading you can be notified when the update completes\r
4280     * @return {Ext.Element} this\r
4281      */\r
4282     update : function(html, loadScripts, callback){\r
4283         html = html || "";\r
4284             \r
4285         if(loadScripts !== true){\r
4286             this.dom.innerHTML = html;\r
4287             if(Ext.isFunction(callback)){\r
4288                 callback();\r
4289             }\r
4290             return this;\r
4291         }\r
4292         \r
4293         var id = Ext.id(),\r
4294                 dom = this.dom;\r
4295 \r
4296         html += '<span id="' + id + '"></span>';\r
4297 \r
4298         Ext.lib.Event.onAvailable(id, function(){\r
4299             var DOC = document,\r
4300                 hd = DOC.getElementsByTagName("head")[0],\r
4301                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
4302                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,\r
4303                 typeRe = /\stype=([\'\"])(.*?)\1/i,\r
4304                 match,\r
4305                 attrs,\r
4306                 srcMatch,\r
4307                 typeMatch,\r
4308                 el,\r
4309                 s;\r
4310 \r
4311             while((match = re.exec(html))){\r
4312                 attrs = match[1];\r
4313                 srcMatch = attrs ? attrs.match(srcRe) : false;\r
4314                 if(srcMatch && srcMatch[2]){\r
4315                    s = DOC.createElement("script");\r
4316                    s.src = srcMatch[2];\r
4317                    typeMatch = attrs.match(typeRe);\r
4318                    if(typeMatch && typeMatch[2]){\r
4319                        s.type = typeMatch[2];\r
4320                    }\r
4321                    hd.appendChild(s);\r
4322                 }else if(match[2] && match[2].length > 0){\r
4323                     if(window.execScript) {\r
4324                        window.execScript(match[2]);\r
4325                     } else {\r
4326                        window.eval(match[2]);\r
4327                     }\r
4328                 }\r
4329             }\r
4330             el = DOC.getElementById(id);\r
4331             if(el){Ext.removeNode(el);}\r
4332             if(Ext.isFunction(callback)){\r
4333                 callback();\r
4334             }\r
4335         });\r
4336         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");\r
4337         return this;\r
4338     },\r
4339     \r
4340     /**\r
4341      * Creates a proxy element of this element\r
4342      * @param {String/Object} config The class name of the proxy element or a DomHelper config object\r
4343      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)\r
4344      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)\r
4345      * @return {Ext.Element} The new proxy element\r
4346      */\r
4347     createProxy : function(config, renderTo, matchBox){\r
4348         config = Ext.isObject(config) ? config : {tag : "div", cls: config};\r
4349 \r
4350         var me = this,\r
4351                 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :\r
4352                                                    Ext.DomHelper.insertBefore(me.dom, config, true);        \r
4353         \r
4354         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded\r
4355            proxy.setBox(me.getBox());\r
4356         }\r
4357         return proxy;\r
4358     }\r
4359 });\r
4360 \r
4361 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;\r
4362 \r
4363 // private\r
4364 Ext.Element.uncache = function(el){\r
4365     for(var i = 0, a = arguments, len = a.length; i < len; i++) {\r
4366         if(a[i]){\r
4367             delete Ext.Element.cache[a[i].id || a[i]];\r
4368         }\r
4369     }\r
4370 };/**\r
4371  * @class Ext.Element\r
4372  */\r
4373 Ext.Element.addMethods({\r
4374     /**\r
4375      * Gets the x,y coordinates specified by the anchor position on the element.\r
4376      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}\r
4377      * for details on supported anchor positions.\r
4378      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead\r
4379      * of page coordinates\r
4380      * @param {Object} size (optional) An object containing the size to use for calculating anchor position\r
4381      * {width: (target width), height: (target height)} (defaults to the element's current size)\r
4382      * @return {Array} [x, y] An array containing the element's x and y coordinates\r
4383      */\r
4384     getAnchorXY : function(anchor, local, s){\r
4385         //Passing a different size is useful for pre-calculating anchors,\r
4386         //especially for anchored animations that change the el size.\r
4387                 anchor = (anchor || "tl").toLowerCase();\r
4388         s = s || {};\r
4389         \r
4390         var me = this,        \r
4391                 vp = me.dom == document.body || me.dom == document,\r
4392                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),\r
4393                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              \r
4394                 xy,             \r
4395                 r = Math.round,\r
4396                 o = me.getXY(),\r
4397                 scroll = me.getScroll(),\r
4398                 extraX = vp ? scroll.left : !local ? o[0] : 0,\r
4399                 extraY = vp ? scroll.top : !local ? o[1] : 0,\r
4400                 hash = {\r
4401                         c  : [r(w * 0.5), r(h * 0.5)],\r
4402                         t  : [r(w * 0.5), 0],\r
4403                         l  : [0, r(h * 0.5)],\r
4404                         r  : [w, r(h * 0.5)],\r
4405                         b  : [r(w * 0.5), h],\r
4406                         tl : [0, 0],    \r
4407                         bl : [0, h],\r
4408                         br : [w, h],\r
4409                         tr : [w, 0]\r
4410                 };\r
4411         \r
4412         xy = hash[anchor];      \r
4413         return [xy[0] + extraX, xy[1] + extraY]; \r
4414     },\r
4415 \r
4416     /**\r
4417      * Anchors an element to another element and realigns it when the window is resized.\r
4418      * @param {Mixed} element The element to align to.\r
4419      * @param {String} position The position to align to.\r
4420      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4421      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object\r
4422      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter\r
4423      * is a number, it is used as the buffer delay (defaults to 50ms).\r
4424      * @param {Function} callback The function to call after the animation finishes\r
4425      * @return {Ext.Element} this\r
4426      */\r
4427     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        \r
4428             var me = this,\r
4429             dom = me.dom;\r
4430             \r
4431             function action(){\r
4432             Ext.fly(dom).alignTo(el, alignment, offsets, animate);\r
4433             Ext.callback(callback, Ext.fly(dom));\r
4434         }\r
4435         \r
4436         Ext.EventManager.onWindowResize(action, me);\r
4437         \r
4438         if(!Ext.isEmpty(monitorScroll)){\r
4439             Ext.EventManager.on(window, 'scroll', action, me,\r
4440                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});\r
4441         }\r
4442         action.call(me); // align immediately\r
4443         return me;\r
4444     },\r
4445 \r
4446     /**\r
4447      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the\r
4448      * supported position values.\r
4449      * @param {Mixed} element The element to align to.\r
4450      * @param {String} position The position to align to.\r
4451      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4452      * @return {Array} [x, y]\r
4453      */\r
4454     getAlignToXY : function(el, p, o){      \r
4455         el = Ext.get(el);\r
4456         \r
4457         if(!el || !el.dom){\r
4458             throw "Element.alignToXY with an element that doesn't exist";\r
4459         }\r
4460         \r
4461         o = o || [0,0];\r
4462         p = (p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       \r
4463                 \r
4464         var me = this,\r
4465                 d = me.dom,\r
4466                 a1,\r
4467                 a2,\r
4468                 x,\r
4469                 y,\r
4470                 //constrain the aligned el to viewport if necessary\r
4471                 w,\r
4472                 h,\r
4473                 r,\r
4474                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie\r
4475                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie\r
4476                 p1y,\r
4477                 p1x,            \r
4478                 p2y,\r
4479                 p2x,\r
4480                 swapY,\r
4481                 swapX,\r
4482                 doc = document,\r
4483                 docElement = doc.documentElement,\r
4484                 docBody = doc.body,\r
4485                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,\r
4486                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,\r
4487                 c = false, //constrain to viewport\r
4488                 p1 = "", \r
4489                 p2 = "",\r
4490                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
4491         \r
4492         if(!m){\r
4493            throw "Element.alignTo with an invalid alignment " + p;\r
4494         }\r
4495         \r
4496         p1 = m[1]; \r
4497         p2 = m[2]; \r
4498         c = !!m[3];\r
4499 \r
4500         //Subtract the aligned el's internal xy from the target's offset xy\r
4501         //plus custom offset to get the aligned el's new offset xy\r
4502         a1 = me.getAnchorXY(p1, true);\r
4503         a2 = el.getAnchorXY(p2, false);\r
4504 \r
4505         x = a2[0] - a1[0] + o[0];\r
4506         y = a2[1] - a1[1] + o[1];\r
4507 \r
4508         if(c){    \r
4509                w = me.getWidth();\r
4510            h = me.getHeight();\r
4511            r = el.getRegion();       \r
4512            //If we are at a viewport boundary and the aligned el is anchored on a target border that is\r
4513            //perpendicular to the vp border, allow the aligned el to slide on that border,\r
4514            //otherwise swap the aligned el to the opposite border of the target.\r
4515            p1y = p1.charAt(0);\r
4516            p1x = p1.charAt(p1.length-1);\r
4517            p2y = p2.charAt(0);\r
4518            p2x = p2.charAt(p2.length-1);\r
4519            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));\r
4520            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          \r
4521            \r
4522 \r
4523            if (x + w > dw + scrollX) {\r
4524                 x = swapX ? r.left-w : dw+scrollX-w;\r
4525            }\r
4526            if (x < scrollX) {\r
4527                x = swapX ? r.right : scrollX;\r
4528            }\r
4529            if (y + h > dh + scrollY) {\r
4530                 y = swapY ? r.top-h : dh+scrollY-h;\r
4531             }\r
4532            if (y < scrollY){\r
4533                y = swapY ? r.bottom : scrollY;\r
4534            }\r
4535         }\r
4536         return [x,y];\r
4537     },\r
4538 \r
4539     /**\r
4540      * Aligns this element with another element relative to the specified anchor points. If the other element is the\r
4541      * document it aligns it to the viewport.\r
4542      * The position parameter is optional, and can be specified in any one of the following formats:\r
4543      * <ul>\r
4544      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>\r
4545      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.\r
4546      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been\r
4547      *       deprecated in favor of the newer two anchor syntax below</i>.</li>\r
4548      *   <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\r
4549      *       element's anchor point, and the second value is used as the target's anchor point.</li>\r
4550      * </ul>\r
4551      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of\r
4552      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to\r
4553      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than\r
4554      * that specified in order to enforce the viewport constraints.\r
4555      * Following are all of the supported anchor positions:\r
4556 <pre>\r
4557 Value  Description\r
4558 -----  -----------------------------\r
4559 tl     The top left corner (default)\r
4560 t      The center of the top edge\r
4561 tr     The top right corner\r
4562 l      The center of the left edge\r
4563 c      In the center of the element\r
4564 r      The center of the right edge\r
4565 bl     The bottom left corner\r
4566 b      The center of the bottom edge\r
4567 br     The bottom right corner\r
4568 </pre>\r
4569 Example Usage:\r
4570 <pre><code>\r
4571 // align el to other-el using the default positioning ("tl-bl", non-constrained)\r
4572 el.alignTo("other-el");\r
4573 \r
4574 // align the top left corner of el with the top right corner of other-el (constrained to viewport)\r
4575 el.alignTo("other-el", "tr?");\r
4576 \r
4577 // align the bottom right corner of el with the center left edge of other-el\r
4578 el.alignTo("other-el", "br-l?");\r
4579 \r
4580 // align the center of el with the bottom left corner of other-el and\r
4581 // adjust the x position by -6 pixels (and the y position by 0)\r
4582 el.alignTo("other-el", "c-bl", [-6, 0]);\r
4583 </code></pre>\r
4584      * @param {Mixed} element The element to align to.\r
4585      * @param {String} position The position to align to.\r
4586      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4587      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
4588      * @return {Ext.Element} this\r
4589      */\r
4590     alignTo : function(element, position, offsets, animate){\r
4591             var me = this;\r
4592         return me.setXY(me.getAlignToXY(element, position, offsets),\r
4593                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);\r
4594     },\r
4595     \r
4596     // private ==>  used outside of core\r
4597     adjustForConstraints : function(xy, parent, offsets){\r
4598         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;\r
4599     },\r
4600 \r
4601     // private ==>  used outside of core\r
4602     getConstrainToXY : function(el, local, offsets, proposedXY){   \r
4603             var os = {top:0, left:0, bottom:0, right: 0};\r
4604 \r
4605         return function(el, local, offsets, proposedXY){\r
4606             el = Ext.get(el);\r
4607             offsets = offsets ? Ext.applyIf(offsets, os) : os;\r
4608 \r
4609             var vw, vh, vx = 0, vy = 0;\r
4610             if(el.dom == document.body || el.dom == document){\r
4611                 vw =Ext.lib.Dom.getViewWidth();\r
4612                 vh = Ext.lib.Dom.getViewHeight();\r
4613             }else{\r
4614                 vw = el.dom.clientWidth;\r
4615                 vh = el.dom.clientHeight;\r
4616                 if(!local){\r
4617                     var vxy = el.getXY();\r
4618                     vx = vxy[0];\r
4619                     vy = vxy[1];\r
4620                 }\r
4621             }\r
4622 \r
4623             var s = el.getScroll();\r
4624 \r
4625             vx += offsets.left + s.left;\r
4626             vy += offsets.top + s.top;\r
4627 \r
4628             vw -= offsets.right;\r
4629             vh -= offsets.bottom;\r
4630 \r
4631             var vr = vx+vw;\r
4632             var vb = vy+vh;\r
4633 \r
4634             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);\r
4635             var x = xy[0], y = xy[1];\r
4636             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;\r
4637 \r
4638             // only move it if it needs it\r
4639             var moved = false;\r
4640 \r
4641             // first validate right/bottom\r
4642             if((x + w) > vr){\r
4643                 x = vr - w;\r
4644                 moved = true;\r
4645             }\r
4646             if((y + h) > vb){\r
4647                 y = vb - h;\r
4648                 moved = true;\r
4649             }\r
4650             // then make sure top/left isn't negative\r
4651             if(x < vx){\r
4652                 x = vx;\r
4653                 moved = true;\r
4654             }\r
4655             if(y < vy){\r
4656                 y = vy;\r
4657                 moved = true;\r
4658             }\r
4659             return moved ? [x, y] : false;\r
4660         };\r
4661     }(),\r
4662             \r
4663             \r
4664                 \r
4665 //         el = Ext.get(el);\r
4666 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});\r
4667 \r
4668 //         var  me = this,\r
4669 //              doc = document,\r
4670 //              s = el.getScroll(),\r
4671 //              vxy = el.getXY(),\r
4672 //              vx = offsets.left + s.left, \r
4673 //              vy = offsets.top + s.top,               \r
4674 //              vw = -offsets.right, \r
4675 //              vh = -offsets.bottom, \r
4676 //              vr,\r
4677 //              vb,\r
4678 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),\r
4679 //              x = xy[0],\r
4680 //              y = xy[1],\r
4681 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,\r
4682 //              moved = false; // only move it if it needs it\r
4683 //       \r
4684 //              \r
4685 //         if(el.dom == doc.body || el.dom == doc){\r
4686 //             vw += Ext.lib.Dom.getViewWidth();\r
4687 //             vh += Ext.lib.Dom.getViewHeight();\r
4688 //         }else{\r
4689 //             vw += el.dom.clientWidth;\r
4690 //             vh += el.dom.clientHeight;\r
4691 //             if(!local){                    \r
4692 //                 vx += vxy[0];\r
4693 //                 vy += vxy[1];\r
4694 //             }\r
4695 //         }\r
4696 \r
4697 //         // first validate right/bottom\r
4698 //         if(x + w > vx + vw){\r
4699 //             x = vx + vw - w;\r
4700 //             moved = true;\r
4701 //         }\r
4702 //         if(y + h > vy + vh){\r
4703 //             y = vy + vh - h;\r
4704 //             moved = true;\r
4705 //         }\r
4706 //         // then make sure top/left isn't negative\r
4707 //         if(x < vx){\r
4708 //             x = vx;\r
4709 //             moved = true;\r
4710 //         }\r
4711 //         if(y < vy){\r
4712 //             y = vy;\r
4713 //             moved = true;\r
4714 //         }\r
4715 //         return moved ? [x, y] : false;\r
4716 //    },\r
4717     \r
4718     /**\r
4719     * Calculates the x, y to center this element on the screen\r
4720     * @return {Array} The x, y values [x, y]\r
4721     */\r
4722     getCenterXY : function(){\r
4723         return this.getAlignToXY(document, 'c-c');\r
4724     },\r
4725 \r
4726     /**\r
4727     * Centers the Element in either the viewport, or another Element.\r
4728     * @param {Mixed} centerIn (optional) The element in which to center the element.\r
4729     */\r
4730     center : function(centerIn){\r
4731         return this.alignTo(centerIn || document, 'c-c');        \r
4732     }    \r
4733 });\r
4734 /**\r
4735  * @class Ext.Element\r
4736  */\r
4737 Ext.Element.addMethods(function(){\r
4738         var PARENTNODE = 'parentNode',\r
4739                 NEXTSIBLING = 'nextSibling',\r
4740                 PREVIOUSSIBLING = 'previousSibling',\r
4741                 DQ = Ext.DomQuery,\r
4742                 GET = Ext.get;\r
4743         \r
4744         return {\r
4745                 /**\r
4746              * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
4747              * @param {String} selector The simple selector to test\r
4748              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)\r
4749              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
4750              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
4751              */\r
4752             findParent : function(simpleSelector, maxDepth, returnEl){\r
4753                 var p = this.dom,\r
4754                         b = document.body, \r
4755                         depth = 0,                      \r
4756                         stopEl;         \r
4757             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {\r
4758                 return null;\r
4759             }\r
4760                 maxDepth = maxDepth || 50;\r
4761                 if (isNaN(maxDepth)) {\r
4762                     stopEl = Ext.getDom(maxDepth);\r
4763                     maxDepth = Number.MAX_VALUE;\r
4764                 }\r
4765                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){\r
4766                     if(DQ.is(p, simpleSelector)){\r
4767                         return returnEl ? GET(p) : p;\r
4768                     }\r
4769                     depth++;\r
4770                     p = p.parentNode;\r
4771                 }\r
4772                 return null;\r
4773             },\r
4774         \r
4775             /**\r
4776              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
4777              * @param {String} selector The simple selector to test\r
4778              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
4779                     search as a number or element (defaults to 10 || document.body)\r
4780              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
4781              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
4782              */\r
4783             findParentNode : function(simpleSelector, maxDepth, returnEl){\r
4784                 var p = Ext.fly(this.dom.parentNode, '_internal');\r
4785                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;\r
4786             },\r
4787         \r
4788             /**\r
4789              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).\r
4790              * This is a shortcut for findParentNode() that always returns an Ext.Element.\r
4791              * @param {String} selector The simple selector to test\r
4792              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
4793                     search as a number or element (defaults to 10 || document.body)\r
4794              * @return {Ext.Element} The matching DOM node (or null if no match was found)\r
4795              */\r
4796             up : function(simpleSelector, maxDepth){\r
4797                 return this.findParentNode(simpleSelector, maxDepth, true);\r
4798             },\r
4799         \r
4800             /**\r
4801              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
4802              * @param {String} selector The CSS selector\r
4803              * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)\r
4804              * @return {CompositeElement/CompositeElementLite} The composite element\r
4805              */\r
4806             select : function(selector, unique){\r
4807                 return Ext.Element.select(selector, unique, this.dom);\r
4808             },\r
4809         \r
4810             /**\r
4811              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).\r
4812              * @param {String} selector The CSS selector\r
4813              * @return {Array} An array of the matched nodes\r
4814              */\r
4815             query : function(selector, unique){\r
4816                 return DQ.select(selector, this.dom);\r
4817             },\r
4818         \r
4819             /**\r
4820              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).\r
4821              * @param {String} selector The CSS selector\r
4822              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
4823              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
4824              */\r
4825             child : function(selector, returnDom){\r
4826                 var n = DQ.selectNode(selector, this.dom);\r
4827                 return returnDom ? n : GET(n);\r
4828             },\r
4829         \r
4830             /**\r
4831              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).\r
4832              * @param {String} selector The CSS selector\r
4833              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
4834              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
4835              */\r
4836             down : function(selector, returnDom){\r
4837                 var n = DQ.selectNode(" > " + selector, this.dom);\r
4838                 return returnDom ? n : GET(n);\r
4839             },\r
4840         \r
4841                  /**\r
4842              * Gets the parent node for this element, optionally chaining up trying to match a selector\r
4843              * @param {String} selector (optional) Find a parent node that matches the passed simple selector\r
4844              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
4845              * @return {Ext.Element/HTMLElement} The parent node or null\r
4846                  */\r
4847             parent : function(selector, returnDom){\r
4848                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);\r
4849             },\r
4850         \r
4851              /**\r
4852              * Gets the next sibling, skipping text nodes\r
4853              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
4854              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
4855              * @return {Ext.Element/HTMLElement} The next sibling or null\r
4856                  */\r
4857             next : function(selector, returnDom){\r
4858                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);\r
4859             },\r
4860         \r
4861             /**\r
4862              * Gets the previous sibling, skipping text nodes\r
4863              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
4864              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
4865              * @return {Ext.Element/HTMLElement} The previous sibling or null\r
4866                  */\r
4867             prev : function(selector, returnDom){\r
4868                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);\r
4869             },\r
4870         \r
4871         \r
4872             /**\r
4873              * Gets the first child, skipping text nodes\r
4874              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
4875              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
4876              * @return {Ext.Element/HTMLElement} The first child or null\r
4877                  */\r
4878             first : function(selector, returnDom){\r
4879                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);\r
4880             },\r
4881         \r
4882             /**\r
4883              * Gets the last child, skipping text nodes\r
4884              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
4885              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
4886              * @return {Ext.Element/HTMLElement} The last child or null\r
4887                  */\r
4888             last : function(selector, returnDom){\r
4889                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);\r
4890             },\r
4891             \r
4892             matchNode : function(dir, start, selector, returnDom){\r
4893                 var n = this.dom[start];\r
4894                 while(n){\r
4895                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){\r
4896                         return !returnDom ? GET(n) : n;\r
4897                     }\r
4898                     n = n[dir];\r
4899                 }\r
4900                 return null;\r
4901             }   \r
4902     }\r
4903 }());/**\r
4904  * @class Ext.Element\r
4905  */\r
4906 Ext.Element.addMethods(\r
4907 function() {\r
4908         var GETDOM = Ext.getDom,\r
4909                 GET = Ext.get,\r
4910                 DH = Ext.DomHelper,\r
4911         isEl = function(el){\r
4912             return  (el.nodeType || el.dom || typeof el == 'string');  \r
4913         };\r
4914         \r
4915         return {\r
4916             /**\r
4917              * Appends the passed element(s) to this element\r
4918              * @param {String/HTMLElement/Array/Element/CompositeElement} el\r
4919              * @return {Ext.Element} this\r
4920              */\r
4921             appendChild: function(el){        \r
4922                 return GET(el).appendTo(this);        \r
4923             },\r
4924         \r
4925             /**\r
4926              * Appends this element to the passed element\r
4927              * @param {Mixed} el The new parent element\r
4928              * @return {Ext.Element} this\r
4929              */\r
4930             appendTo: function(el){        \r
4931                 GETDOM(el).appendChild(this.dom);        \r
4932                 return this;\r
4933             },\r
4934         \r
4935             /**\r
4936              * Inserts this element before the passed element in the DOM\r
4937              * @param {Mixed} el The element before which this element will be inserted\r
4938              * @return {Ext.Element} this\r
4939              */\r
4940             insertBefore: function(el){                   \r
4941                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);\r
4942                 return this;\r
4943             },\r
4944         \r
4945             /**\r
4946              * Inserts this element after the passed element in the DOM\r
4947              * @param {Mixed} el The element to insert after\r
4948              * @return {Ext.Element} this\r
4949              */\r
4950             insertAfter: function(el){\r
4951                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);\r
4952                 return this;\r
4953             },\r
4954         \r
4955             /**\r
4956              * Inserts (or creates) an element (or DomHelper config) as the first child of this element\r
4957              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert\r
4958              * @return {Ext.Element} The new child\r
4959              */\r
4960             insertFirst: function(el, returnDom){\r
4961             el = el || {};\r
4962             if(isEl(el)){ // element\r
4963                 el = GETDOM(el);\r
4964                 this.dom.insertBefore(el, this.dom.firstChild);\r
4965                 return !returnDom ? GET(el) : el;\r
4966             }else{ // dh config\r
4967                 return this.createChild(el, this.dom.firstChild, returnDom);\r
4968             }\r
4969     },\r
4970         \r
4971             /**\r
4972              * Replaces the passed element with this element\r
4973              * @param {Mixed} el The element to replace\r
4974              * @return {Ext.Element} this\r
4975              */\r
4976             replace: function(el){\r
4977                 el = GET(el);\r
4978                 this.insertBefore(el);\r
4979                 el.remove();\r
4980                 return this;\r
4981             },\r
4982         \r
4983             /**\r
4984              * Replaces this element with the passed element\r
4985              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create\r
4986              * @return {Ext.Element} this\r
4987              */\r
4988             replaceWith: function(el){\r
4989                     var me = this,\r
4990                         Element = Ext.Element;\r
4991             if(isEl(el)){\r
4992                 el = GETDOM(el);\r
4993                 me.dom.parentNode.insertBefore(el, me.dom);\r
4994             }else{\r
4995                 el = DH.insertBefore(me.dom, el);\r
4996             }\r
4997                 \r
4998                 delete Element.cache[me.id];\r
4999                 Ext.removeNode(me.dom);      \r
5000                 me.id = Ext.id(me.dom = el);\r
5001                 return Element.cache[me.id] = me;        \r
5002             },\r
5003             \r
5004                 /**\r
5005                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.\r
5006                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be\r
5007                  * automatically generated with the specified attributes.\r
5008                  * @param {HTMLElement} insertBefore (optional) a child element of this element\r
5009                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element\r
5010                  * @return {Ext.Element} The new child element\r
5011                  */\r
5012                 createChild: function(config, insertBefore, returnDom){\r
5013                     config = config || {tag:'div'};\r
5014                     return insertBefore ? \r
5015                            DH.insertBefore(insertBefore, config, returnDom !== true) :  \r
5016                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);\r
5017                 },\r
5018                 \r
5019                 /**\r
5020                  * Creates and wraps this element with another element\r
5021                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div\r
5022                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5023                  * @return {HTMLElement/Element} The newly created wrapper element\r
5024                  */\r
5025                 wrap: function(config, returnDom){        \r
5026                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);\r
5027                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);\r
5028                     return newEl;\r
5029                 },\r
5030                 \r
5031                 /**\r
5032                  * Inserts an html fragment into this element\r
5033                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
5034                  * @param {String} html The HTML fragment\r
5035                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)\r
5036                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)\r
5037                  */\r
5038                 insertHtml : function(where, html, returnEl){\r
5039                     var el = DH.insertHtml(where, this.dom, html);\r
5040                     return returnEl ? Ext.get(el) : el;\r
5041                 }\r
5042         }\r
5043 }());/**\r
5044  * @class Ext.Element\r
5045  */\r
5046 Ext.apply(Ext.Element.prototype, function() {\r
5047         var GETDOM = Ext.getDom,\r
5048                 GET = Ext.get,\r
5049                 DH = Ext.DomHelper;\r
5050         \r
5051         return {        \r
5052                 /**\r
5053              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element\r
5054              * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.\r
5055              * @param {String} where (optional) 'before' or 'after' defaults to before\r
5056              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5057              * @return {Ext.Element} the inserted Element\r
5058              */\r
5059             insertSibling: function(el, where, returnDom){\r
5060                 var me = this,\r
5061                         rt;\r
5062                         \r
5063                 if(Ext.isArray(el)){            \r
5064                     Ext.each(el, function(e) {\r
5065                             rt = me.insertSibling(e, where, returnDom);\r
5066                     });\r
5067                     return rt;\r
5068                 }\r
5069                         \r
5070                 where = (where || 'before').toLowerCase();\r
5071                 el = el || {};\r
5072                 \r
5073             if(el.nodeType || el.dom){\r
5074                 rt = me.dom.parentNode.insertBefore(GETDOM(el), where == 'before' ? me.dom : me.dom.nextSibling);\r
5075                 if (!returnDom) {\r
5076                     rt = GET(rt);\r
5077                 }\r
5078             }else{\r
5079                 if (where == 'after' && !me.dom.nextSibling) {\r
5080                     rt = DH.append(me.dom.parentNode, el, !returnDom);\r
5081                 } else {                    \r
5082                     rt = DH[where == 'after' ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);\r
5083                 }\r
5084             }\r
5085                 return rt;\r
5086             }\r
5087     };\r
5088 }());/**\r
5089  * @class Ext.Element\r
5090  */\r
5091 Ext.Element.addMethods(function(){  \r
5092     // local style camelizing for speed\r
5093     var propCache = {},\r
5094         camelRe = /(-[a-z])/gi,\r
5095         classReCache = {},\r
5096         view = document.defaultView,\r
5097         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',\r
5098         opacityRe = /alpha\(opacity=(.*)\)/i,\r
5099         trimRe = /^\s+|\s+$/g,\r
5100         EL = Ext.Element,   \r
5101         PADDING = "padding",\r
5102         MARGIN = "margin",\r
5103         BORDER = "border",\r
5104         LEFT = "-left",\r
5105         RIGHT = "-right",\r
5106         TOP = "-top",\r
5107         BOTTOM = "-bottom",\r
5108         WIDTH = "-width",    \r
5109         MATH = Math,\r
5110         HIDDEN = 'hidden',\r
5111         ISCLIPPED = 'isClipped',\r
5112         OVERFLOW = 'overflow',\r
5113         OVERFLOWX = 'overflow-x',\r
5114         OVERFLOWY = 'overflow-y',\r
5115         ORIGINALCLIP = 'originalClip',\r
5116         // special markup used throughout Ext when box wrapping elements    \r
5117         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},\r
5118         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},\r
5119         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},\r
5120         data = Ext.Element.data;\r
5121         \r
5122     \r
5123     // private  \r
5124     function camelFn(m, a) {\r
5125         return a.charAt(1).toUpperCase();\r
5126     }\r
5127     \r
5128     // private (needs to be called => addStyles.call(this, sides, styles))\r
5129     function addStyles(sides, styles){\r
5130         var val = 0;    \r
5131         \r
5132         Ext.each(sides.match(/\w/g), function(s) {\r
5133             if (s = parseInt(this.getStyle(styles[s]), 10)) {\r
5134                 val += MATH.abs(s);      \r
5135             }\r
5136         },\r
5137         this);\r
5138         return val;\r
5139     }\r
5140 \r
5141     function chkCache(prop) {\r
5142         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));\r
5143 \r
5144     }\r
5145             \r
5146     return {    \r
5147         // private  ==> used by Fx  \r
5148         adjustWidth : function(width) {\r
5149             var me = this;\r
5150             var isNum = (typeof width == "number");\r
5151             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){\r
5152                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));\r
5153             }\r
5154             return (isNum && width < 0) ? 0 : width;\r
5155         },\r
5156         \r
5157         // private   ==> used by Fx \r
5158         adjustHeight : function(height) {\r
5159             var me = this;\r
5160             var isNum = (typeof height == "number");\r
5161             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){\r
5162                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));               \r
5163             }\r
5164             return (isNum && height < 0) ? 0 : height;\r
5165         },\r
5166     \r
5167     \r
5168         /**\r
5169          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.\r
5170          * @param {String/Array} className The CSS class to add, or an array of classes\r
5171          * @return {Ext.Element} this\r
5172          */\r
5173         addClass : function(className){\r
5174             var me = this;\r
5175             Ext.each(className, function(v) {\r
5176                 me.dom.className += (!me.hasClass(v) && v ? " " + v : "");  \r
5177             });\r
5178             return me;\r
5179         },\r
5180     \r
5181         /**\r
5182          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.\r
5183          * @param {String/Array} className The CSS class to add, or an array of classes\r
5184          * @return {Ext.Element} this\r
5185          */\r
5186         radioClass : function(className){\r
5187             Ext.each(this.dom.parentNode.childNodes, function(v) {\r
5188                 if(v.nodeType == 1) {\r
5189                     Ext.fly(v, '_internal').removeClass(className);          \r
5190                 }\r
5191             });\r
5192             return this.addClass(className);\r
5193         },\r
5194     \r
5195         /**\r
5196          * Removes one or more CSS classes from the element.\r
5197          * @param {String/Array} className The CSS class to remove, or an array of classes\r
5198          * @return {Ext.Element} this\r
5199          */\r
5200         removeClass : function(className){\r
5201             var me = this;\r
5202             if (me.dom.className) {\r
5203                 Ext.each(className, function(v) {               \r
5204                     me.dom.className = me.dom.className.replace(\r
5205                         classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), \r
5206                         " ");               \r
5207                 });    \r
5208             }\r
5209             return me;\r
5210         },\r
5211     \r
5212         /**\r
5213          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).\r
5214          * @param {String} className The CSS class to toggle\r
5215          * @return {Ext.Element} this\r
5216          */\r
5217         toggleClass : function(className){\r
5218             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);\r
5219         },\r
5220     \r
5221         /**\r
5222          * Checks if the specified CSS class exists on this element's DOM node.\r
5223          * @param {String} className The CSS class to check for\r
5224          * @return {Boolean} True if the class exists, else false\r
5225          */\r
5226         hasClass : function(className){\r
5227             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;\r
5228         },\r
5229     \r
5230         /**\r
5231          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.\r
5232          * @param {String} oldClassName The CSS class to replace\r
5233          * @param {String} newClassName The replacement CSS class\r
5234          * @return {Ext.Element} this\r
5235          */\r
5236         replaceClass : function(oldClassName, newClassName){\r
5237             return this.removeClass(oldClassName).addClass(newClassName);\r
5238         },\r
5239         \r
5240         isStyle : function(style, val) {\r
5241             return this.getStyle(style) == val;  \r
5242         },\r
5243     \r
5244         /**\r
5245          * Normalizes currentStyle and computedStyle.\r
5246          * @param {String} property The style property whose value is returned.\r
5247          * @return {String} The current value of the style property for this element.\r
5248          */\r
5249         getStyle : function(){         \r
5250             return view && view.getComputedStyle ?\r
5251                 function(prop){\r
5252                     var el = this.dom,\r
5253                         v,                  \r
5254                         cs;\r
5255                     if(el == document) return null;\r
5256                     prop = chkCache(prop);\r
5257                     return (v = el.style[prop]) ? v : \r
5258                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;\r
5259                 } :\r
5260                 function(prop){      \r
5261                     var el = this.dom, \r
5262                         m, \r
5263                         cs;     \r
5264                         \r
5265                     if(el == document) return null;      \r
5266                     if (prop == 'opacity') {\r
5267                         if (el.style.filter.match) {                       \r
5268                             if(m = el.style.filter.match(opacityRe)){\r
5269                                 var fv = parseFloat(m[1]);\r
5270                                 if(!isNaN(fv)){\r
5271                                     return fv ? fv / 100 : 0;\r
5272                                 }\r
5273                             }\r
5274                         }\r
5275                         return 1;\r
5276                     }\r
5277                     prop = chkCache(prop);  \r
5278                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);\r
5279                 };\r
5280         }(),\r
5281         \r
5282         /**\r
5283          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values\r
5284          * are convert to standard 6 digit hex color.\r
5285          * @param {String} attr The css attribute\r
5286          * @param {String} defaultValue The default value to use when a valid color isn't found\r
5287          * @param {String} prefix (optional) defaults to #. Use an empty string when working with\r
5288          * color anims.\r
5289          */\r
5290         getColor : function(attr, defaultValue, prefix){\r
5291             var v = this.getStyle(attr),\r
5292                 color = prefix || '#',\r
5293                 h;\r
5294                 \r
5295             if(!v || /transparent|inherit/.test(v)){\r
5296                 return defaultValue;\r
5297             }\r
5298             if(/^r/.test(v)){\r
5299                 Ext.each(v.slice(4, v.length -1).split(','), function(s){\r
5300                     h = parseInt(s, 10);\r
5301                     color += (h < 16 ? '0' : '') + h.toString(16); \r
5302                 });\r
5303             }else{\r
5304                 v = v.replace('#', '');\r
5305                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;\r
5306             }\r
5307             return(color.length > 5 ? color.toLowerCase() : defaultValue);\r
5308         },\r
5309     \r
5310         /**\r
5311          * Wrapper for setting style properties, also takes single object parameter of multiple styles.\r
5312          * @param {String/Object} property The style property to be set, or an object of multiple styles.\r
5313          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.\r
5314          * @return {Ext.Element} this\r
5315          */\r
5316         setStyle : function(prop, value){\r
5317             var tmp, \r
5318                 style,\r
5319                 camel;\r
5320             if (!Ext.isObject(prop)) {\r
5321                 tmp = {};\r
5322                 tmp[prop] = value;          \r
5323                 prop = tmp;\r
5324             }\r
5325             for (style in prop) {\r
5326                 value = prop[style];            \r
5327                 style == 'opacity' ? \r
5328                     this.setOpacity(value) : \r
5329                     this.dom.style[chkCache(style)] = value;\r
5330             }\r
5331             return this;\r
5332         },\r
5333         \r
5334         /**\r
5335          * Set the opacity of the element\r
5336          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc\r
5337          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for\r
5338          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)\r
5339          * @return {Ext.Element} this\r
5340          */\r
5341          setOpacity : function(opacity, animate){\r
5342             var me = this,\r
5343                 s = me.dom.style;\r
5344                 \r
5345             if(!animate || !me.anim){            \r
5346                 if(Ext.isIE){\r
5347                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '', \r
5348                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');\r
5349 \r
5350                     s.zoom = 1;\r
5351                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;\r
5352                 }else{\r
5353                     s.opacity = opacity;\r
5354                 }\r
5355             }else{\r
5356                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');\r
5357             }\r
5358             return me;\r
5359         },\r
5360         \r
5361         /**\r
5362          * Clears any opacity settings from this element. Required in some cases for IE.\r
5363          * @return {Ext.Element} this\r
5364          */\r
5365         clearOpacity : function(){\r
5366             var style = this.dom.style;\r
5367             if(Ext.isIE){\r
5368                 if(!Ext.isEmpty(style.filter)){\r
5369                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');\r
5370                 }\r
5371             }else{\r
5372                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';\r
5373             }\r
5374             return this;\r
5375         },\r
5376     \r
5377         /**\r
5378          * Returns the offset height of the element\r
5379          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding\r
5380          * @return {Number} The element's height\r
5381          */\r
5382         getHeight : function(contentHeight){\r
5383             var me = this,\r
5384                 dom = me.dom,\r
5385                 h = MATH.max(dom.offsetHeight, dom.clientHeight) || 0;\r
5386             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");\r
5387             return h < 0 ? 0 : h;\r
5388         },\r
5389     \r
5390         /**\r
5391          * Returns the offset width of the element\r
5392          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding\r
5393          * @return {Number} The element's width\r
5394          */\r
5395         getWidth : function(contentWidth){\r
5396             var me = this,\r
5397                 dom = me.dom,\r
5398                 w = MATH.max(dom.offsetWidth, dom.clientWidth) || 0;\r
5399             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");\r
5400             return w < 0 ? 0 : w;\r
5401         },\r
5402     \r
5403         /**\r
5404          * Set the width of this Element.\r
5405          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
5406          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
5407          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
5408          * </ul></div>\r
5409          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
5410          * @return {Ext.Element} this\r
5411          */\r
5412         setWidth : function(width, animate){\r
5413             var me = this;\r
5414             width = me.adjustWidth(width);\r
5415             !animate || !me.anim ? \r
5416                 me.dom.style.width = me.addUnits(width) :\r
5417                 me.anim({width : {to : width}}, me.preanim(arguments, 1));\r
5418             return me;\r
5419         },\r
5420     \r
5421         /**\r
5422          * Set the height of this Element.\r
5423          * <pre><code>\r
5424 // change the height to 200px and animate with default configuration\r
5425 Ext.fly('elementId').setHeight(200, true);\r
5426 \r
5427 // change the height to 150px and animate with a custom configuration\r
5428 Ext.fly('elId').setHeight(150, {\r
5429     duration : .5, // animation will have a duration of .5 seconds\r
5430     // will change the content to "finished"\r
5431     callback: function(){ this.{@link #update}("finished"); } \r
5432 });\r
5433          * </code></pre>\r
5434          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
5435          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>\r
5436          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
5437          * </ul></div>\r
5438          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
5439          * @return {Ext.Element} this\r
5440          */\r
5441          setHeight : function(height, animate){\r
5442             var me = this;\r
5443             height = me.adjustHeight(height);\r
5444             !animate || !me.anim ? \r
5445                 me.dom.style.height = me.addUnits(height) :\r
5446                 me.anim({height : {to : height}}, me.preanim(arguments, 1));\r
5447             return me;\r
5448         },\r
5449         \r
5450         /**\r
5451          * Gets the width of the border(s) for the specified side(s)\r
5452          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,\r
5453          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.\r
5454          * @return {Number} The width of the sides passed added together\r
5455          */\r
5456         getBorderWidth : function(side){\r
5457             return addStyles.call(this, side, borders);\r
5458         },\r
5459     \r
5460         /**\r
5461          * Gets the width of the padding(s) for the specified side(s)\r
5462          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,\r
5463          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.\r
5464          * @return {Number} The padding of the sides passed added together\r
5465          */\r
5466         getPadding : function(side){\r
5467             return addStyles.call(this, side, paddings);\r
5468         },\r
5469     \r
5470         /**\r
5471          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove\r
5472          * @return {Ext.Element} this\r
5473          */\r
5474         clip : function(){\r
5475             var me = this,\r
5476                 dom = me.dom;\r
5477                 \r
5478             if(!data(dom, ISCLIPPED)){\r
5479                 data(dom, ISCLIPPED, true);\r
5480                 data(dom, ORIGINALCLIP, {\r
5481                     o: me.getStyle(OVERFLOW),\r
5482                     x: me.getStyle(OVERFLOWX),\r
5483                     y: me.getStyle(OVERFLOWY)\r
5484                 });\r
5485                 me.setStyle(OVERFLOW, HIDDEN);\r
5486                 me.setStyle(OVERFLOWX, HIDDEN);\r
5487                 me.setStyle(OVERFLOWY, HIDDEN);\r
5488             }\r
5489             return me;\r
5490         },\r
5491     \r
5492         /**\r
5493          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called\r
5494          * @return {Ext.Element} this\r
5495          */\r
5496         unclip : function(){\r
5497             var me = this,\r
5498                 dom = me.dom;\r
5499                 \r
5500             if(data(dom, ISCLIPPED)){\r
5501                 data(dom, ISCLIPPED, false);\r
5502                 var o = data(dom, ORIGINALCLIP);\r
5503                 if(o.o){\r
5504                     me.setStyle(OVERFLOW, o.o);\r
5505                 }\r
5506                 if(o.x){\r
5507                     me.setStyle(OVERFLOWX, o.x);\r
5508                 }\r
5509                 if(o.y){\r
5510                     me.setStyle(OVERFLOWY, o.y);\r
5511                 }\r
5512             }\r
5513             return me;\r
5514         },\r
5515         \r
5516         addStyles : addStyles,\r
5517         margins : margins\r
5518     }\r
5519 }()         \r
5520 );/**\r
5521  * @class Ext.Element\r
5522  */\r
5523 \r
5524 // special markup used throughout Ext when box wrapping elements\r
5525 Ext.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';\r
5526 \r
5527 Ext.Element.addMethods(function(){\r
5528         var INTERNAL = "_internal";\r
5529         return {\r
5530             /**\r
5531              * More flexible version of {@link #setStyle} for setting style properties.\r
5532              * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
5533              * a function which returns such a specification.\r
5534              * @return {Ext.Element} this\r
5535              */\r
5536             applyStyles : function(style){\r
5537                 Ext.DomHelper.applyStyles(this.dom, style);\r
5538                 return this;\r
5539             },\r
5540 \r
5541                 /**\r
5542              * Returns an object with properties matching the styles requested.\r
5543              * For example, el.getStyles('color', 'font-size', 'width') might return\r
5544              * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
5545              * @param {String} style1 A style name\r
5546              * @param {String} style2 A style name\r
5547              * @param {String} etc.\r
5548              * @return {Object} The style object\r
5549              */\r
5550             getStyles : function(){\r
5551                     var ret = {};\r
5552                     Ext.each(arguments, function(v) {\r
5553                            ret[v] = this.getStyle(v);\r
5554                     },\r
5555                     this);\r
5556                     return ret;\r
5557             },\r
5558 \r
5559                 getStyleSize : function(){\r
5560                 var me = this,\r
5561                         w,\r
5562                         h,\r
5563                         d = this.dom,\r
5564                         s = d.style;\r
5565                 if(s.width && s.width != 'auto'){\r
5566                     w = parseInt(s.width, 10);\r
5567                     if(me.isBorderBox()){\r
5568                        w -= me.getFrameWidth('lr');\r
5569                     }\r
5570                 }\r
5571                 if(s.height && s.height != 'auto'){\r
5572                     h = parseInt(s.height, 10);\r
5573                     if(me.isBorderBox()){\r
5574                        h -= me.getFrameWidth('tb');\r
5575                     }\r
5576                 }\r
5577                 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};\r
5578             },\r
5579 \r
5580             // private  ==> used by ext full\r
5581                 setOverflow : function(v){\r
5582                         var dom = this.dom;\r
5583                 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
5584                         dom.style.overflow = 'hidden';\r
5585                         (function(){dom.style.overflow = 'auto';}).defer(1);\r
5586                 }else{\r
5587                         dom.style.overflow = v;\r
5588                 }\r
5589                 },\r
5590 \r
5591            /**\r
5592                 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as\r
5593                 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>\r
5594                 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},\r
5595                 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup\r
5596                 * is of this form:</p>\r
5597                 * <pre><code>\r
5598 Ext.Element.boxMarkup =\r
5599     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>\r
5600      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>\r
5601      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;\r
5602                 * </code></pre>\r
5603                 * <p>Example usage:</p>\r
5604                 * <pre><code>\r
5605 // Basic box wrap\r
5606 Ext.get("foo").boxWrap();\r
5607 \r
5608 // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
5609 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
5610 // for how to create a custom box wrap style.\r
5611 Ext.get("foo").boxWrap().addClass("x-box-blue");\r
5612                 * </code></pre>\r
5613                 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element\r
5614                 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on\r
5615                 * this name to make the overall effect work, so if you supply an alternate base class, make sure you\r
5616                 * also supply all of the necessary rules.\r
5617                 * @return {Ext.Element} this\r
5618                 */\r
5619             boxWrap : function(cls){\r
5620                 cls = cls || 'x-box';\r
5621                 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>"));        //String.format('<div class="{0}">'+Ext.Element.boxMarkup+'</div>', cls)));\r
5622                 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);\r
5623                 return el;\r
5624             },\r
5625 \r
5626         /**\r
5627          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.\r
5628          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
5629          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
5630          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
5631          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>\r
5632          * </ul></div>\r
5633          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
5634          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
5635          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
5636          * </ul></div>\r
5637          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
5638          * @return {Ext.Element} this\r
5639          */\r
5640             setSize : function(width, height, animate){\r
5641                         var me = this;\r
5642                         if(Ext.isObject(width)){ // in case of object from getSize()\r
5643                             height = width.height;\r
5644                             width = width.width;\r
5645                         }\r
5646                         width = me.adjustWidth(width);\r
5647                         height = me.adjustHeight(height);\r
5648                         if(!animate || !me.anim){\r
5649                             me.dom.style.width = me.addUnits(width);\r
5650                             me.dom.style.height = me.addUnits(height);\r
5651                         }else{\r
5652                             me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));\r
5653                         }\r
5654                         return me;\r
5655             },\r
5656 \r
5657             /**\r
5658              * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
5659              * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
5660              * if a height has not been set using CSS.\r
5661              * @return {Number}\r
5662              */\r
5663             getComputedHeight : function(){\r
5664                     var me = this,\r
5665                         h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);\r
5666                 if(!h){\r
5667                     h = parseInt(me.getStyle('height'), 10) || 0;\r
5668                     if(!me.isBorderBox()){\r
5669                         h += me.getFrameWidth('tb');\r
5670                     }\r
5671                 }\r
5672                 return h;\r
5673             },\r
5674 \r
5675             /**\r
5676              * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
5677              * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
5678              * if a width has not been set using CSS.\r
5679              * @return {Number}\r
5680              */\r
5681             getComputedWidth : function(){\r
5682                 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
5683                 if(!w){\r
5684                     w = parseInt(this.getStyle('width'), 10) || 0;\r
5685                     if(!this.isBorderBox()){\r
5686                         w += this.getFrameWidth('lr');\r
5687                     }\r
5688                 }\r
5689                 return w;\r
5690             },\r
5691 \r
5692             /**\r
5693              * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
5694              for more information about the sides.\r
5695              * @param {String} sides\r
5696              * @return {Number}\r
5697              */\r
5698             getFrameWidth : function(sides, onlyContentBox){\r
5699                 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
5700             },\r
5701 \r
5702             /**\r
5703              * Sets up event handlers to add and remove a css class when the mouse is over this element\r
5704              * @param {String} className\r
5705              * @return {Ext.Element} this\r
5706              */\r
5707             addClassOnOver : function(className){\r
5708                 this.hover(\r
5709                     function(){\r
5710                         Ext.fly(this, INTERNAL).addClass(className);\r
5711                     },\r
5712                     function(){\r
5713                         Ext.fly(this, INTERNAL).removeClass(className);\r
5714                     }\r
5715                 );\r
5716                 return this;\r
5717             },\r
5718 \r
5719             /**\r
5720              * Sets up event handlers to add and remove a css class when this element has the focus\r
5721              * @param {String} className\r
5722              * @return {Ext.Element} this\r
5723              */\r
5724             addClassOnFocus : function(className){\r
5725                     this.on("focus", function(){\r
5726                         Ext.fly(this, INTERNAL).addClass(className);\r
5727                     }, this.dom);\r
5728                     this.on("blur", function(){\r
5729                         Ext.fly(this, INTERNAL).removeClass(className);\r
5730                     }, this.dom);\r
5731                     return this;\r
5732             },\r
5733 \r
5734             /**\r
5735              * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)\r
5736              * @param {String} className\r
5737              * @return {Ext.Element} this\r
5738              */\r
5739             addClassOnClick : function(className){\r
5740                 var dom = this.dom;\r
5741                 this.on("mousedown", function(){\r
5742                     Ext.fly(dom, INTERNAL).addClass(className);\r
5743                     var d = Ext.getDoc(),\r
5744                         fn = function(){\r
5745                                 Ext.fly(dom, INTERNAL).removeClass(className);\r
5746                                 d.removeListener("mouseup", fn);\r
5747                             };\r
5748                     d.on("mouseup", fn);\r
5749                 });\r
5750                 return this;\r
5751             },\r
5752 \r
5753             /**\r
5754              * Returns the width and height of the viewport.\r
5755         * <pre><code>\r
5756         var vpSize = Ext.getBody().getViewSize();\r
5757 \r
5758         // all Windows created afterwards will have a default value of 90% height and 95% width\r
5759         Ext.Window.override({\r
5760             width: vpSize.width * 0.9,\r
5761             height: vpSize.height * 0.95\r
5762         });\r
5763         // To handle window resizing you would have to hook onto onWindowResize.\r
5764         </code></pre>\r
5765              * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}\r
5766              */\r
5767             getViewSize : function(){\r
5768                 var doc = document,\r
5769                         d = this.dom,\r
5770                         extdom = Ext.lib.Dom,\r
5771                         isDoc = (d == doc || d == doc.body);\r
5772                 return { width : (isDoc ? extdom.getViewWidth() : d.clientWidth),\r
5773                                  height : (isDoc ? extdom.getViewHeight() : d.clientHeight) };\r
5774             },\r
5775 \r
5776             /**\r
5777              * Returns the size of the element.\r
5778              * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
5779              * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
5780              */\r
5781             getSize : function(contentSize){\r
5782                 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
5783             },\r
5784 \r
5785             /**\r
5786              * Forces the browser to repaint this element\r
5787              * @return {Ext.Element} this\r
5788              */\r
5789             repaint : function(){\r
5790                 var dom = this.dom;\r
5791                 this.addClass("x-repaint");\r
5792                 setTimeout(function(){\r
5793                     Ext.fly(dom).removeClass("x-repaint");\r
5794                 }, 1);\r
5795                 return this;\r
5796             },\r
5797 \r
5798             /**\r
5799              * Disables text selection for this element (normalized across browsers)\r
5800              * @return {Ext.Element} this\r
5801              */\r
5802             unselectable : function(){\r
5803                 this.dom.unselectable = "on";\r
5804                 return this.swallowEvent("selectstart", true).\r
5805                                     applyStyles("-moz-user-select:none;-khtml-user-select:none;").\r
5806                                     addClass("x-unselectable");\r
5807             },\r
5808 \r
5809             /**\r
5810              * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
5811              * then it returns the calculated width of the sides (see getPadding)\r
5812              * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
5813              * @return {Object/Number}\r
5814              */\r
5815             getMargins : function(side){\r
5816                     var me = this,\r
5817                         key,\r
5818                         hash = {t:"top", l:"left", r:"right", b: "bottom"},\r
5819                         o = {};\r
5820 \r
5821                     if (!side) {\r
5822                         for (key in me.margins){\r
5823                                 o[hash[key]] = parseInt(me.getStyle(me.margins[key]), 10) || 0;\r
5824                 }\r
5825                         return o;\r
5826                 } else {\r
5827                     return me.addStyles.call(me, side, me.margins);\r
5828                 }\r
5829             }\r
5830     };\r
5831 }());/**\r
5832  * @class Ext.Element\r
5833  */\r
5834 (function(){\r
5835 var D = Ext.lib.Dom,\r
5836         LEFT = "left",\r
5837         RIGHT = "right",\r
5838         TOP = "top",\r
5839         BOTTOM = "bottom",\r
5840         POSITION = "position",\r
5841         STATIC = "static",\r
5842         RELATIVE = "relative",\r
5843         AUTO = "auto",\r
5844         ZINDEX = "z-index";\r
5845 \r
5846 function animTest(args, animate, i) {\r
5847         return this.preanim && !!animate ? this.preanim(args, i) : false        \r
5848 }\r
5849 \r
5850 Ext.Element.addMethods({\r
5851         /**\r
5852       * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5853       * @return {Number} The X position of the element\r
5854       */\r
5855     getX : function(){\r
5856         return D.getX(this.dom);\r
5857     },\r
5858 \r
5859     /**\r
5860       * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5861       * @return {Number} The Y position of the element\r
5862       */\r
5863     getY : function(){\r
5864         return D.getY(this.dom);\r
5865     },\r
5866 \r
5867     /**\r
5868       * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5869       * @return {Array} The XY position of the element\r
5870       */\r
5871     getXY : function(){\r
5872         return D.getXY(this.dom);\r
5873     },\r
5874 \r
5875     /**\r
5876       * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.\r
5877       * @param {Mixed} element The element to get the offsets from.\r
5878       * @return {Array} The XY page offsets (e.g. [100, -200])\r
5879       */\r
5880     getOffsetsTo : function(el){\r
5881         var o = this.getXY(),\r
5882                 e = Ext.fly(el, '_internal').getXY();\r
5883         return [o[0]-e[0],o[1]-e[1]];\r
5884     },\r
5885 \r
5886     /**\r
5887      * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5888      * @param {Number} The X position of the element\r
5889      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
5890      * @return {Ext.Element} this\r
5891      */\r
5892     setX : function(x, animate){            \r
5893             return this.setXY([x, this.getY()], animTest.call(this, arguments, animate, 1));\r
5894     },\r
5895 \r
5896     /**\r
5897      * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5898      * @param {Number} The Y position of the element\r
5899      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
5900      * @return {Ext.Element} this\r
5901      */\r
5902     setY : function(y, animate){            \r
5903             return this.setXY([this.getX(), y], animTest.call(this, arguments, animate, 1));\r
5904     },\r
5905 \r
5906     /**\r
5907      * Sets the element's left position directly using CSS style (instead of {@link #setX}).\r
5908      * @param {String} left The left CSS property value\r
5909      * @return {Ext.Element} this\r
5910      */\r
5911     setLeft : function(left){\r
5912         this.setStyle(LEFT, this.addUnits(left));\r
5913         return this;\r
5914     },\r
5915 \r
5916     /**\r
5917      * Sets the element's top position directly using CSS style (instead of {@link #setY}).\r
5918      * @param {String} top The top CSS property value\r
5919      * @return {Ext.Element} this\r
5920      */\r
5921     setTop : function(top){\r
5922         this.setStyle(TOP, this.addUnits(top));\r
5923         return this;\r
5924     },\r
5925 \r
5926     /**\r
5927      * Sets the element's CSS right style.\r
5928      * @param {String} right The right CSS property value\r
5929      * @return {Ext.Element} this\r
5930      */\r
5931     setRight : function(right){\r
5932         this.setStyle(RIGHT, this.addUnits(right));\r
5933         return this;\r
5934     },\r
5935 \r
5936     /**\r
5937      * Sets the element's CSS bottom style.\r
5938      * @param {String} bottom The bottom CSS property value\r
5939      * @return {Ext.Element} this\r
5940      */\r
5941     setBottom : function(bottom){\r
5942         this.setStyle(BOTTOM, this.addUnits(bottom));\r
5943         return this;\r
5944     },\r
5945 \r
5946     /**\r
5947      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
5948      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5949      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)\r
5950      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
5951      * @return {Ext.Element} this\r
5952      */\r
5953     setXY : function(pos, animate){\r
5954             var me = this;\r
5955         if(!animate || !me.anim){\r
5956             D.setXY(me.dom, pos);\r
5957         }else{\r
5958             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');\r
5959         }\r
5960         return me;\r
5961     },\r
5962 \r
5963     /**\r
5964      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
5965      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5966      * @param {Number} x X value for new position (coordinates are page-based)\r
5967      * @param {Number} y Y value for new position (coordinates are page-based)\r
5968      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
5969      * @return {Ext.Element} this\r
5970      */\r
5971     setLocation : function(x, y, animate){\r
5972         return this.setXY([x, y], animTest.call(this, arguments, animate, 2));\r
5973     },\r
5974 \r
5975     /**\r
5976      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
5977      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
5978      * @param {Number} x X value for new position (coordinates are page-based)\r
5979      * @param {Number} y Y value for new position (coordinates are page-based)\r
5980      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
5981      * @return {Ext.Element} this\r
5982      */\r
5983     moveTo : function(x, y, animate){\r
5984         return this.setXY([x, y], animTest.call(this, arguments, animate, 2));        \r
5985     },    \r
5986     \r
5987     /**\r
5988      * Gets the left X coordinate\r
5989      * @param {Boolean} local True to get the local css position instead of page coordinate\r
5990      * @return {Number}\r
5991      */\r
5992     getLeft : function(local){\r
5993             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;\r
5994     },\r
5995 \r
5996     /**\r
5997      * Gets the right X coordinate of the element (element X position + element width)\r
5998      * @param {Boolean} local True to get the local css position instead of page coordinate\r
5999      * @return {Number}\r
6000      */\r
6001     getRight : function(local){\r
6002             var me = this;\r
6003             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;\r
6004     },\r
6005 \r
6006     /**\r
6007      * Gets the top Y coordinate\r
6008      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6009      * @return {Number}\r
6010      */\r
6011     getTop : function(local) {\r
6012             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;\r
6013     },\r
6014 \r
6015     /**\r
6016      * Gets the bottom Y coordinate of the element (element Y position + element height)\r
6017      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6018      * @return {Number}\r
6019      */\r
6020     getBottom : function(local){\r
6021             var me = this;\r
6022             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;\r
6023     },\r
6024 \r
6025     /**\r
6026     * Initializes positioning on this element. If a desired position is not passed, it will make the\r
6027     * the element positioned relative IF it is not already positioned.\r
6028     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"\r
6029     * @param {Number} zIndex (optional) The zIndex to apply\r
6030     * @param {Number} x (optional) Set the page X position\r
6031     * @param {Number} y (optional) Set the page Y position\r
6032     */\r
6033     position : function(pos, zIndex, x, y){\r
6034             var me = this;\r
6035             \r
6036         if(!pos && me.isStyle(POSITION, STATIC)){           \r
6037             me.setStyle(POSITION, RELATIVE);           \r
6038         } else if(pos) {\r
6039             me.setStyle(POSITION, pos);\r
6040         }\r
6041         if(zIndex){\r
6042             me.setStyle(ZINDEX, zIndex);\r
6043         }\r
6044         if(x || y) me.setXY([x || false, y || false]);\r
6045     },\r
6046 \r
6047     /**\r
6048     * Clear positioning back to the default when the document was loaded\r
6049     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.\r
6050     * @return {Ext.Element} this\r
6051      */\r
6052     clearPositioning : function(value){\r
6053         value = value || '';\r
6054         this.setStyle({\r
6055             left : value,\r
6056             right : value,\r
6057             top : value,\r
6058             bottom : value,\r
6059             "z-index" : "",\r
6060             position : STATIC\r
6061         });\r
6062         return this;\r
6063     },\r
6064 \r
6065     /**\r
6066     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get\r
6067     * snapshot before performing an update and then restoring the element.\r
6068     * @return {Object}\r
6069     */\r
6070     getPositioning : function(){\r
6071         var l = this.getStyle(LEFT);\r
6072         var t = this.getStyle(TOP);\r
6073         return {\r
6074             "position" : this.getStyle(POSITION),\r
6075             "left" : l,\r
6076             "right" : l ? "" : this.getStyle(RIGHT),\r
6077             "top" : t,\r
6078             "bottom" : t ? "" : this.getStyle(BOTTOM),\r
6079             "z-index" : this.getStyle(ZINDEX)\r
6080         };\r
6081     },\r
6082     \r
6083     /**\r
6084     * Set positioning with an object returned by getPositioning().\r
6085     * @param {Object} posCfg\r
6086     * @return {Ext.Element} this\r
6087      */\r
6088     setPositioning : function(pc){\r
6089             var me = this,\r
6090                 style = me.dom.style;\r
6091                 \r
6092         me.setStyle(pc);\r
6093         \r
6094         if(pc.right == AUTO){\r
6095             style.right = "";\r
6096         }\r
6097         if(pc.bottom == AUTO){\r
6098             style.bottom = "";\r
6099         }\r
6100         \r
6101         return me;\r
6102     },    \r
6103         \r
6104     /**\r
6105      * Translates the passed page coordinates into left/top css values for this element\r
6106      * @param {Number/Array} x The page x or an array containing [x, y]\r
6107      * @param {Number} y (optional) The page y, required if x is not an array\r
6108      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}\r
6109      */\r
6110     translatePoints : function(x, y){                \r
6111             y = isNaN(x[1]) ? y : x[1];\r
6112         x = isNaN(x[0]) ? x : x[0];\r
6113         var me = this,\r
6114                 relative = me.isStyle(POSITION, RELATIVE),\r
6115                 o = me.getXY(),\r
6116                 l = parseInt(me.getStyle(LEFT), 10),\r
6117                 t = parseInt(me.getStyle(TOP), 10);\r
6118         \r
6119         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);\r
6120         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        \r
6121 \r
6122         return {left: (x - o[0] + l), top: (y - o[1] + t)}; \r
6123     },\r
6124     \r
6125     animTest : animTest\r
6126 });\r
6127 })();/**\r
6128  * @class Ext.Element\r
6129  */\r
6130 Ext.Element.addMethods({\r
6131     /**\r
6132      * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.\r
6133      * @param {Object} box The box to fill {x, y, width, height}\r
6134      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically\r
6135      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6136      * @return {Ext.Element} this\r
6137      */\r
6138     setBox : function(box, adjust, animate){\r
6139         var me = this,\r
6140                 w = box.width, \r
6141                 h = box.height;\r
6142         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){\r
6143            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));\r
6144            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));\r
6145         }\r
6146         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));\r
6147         return me;\r
6148     },\r
6149     \r
6150     /**\r
6151      * Return a box {x, y, width, height} that can be used to set another elements\r
6152      * size/location to match this element.\r
6153      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.\r
6154      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.\r
6155      * @return {Object} box An object in the format {x, y, width, height}\r
6156      */\r
6157         getBox : function(contentBox, local) {      \r
6158             var me = this,\r
6159                 xy,\r
6160                 left,\r
6161                 top,\r
6162                 getBorderWidth = me.getBorderWidth,\r
6163                 getPadding = me.getPadding, \r
6164                 l,\r
6165                 r,\r
6166                 t,\r
6167                 b;\r
6168         if(!local){\r
6169             xy = me.getXY();\r
6170         }else{\r
6171             left = parseInt(me.getStyle("left"), 10) || 0;\r
6172             top = parseInt(me.getStyle("top"), 10) || 0;\r
6173             xy = [left, top];\r
6174         }\r
6175         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;\r
6176         if(!contentBox){\r
6177             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};\r
6178         }else{\r
6179             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");\r
6180             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");\r
6181             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");\r
6182             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");\r
6183             bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};\r
6184         }\r
6185         bx.right = bx.x + bx.width;\r
6186         bx.bottom = bx.y + bx.height;\r
6187         return bx;\r
6188         },\r
6189         \r
6190     /**\r
6191      * Move this element relative to its current position.\r
6192      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6193      * @param {Number} distance How far to move the element in pixels\r
6194      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6195      * @return {Ext.Element} this\r
6196      */\r
6197      move : function(direction, distance, animate){\r
6198         var me = this,          \r
6199                 xy = me.getXY(),\r
6200                 x = xy[0],\r
6201                 y = xy[1],              \r
6202                 left = [x - distance, y],\r
6203                 right = [x + distance, y],\r
6204                 top = [x, y - distance],\r
6205                 bottom = [x, y + distance],\r
6206                 hash = {\r
6207                         l :     left,\r
6208                         left : left,\r
6209                         r : right,\r
6210                         right : right,\r
6211                         t : top,\r
6212                         top : top,\r
6213                         up : top,\r
6214                         b : bottom, \r
6215                         bottom : bottom,\r
6216                         down : bottom                           \r
6217                 };\r
6218         \r
6219             direction = direction.toLowerCase();    \r
6220             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));\r
6221     },\r
6222     \r
6223     /**\r
6224      * Quick set left and top adding default units\r
6225      * @param {String} left The left CSS property value\r
6226      * @param {String} top The top CSS property value\r
6227      * @return {Ext.Element} this\r
6228      */\r
6229      setLeftTop : function(left, top){\r
6230             var me = this,\r
6231                 style = me.dom.style;\r
6232         style.left = me.addUnits(left);\r
6233         style.top = me.addUnits(top);\r
6234         return me;\r
6235     },\r
6236     \r
6237     /**\r
6238      * Returns the region of the given element.\r
6239      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).\r
6240      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.\r
6241      */\r
6242     getRegion : function(){\r
6243         return Ext.lib.Dom.getRegion(this.dom);\r
6244     },\r
6245     \r
6246     /**\r
6247      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.\r
6248      * @param {Number} x X value for new position (coordinates are page-based)\r
6249      * @param {Number} y Y value for new position (coordinates are page-based)\r
6250      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6251      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6252      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6253      * </ul></div>\r
6254      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6255      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6256      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6257      * </ul></div>\r
6258      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6259      * @return {Ext.Element} this\r
6260      */\r
6261     setBounds : function(x, y, width, height, animate){\r
6262             var me = this;\r
6263         if (!animate || !me.anim) {\r
6264             me.setSize(width, height);\r
6265             me.setLocation(x, y);\r
6266         } else {\r
6267             me.anim({points: {to: [x, y]}, \r
6268                          width: {to: me.adjustWidth(width)}, \r
6269                          height: {to: me.adjustHeight(height)}},\r
6270                      me.preanim(arguments, 4), \r
6271                      'motion');\r
6272         }\r
6273         return me;\r
6274     },\r
6275 \r
6276     /**\r
6277      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.\r
6278      * @param {Ext.lib.Region} region The region to fill\r
6279      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6280      * @return {Ext.Element} this\r
6281      */\r
6282     setRegion : function(region, animate) {\r
6283         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));\r
6284     }\r
6285 });/**\r
6286  * @class Ext.Element\r
6287  */\r
6288 Ext.Element.addMethods({\r
6289     /**\r
6290      * Returns true if this element is scrollable.\r
6291      * @return {Boolean}\r
6292      */\r
6293     isScrollable : function(){\r
6294         var dom = this.dom;\r
6295         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;\r
6296     },\r
6297 \r
6298     /**\r
6299      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().\r
6300      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6301      * @param {Number} value The new scroll value.\r
6302      * @return {Element} this\r
6303      */\r
6304     scrollTo : function(side, value){\r
6305         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;\r
6306         return this;\r
6307     },\r
6308 \r
6309     /**\r
6310      * Returns the current scroll position of the element.\r
6311      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}\r
6312      */\r
6313     getScroll : function(){\r
6314         var d = this.dom, \r
6315             doc = document,\r
6316             body = doc.body,\r
6317             docElement = doc.documentElement,\r
6318             l,\r
6319             t,\r
6320             ret;\r
6321 \r
6322         if(d == doc || d == body){\r
6323             if(Ext.isIE && Ext.isStrict){\r
6324                 l = docElement.scrollLeft; \r
6325                 t = docElement.scrollTop;\r
6326             }else{\r
6327                 l = window.pageXOffset;\r
6328                 t = window.pageYOffset;\r
6329             }\r
6330             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};\r
6331         }else{\r
6332             ret = {left: d.scrollLeft, top: d.scrollTop};\r
6333         }\r
6334         return ret;\r
6335     }\r
6336 });/**\r
6337  * @class Ext.Element\r
6338  */\r
6339 Ext.Element.addMethods({\r
6340     /**\r
6341      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().\r
6342      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6343      * @param {Number} value The new scroll value\r
6344      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6345      * @return {Element} this\r
6346      */\r
6347     scrollTo : function(side, value, animate){\r
6348         var tester = /top/i,\r
6349                 prop = "scroll" + (tester.test(side) ? "Top" : "Left"),\r
6350                 me = this,\r
6351                 dom = me.dom;\r
6352         if (!animate || !me.anim) {\r
6353             dom[prop] = value;\r
6354         } else {\r
6355             me.anim({scroll: {to: tester.test(prop) ? [dom[prop], value] : [value, dom[prop]]}},\r
6356                          me.preanim(arguments, 2), 'scroll');\r
6357         }\r
6358         return me;\r
6359     },\r
6360     \r
6361     /**\r
6362      * Scrolls this element into view within the passed container.\r
6363      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a\r
6364      * string (id), dom node, or Ext.Element.\r
6365      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)\r
6366      * @return {Ext.Element} this\r
6367      */\r
6368     scrollIntoView : function(container, hscroll){\r
6369         var c = Ext.getDom(container) || Ext.getBody().dom,\r
6370                 el = this.dom,\r
6371                 o = this.getOffsetsTo(c),\r
6372             l = o[0] + c.scrollLeft,\r
6373             t = o[1] + c.scrollTop,\r
6374             b = t + el.offsetHeight,\r
6375             r = l + el.offsetWidth,\r
6376                 ch = c.clientHeight,\r
6377                 ct = parseInt(c.scrollTop, 10),\r
6378                 cl = parseInt(c.scrollLeft, 10),\r
6379                 cb = ct + ch,\r
6380                 cr = cl + c.clientWidth;\r
6381 \r
6382         if (el.offsetHeight > ch || t < ct) {\r
6383                 c.scrollTop = t;\r
6384         } else if (b > cb){\r
6385             c.scrollTop = b-ch;\r
6386         }\r
6387         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore\r
6388 \r
6389         if(hscroll !== false){\r
6390                         if(el.offsetWidth > c.clientWidth || l < cl){\r
6391                 c.scrollLeft = l;\r
6392             }else if(r > cr){\r
6393                 c.scrollLeft = r - c.clientWidth;\r
6394             }\r
6395             c.scrollLeft = c.scrollLeft;\r
6396         }\r
6397         return this;\r
6398     },\r
6399 \r
6400     // private\r
6401     scrollChildIntoView : function(child, hscroll){\r
6402         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);\r
6403     },\r
6404     \r
6405     /**\r
6406      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is\r
6407      * within this element's scrollable range.\r
6408      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6409      * @param {Number} distance How far to scroll the element in pixels\r
6410      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6411      * @return {Boolean} Returns true if a scroll was triggered or false if the element\r
6412      * was scrolled as far as it could go.\r
6413      */\r
6414      scroll : function(direction, distance, animate){\r
6415          if(!this.isScrollable()){\r
6416              return;\r
6417          }\r
6418          var el = this.dom,\r
6419             l = el.scrollLeft, t = el.scrollTop,\r
6420             w = el.scrollWidth, h = el.scrollHeight,\r
6421             cw = el.clientWidth, ch = el.clientHeight,\r
6422             scrolled = false, v,\r
6423             hash = {\r
6424                 l: Math.min(l + distance, w-cw),\r
6425                 r: v = Math.max(l - distance, 0),\r
6426                 t: Math.max(t - distance, 0),\r
6427                 b: Math.min(t + distance, h-ch)\r
6428             };\r
6429             hash.d = hash.b;\r
6430             hash.u = hash.t;\r
6431             \r
6432          direction = direction.substr(0, 1);\r
6433          if((v = hash[direction]) > -1){\r
6434             scrolled = true;\r
6435             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));\r
6436          }\r
6437          return scrolled;\r
6438     }\r
6439 });/**\r
6440  * @class Ext.Element\r
6441  */\r
6442 /**\r
6443  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element\r
6444  * @static\r
6445  * @type Number\r
6446  */\r
6447 Ext.Element.VISIBILITY = 1;\r
6448 /**\r
6449  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element\r
6450  * @static\r
6451  * @type Number\r
6452  */\r
6453 Ext.Element.DISPLAY = 2;\r
6454 \r
6455 Ext.Element.addMethods(function(){\r
6456     var VISIBILITY = "visibility",\r
6457         DISPLAY = "display",\r
6458         HIDDEN = "hidden",\r
6459         NONE = "none",      \r
6460         ORIGINALDISPLAY = 'originalDisplay',\r
6461         VISMODE = 'visibilityMode',\r
6462         ELDISPLAY = Ext.Element.DISPLAY,\r
6463         data = Ext.Element.data,\r
6464         getDisplay = function(dom){\r
6465             var d = data(dom, ORIGINALDISPLAY);\r
6466             if(d === undefined){\r
6467                 data(dom, ORIGINALDISPLAY, d = '');\r
6468             }\r
6469             return d;\r
6470         },\r
6471         getVisMode = function(dom){\r
6472             var m = data(dom, VISMODE);\r
6473             if(m === undefined){\r
6474                 data(dom, VISMODE, m = 1)\r
6475             }\r
6476             return m;\r
6477         };\r
6478     \r
6479     return {\r
6480         /**\r
6481          * The element's default display mode  (defaults to "")\r
6482          * @type String\r
6483          */\r
6484         originalDisplay : "",\r
6485         visibilityMode : 1,\r
6486         \r
6487         /**\r
6488          * Sets the element's visibility mode. When setVisible() is called it\r
6489          * will use this to determine whether to set the visibility or the display property.\r
6490          * @param visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY\r
6491          * @return {Ext.Element} this\r
6492          */\r
6493         setVisibilityMode : function(visMode){  \r
6494             data(this.dom, VISMODE, visMode);\r
6495             return this;\r
6496         },\r
6497         \r
6498         /**\r
6499          * Perform custom animation on this element.\r
6500          * <div><ul class="mdetail-params">\r
6501          * <li><u>Animation Properties</u></li>\r
6502          * \r
6503          * <p>The Animation Control Object enables gradual transitions for any member of an\r
6504          * element's style object that takes a numeric value including but not limited to\r
6505          * these properties:</p><div><ul class="mdetail-params">\r
6506          * <li><tt>bottom, top, left, right</tt></li>\r
6507          * <li><tt>height, width</tt></li>\r
6508          * <li><tt>margin, padding</tt></li>\r
6509          * <li><tt>borderWidth</tt></li>\r
6510          * <li><tt>opacity</tt></li>\r
6511          * <li><tt>fontSize</tt></li>\r
6512          * <li><tt>lineHeight</tt></li>\r
6513          * </ul></div>\r
6514          * \r
6515          * \r
6516          * <li><u>Animation Property Attributes</u></li>\r
6517          * \r
6518          * <p>Each Animation Property is a config object with optional properties:</p>\r
6519          * <div><ul class="mdetail-params">\r
6520          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>\r
6521          * <li><tt>from</tt> : ignore current value, start from this value</li>\r
6522          * <li><tt>to</tt>*  : start at current value, go to this value</li>\r
6523          * <li><tt>unit</tt> : any allowable unit specification</li>\r
6524          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>\r
6525          * </ul></div>\r
6526          * \r
6527          * <li><u>Animation Types</u></li>\r
6528          * \r
6529          * <p>The supported animation types:</p><div><ul class="mdetail-params">\r
6530          * <li><tt>'run'</tt> : Default\r
6531          * <pre><code>\r
6532 var el = Ext.get('complexEl');\r
6533 el.animate(\r
6534     // animation control object\r
6535     {\r
6536         borderWidth: {to: 3, from: 0},\r
6537         opacity: {to: .3, from: 1},\r
6538         height: {to: 50, from: el.getHeight()},\r
6539         width: {to: 300, from: el.getWidth()},\r
6540         top  : {by: - 100, unit: 'px'},\r
6541     },\r
6542     0.35,      // animation duration\r
6543     null,      // callback\r
6544     'easeOut', // easing method\r
6545     'run'      // animation type ('run','color','motion','scroll')    \r
6546 );\r
6547          * </code></pre>\r
6548          * </li>\r
6549          * <li><tt>'color'</tt>\r
6550          * <p>Animates transition of background, text, or border colors.</p>\r
6551          * <pre><code>\r
6552 el.animate(\r
6553     // animation control object\r
6554     {\r
6555         color: { to: '#06e' },\r
6556         backgroundColor: { to: '#e06' }\r
6557     },\r
6558     0.35,      // animation duration\r
6559     null,      // callback\r
6560     'easeOut', // easing method\r
6561     'color'    // animation type ('run','color','motion','scroll')    \r
6562 );\r
6563          * </code></pre> \r
6564          * </li>\r
6565          * \r
6566          * <li><tt>'motion'</tt>\r
6567          * <p>Animates the motion of an element to/from specific points using optional bezier\r
6568          * way points during transit.</p>\r
6569          * <pre><code>\r
6570 el.animate(\r
6571     // animation control object\r
6572     {\r
6573         borderWidth: {to: 3, from: 0},\r
6574         opacity: {to: .3, from: 1},\r
6575         height: {to: 50, from: el.getHeight()},\r
6576         width: {to: 300, from: el.getWidth()},\r
6577         top  : {by: - 100, unit: 'px'},\r
6578         points: {\r
6579             to: [50, 100],  // go to this point\r
6580             control: [      // optional bezier way points\r
6581                 [ 600, 800],\r
6582                 [-100, 200]\r
6583             ]\r
6584         }\r
6585     },\r
6586     3000,      // animation duration (milliseconds!)\r
6587     null,      // callback\r
6588     'easeOut', // easing method\r
6589     'motion'   // animation type ('run','color','motion','scroll')    \r
6590 );\r
6591          * </code></pre> \r
6592          * </li>\r
6593          * <li><tt>'scroll'</tt>\r
6594          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>\r
6595          * <pre><code>\r
6596 el.animate(\r
6597     // animation control object\r
6598     {\r
6599         scroll: {to: [400, 300]}\r
6600     },\r
6601     0.35,      // animation duration\r
6602     null,      // callback\r
6603     'easeOut', // easing method\r
6604     'scroll'   // animation type ('run','color','motion','scroll')    \r
6605 );\r
6606          * </code></pre> \r
6607          * </li>\r
6608          * </ul></div>\r
6609          * \r
6610          * </ul></div>\r
6611          * \r
6612          * @param {Object} args The animation control args\r
6613          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)\r
6614          * @param {Function} onComplete (optional) Function to call when animation completes\r
6615          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)\r
6616          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,\r
6617          * <tt>'motion'</tt>, or <tt>'scroll'</tt>\r
6618          * @return {Ext.Element} this\r
6619          */\r
6620         animate : function(args, duration, onComplete, easing, animType){       \r
6621             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);\r
6622             return this;\r
6623         },\r
6624     \r
6625         /*\r
6626          * @private Internal animation call\r
6627          */\r
6628         anim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
6629             animType = animType || 'run';\r
6630             opt = opt || {};\r
6631             var me = this,              \r
6632                 anim = Ext.lib.Anim[animType](\r
6633                     me.dom, \r
6634                     args,\r
6635                     (opt.duration || defaultDur) || .35,\r
6636                     (opt.easing || defaultEase) || 'easeOut',\r
6637                     function(){\r
6638                         if(cb) cb.call(me);\r
6639                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);\r
6640                     },\r
6641                     me\r
6642                 );\r
6643             opt.anim = anim;\r
6644             return anim;\r
6645         },\r
6646     \r
6647         // private legacy anim prep\r
6648         preanim : function(a, i){\r
6649             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});\r
6650         },\r
6651         \r
6652         /**\r
6653          * Checks whether the element is currently visible using both visibility and display properties.         \r
6654          * @return {Boolean} True if the element is currently visible, else false\r
6655          */\r
6656         isVisible : function() {\r
6657             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);\r
6658         },\r
6659         \r
6660         /**\r
6661          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use\r
6662          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.\r
6663          * @param {Boolean} visible Whether the element is visible\r
6664          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6665          * @return {Ext.Element} this\r
6666          */\r
6667          setVisible : function(visible, animate){\r
6668             var me = this,\r
6669                 dom = me.dom,\r
6670                 isDisplay = getVisMode(this.dom) == ELDISPLAY;\r
6671                 \r
6672             if (!animate || !me.anim) {\r
6673                 if(isDisplay){\r
6674                     me.setDisplayed(visible);\r
6675                 }else{\r
6676                     me.fixDisplay();\r
6677                     dom.style.visibility = visible ? "visible" : HIDDEN;\r
6678                 }\r
6679             }else{\r
6680                 // closure for composites            \r
6681                 if(visible){\r
6682                     me.setOpacity(.01);\r
6683                     me.setVisible(true);\r
6684                 }\r
6685                 me.anim({opacity: { to: (visible?1:0) }},\r
6686                         me.preanim(arguments, 1),\r
6687                         null,\r
6688                         .35,\r
6689                         'easeIn',\r
6690                         function(){\r
6691                              if(!visible){\r
6692                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;                     \r
6693                                  Ext.fly(dom).setOpacity(1);\r
6694                              }\r
6695                         });\r
6696             }\r
6697             return me;\r
6698         },\r
6699     \r
6700         /**\r
6701          * Toggles the element's visibility or display, depending on visibility mode.\r
6702          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6703          * @return {Ext.Element} this\r
6704          */\r
6705         toggle : function(animate){\r
6706             var me = this;\r
6707             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));\r
6708             return me;\r
6709         },\r
6710     \r
6711         /**\r
6712          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.\r
6713          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.\r
6714          * @return {Ext.Element} this\r
6715          */\r
6716         setDisplayed : function(value) {            \r
6717             if(typeof value == "boolean"){\r
6718                value = value ? getDisplay(this.dom) : NONE;\r
6719             }\r
6720             this.setStyle(DISPLAY, value);\r
6721             return this;\r
6722         },\r
6723         \r
6724         // private\r
6725         fixDisplay : function(){\r
6726             var me = this;\r
6727             if(me.isStyle(DISPLAY, NONE)){\r
6728                 me.setStyle(VISIBILITY, HIDDEN);\r
6729                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default\r
6730                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block\r
6731                     me.setStyle(DISPLAY, "block");\r
6732                 }\r
6733             }\r
6734         },\r
6735     \r
6736         /**\r
6737          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
6738          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6739          * @return {Ext.Element} this\r
6740          */\r
6741         hide : function(animate){\r
6742             this.setVisible(false, this.preanim(arguments, 0));\r
6743             return this;\r
6744         },\r
6745     \r
6746         /**\r
6747         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
6748         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6749          * @return {Ext.Element} this\r
6750          */\r
6751         show : function(animate){\r
6752             this.setVisible(true, this.preanim(arguments, 0));\r
6753             return this;\r
6754         }\r
6755     }\r
6756 }());/**\r
6757  * @class Ext.Element\r
6758  */\r
6759 Ext.Element.addMethods(\r
6760 function(){\r
6761     var VISIBILITY = "visibility",\r
6762         DISPLAY = "display",\r
6763         HIDDEN = "hidden",\r
6764         NONE = "none",\r
6765             XMASKED = "x-masked",\r
6766                 XMASKEDRELATIVE = "x-masked-relative",\r
6767         data = Ext.Element.data;\r
6768                 \r
6769         return {\r
6770                 /**\r
6771              * Checks whether the element is currently visible using both visibility and display properties.\r
6772              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)\r
6773              * @return {Boolean} True if the element is currently visible, else false\r
6774              */\r
6775             isVisible : function(deep) {\r
6776                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),\r
6777                         p = this.dom.parentNode;\r
6778                 if(deep !== true || !vis){\r
6779                     return vis;\r
6780                 }               \r
6781                 while(p && !/body/i.test(p.tagName)){\r
6782                     if(!Ext.fly(p, '_isVisible').isVisible()){\r
6783                         return false;\r
6784                     }\r
6785                     p = p.parentNode;\r
6786                 }\r
6787                 return true;\r
6788             },\r
6789             \r
6790             /**\r
6791              * Returns true if display is not "none"\r
6792              * @return {Boolean}\r
6793              */\r
6794             isDisplayed : function() {\r
6795                 return !this.isStyle(DISPLAY, NONE);\r
6796             },\r
6797             \r
6798                 /**\r
6799              * Convenience method for setVisibilityMode(Element.DISPLAY)\r
6800              * @param {String} display (optional) What to set display to when visible\r
6801              * @return {Ext.Element} this\r
6802              */\r
6803             enableDisplayMode : function(display){          \r
6804                 this.setVisibilityMode(Ext.Element.DISPLAY);\r
6805                 if(!Ext.isEmpty(display)){\r
6806                 data(this.dom, 'originalDisplay', display);\r
6807             }\r
6808                 return this;\r
6809             },\r
6810             \r
6811                 /**\r
6812              * Puts a mask over this element to disable user interaction. Requires core.css.\r
6813              * This method can only be applied to elements which accept child nodes.\r
6814              * @param {String} msg (optional) A message to display in the mask\r
6815              * @param {String} msgCls (optional) A css class to apply to the msg element\r
6816              * @return {Element} The mask element\r
6817              */\r
6818             mask : function(msg, msgCls){\r
6819                     var me = this,\r
6820                         dom = me.dom,\r
6821                         dh = Ext.DomHelper,\r
6822                         EXTELMASKMSG = "ext-el-mask-msg",\r
6823                 el, \r
6824                 mask;\r
6825                         \r
6826                 if(me.getStyle("position") == "static"){\r
6827                     me.addClass(XMASKEDRELATIVE);\r
6828                 }\r
6829                 if((el = data(dom, 'maskMsg'))){\r
6830                     el.remove();\r
6831                 }\r
6832                 if((el = data(dom, 'mask'))){\r
6833                     el.remove();\r
6834                 }\r
6835         \r
6836             mask = dh.append(dom, {cls : "ext-el-mask"}, true);\r
6837                 data(dom, 'mask', mask);\r
6838         \r
6839                 me.addClass(XMASKED);\r
6840                 mask.setDisplayed(true);\r
6841                 if(typeof msg == 'string'){\r
6842                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);\r
6843                 data(dom, 'maskMsg', mm);\r
6844                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;\r
6845                     mm.dom.firstChild.innerHTML = msg;\r
6846                     mm.setDisplayed(true);\r
6847                     mm.center(me);\r
6848                 }\r
6849                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically\r
6850                     mask.setSize(undefined, me.getHeight());\r
6851                 }\r
6852                 return mask;\r
6853             },\r
6854         \r
6855             /**\r
6856              * Removes a previously applied mask.\r
6857              */\r
6858             unmask : function(){\r
6859                     var me = this,\r
6860                 dom = me.dom,\r
6861                         mask = data(dom, 'mask'),\r
6862                         maskMsg = data(dom, 'maskMsg');\r
6863                 if(mask){\r
6864                     if(maskMsg){\r
6865                         maskMsg.remove();\r
6866                     data(dom, 'maskMsg', undefined);\r
6867                     }\r
6868                     mask.remove();\r
6869                 data(dom, 'mask', undefined);\r
6870                 }\r
6871                 me.removeClass([XMASKED, XMASKEDRELATIVE]);\r
6872             },\r
6873         \r
6874             /**\r
6875              * Returns true if this element is masked\r
6876              * @return {Boolean}\r
6877              */\r
6878             isMasked : function(){\r
6879             var m = data(this.dom, 'mask');\r
6880                 return m && m.isVisible();\r
6881             },\r
6882             \r
6883             /**\r
6884              * Creates an iframe shim for this element to keep selects and other windowed objects from\r
6885              * showing through.\r
6886              * @return {Ext.Element} The new shim element\r
6887              */\r
6888             createShim : function(){\r
6889                 var el = document.createElement('iframe'),              \r
6890                         shim;\r
6891                 el.frameBorder = '0';\r
6892                 el.className = 'ext-shim';\r
6893                 if(Ext.isIE && Ext.isSecure){\r
6894                     el.src = Ext.SSL_SECURE_URL;\r
6895                 }\r
6896                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));\r
6897                 shim.autoBoxAdjust = false;\r
6898                 return shim;\r
6899             }\r
6900     };\r
6901 }());/**\r
6902  * @class Ext.Element\r
6903  */\r
6904 Ext.Element.addMethods({\r
6905     /**\r
6906      * Convenience method for constructing a KeyMap\r
6907      * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:\r
6908      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
6909      * @param {Function} fn The function to call\r
6910      * @param {Object} scope (optional) The scope of the function\r
6911      * @return {Ext.KeyMap} The KeyMap created\r
6912      */\r
6913     addKeyListener : function(key, fn, scope){\r
6914         var config;\r
6915         if(!Ext.isObject(key) || Ext.isArray(key)){\r
6916             config = {\r
6917                 key: key,\r
6918                 fn: fn,\r
6919                 scope: scope\r
6920             };\r
6921         }else{\r
6922             config = {\r
6923                 key : key.key,\r
6924                 shift : key.shift,\r
6925                 ctrl : key.ctrl,\r
6926                 alt : key.alt,\r
6927                 fn: fn,\r
6928                 scope: scope\r
6929             };\r
6930         }\r
6931         return new Ext.KeyMap(this, config);\r
6932     },\r
6933 \r
6934     /**\r
6935      * Creates a KeyMap for this element\r
6936      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details\r
6937      * @return {Ext.KeyMap} The KeyMap created\r
6938      */\r
6939     addKeyMap : function(config){\r
6940         return new Ext.KeyMap(this, config);\r
6941     }\r
6942 });(function(){\r
6943     // contants\r
6944     var NULL = null,\r
6945         UNDEFINED = undefined,\r
6946         TRUE = true,\r
6947         FALSE = false,\r
6948         SETX = "setX",\r
6949         SETY = "setY",\r
6950         SETXY = "setXY",\r
6951         LEFT = "left",\r
6952         BOTTOM = "bottom",\r
6953         TOP = "top",\r
6954         RIGHT = "right",\r
6955         HEIGHT = "height",\r
6956         WIDTH = "width",\r
6957         POINTS = "points",\r
6958         HIDDEN = "hidden",\r
6959         ABSOLUTE = "absolute",\r
6960         VISIBLE = "visible",\r
6961         MOTION = "motion",\r
6962         POSITION = "position",\r
6963         EASEOUT = "easeOut",\r
6964         /*\r
6965          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element\r
6966          */\r
6967         flyEl = new Ext.Element.Flyweight(),\r
6968         queues = {},\r
6969         getObject = function(o){\r
6970             return o || {};\r
6971         },\r
6972         fly = function(dom){\r
6973             flyEl.dom = dom;\r
6974             flyEl.id = Ext.id(dom);\r
6975             return flyEl;\r
6976         },\r
6977         /*\r
6978          * Queueing now stored outside of the element due to closure issues\r
6979          */\r
6980         getQueue = function(id){\r
6981             if(!queues[id]){\r
6982                 queues[id] = [];\r
6983             }\r
6984             return queues[id];\r
6985         },\r
6986         setQueue = function(id, value){\r
6987             queues[id] = value;\r
6988         };\r
6989         \r
6990 //Notifies Element that fx methods are available\r
6991 Ext.enableFx = TRUE;\r
6992 \r
6993 /**\r
6994  * @class Ext.Fx\r
6995  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied\r
6996  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.\r
6997  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be\r
6998  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>\r
6999  * \r
7000  * <p><b><u>Method Chaining</u></b></p>\r
7001  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that\r
7002  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single\r
7003  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.\r
7004  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,\r
7005  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the\r
7006  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>\r
7007  *\r
7008  * <p><b><u>Anchor Options for Motion Effects</u></b></p>\r
7009  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element\r
7010  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>\r
7011 <pre>\r
7012 Value  Description\r
7013 -----  -----------------------------\r
7014 tl     The top left corner\r
7015 t      The center of the top edge\r
7016 tr     The top right corner\r
7017 l      The center of the left edge\r
7018 r      The center of the right edge\r
7019 bl     The bottom left corner\r
7020 b      The center of the bottom edge\r
7021 br     The bottom right corner\r
7022 </pre>\r
7023  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options\r
7024  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>\r
7025  * \r
7026  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the\r
7027  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together\r
7028  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>\r
7029  * el.slideIn().highlight();\r
7030  * </code></pre>\r
7031  * The callback is intended for any additional code that should run once a particular effect has completed. The Element\r
7032  * being operated upon is passed as the first parameter.\r
7033  * \r
7034  * @cfg {Object} scope The scope of the <tt>{@link #callback}</tt> function\r
7035  * \r
7036  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>\r
7037  * <li><b><tt>backBoth</tt></b></li>\r
7038  * <li><b><tt>backIn</tt></b></li>\r
7039  * <li><b><tt>backOut</tt></b></li>\r
7040  * <li><b><tt>bounceBoth</tt></b></li>\r
7041  * <li><b><tt>bounceIn</tt></b></li>\r
7042  * <li><b><tt>bounceOut</tt></b></li>\r
7043  * <li><b><tt>easeBoth</tt></b></li>\r
7044  * <li><b><tt>easeBothStrong</tt></b></li>\r
7045  * <li><b><tt>easeIn</tt></b></li>\r
7046  * <li><b><tt>easeInStrong</tt></b></li>\r
7047  * <li><b><tt>easeNone</tt></b></li>\r
7048  * <li><b><tt>easeOut</tt></b></li>\r
7049  * <li><b><tt>easeOutStrong</tt></b></li>\r
7050  * <li><b><tt>elasticBoth</tt></b></li>\r
7051  * <li><b><tt>elasticIn</tt></b></li>\r
7052  * <li><b><tt>elasticOut</tt></b></li>\r
7053  * </ul></div>\r
7054  *\r
7055  * @cfg {String} afterCls A css class to apply after the effect\r
7056  * @cfg {Number} duration The length of time (in seconds) that the effect should last\r
7057  * \r
7058  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between\r
7059  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.\r
7060  *  \r
7061  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes\r
7062  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to \r
7063  * effects that end with the element being visually hidden, ignored otherwise)\r
7064  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object\r
7065  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the\r
7066  * Element after the effect finishes.\r
7067  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs\r
7068  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence\r
7069  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)\r
7070  */\r
7071 Ext.Fx = {\r
7072     \r
7073     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.\r
7074     //           this is useful for replacing switch statements (for example).\r
7075     switchStatements : function(key, fn, argHash){\r
7076         return fn.apply(this, argHash[key]);\r
7077     },\r
7078     \r
7079     /**\r
7080      * Slides the element into view.  An anchor point can be optionally passed to set the point of\r
7081      * origin for the slide effect.  This function automatically handles wrapping the element with\r
7082      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7083      * Usage:\r
7084      *<pre><code>\r
7085 // default: slide the element in from the top\r
7086 el.slideIn();\r
7087 \r
7088 // custom: slide the element in from the right with a 2-second duration\r
7089 el.slideIn('r', { duration: 2 });\r
7090 \r
7091 // common config options shown with default values\r
7092 el.slideIn('t', {\r
7093     easing: 'easeOut',\r
7094     duration: .5\r
7095 });\r
7096 </code></pre>\r
7097      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7098      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7099      * @return {Ext.Element} The Element\r
7100      */\r
7101     slideIn : function(anchor, o){ \r
7102         o = getObject(o);\r
7103         var me = this,\r
7104             dom = me.dom,\r
7105             st = dom.style,\r
7106             xy,\r
7107             r,\r
7108             b,              \r
7109             wrap,               \r
7110             after,\r
7111             st,\r
7112             args, \r
7113             pt,\r
7114             bw,\r
7115             bh;\r
7116             \r
7117         anchor = anchor || "t";\r
7118 \r
7119         me.queueFx(o, function(){            \r
7120             xy = fly(dom).getXY();\r
7121             // fix display to visibility\r
7122             fly(dom).fixDisplay();            \r
7123             \r
7124             // restore values after effect\r
7125             r = fly(dom).getFxRestore();      \r
7126             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7127             b.right = b.x + b.width;\r
7128             b.bottom = b.y + b.height;\r
7129             \r
7130             // fixed size for slide\r
7131             fly(dom).setWidth(b.width).setHeight(b.height);            \r
7132             \r
7133             // wrap if needed\r
7134             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);\r
7135             \r
7136             st.visibility = VISIBLE;\r
7137             st.position = ABSOLUTE;\r
7138             \r
7139             // clear out temp styles after slide and unwrap\r
7140             function after(){\r
7141                  fly(dom).fxUnwrap(wrap, r.pos, o);\r
7142                  st.width = r.width;\r
7143                  st.height = r.height;\r
7144                  fly(dom).afterFx(o);\r
7145             }\r
7146             \r
7147             // time to calculate the positions        \r
7148             pt = {to: [b.x, b.y]}; \r
7149             bw = {to: b.width};\r
7150             bh = {to: b.height};\r
7151                 \r
7152             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    \r
7153                 var ret = {};\r
7154                 fly(wrap).setWidth(ww).setHeight(wh);\r
7155                 if(fly(wrap)[sXY]){\r
7156                     fly(wrap)[sXY](sXYval);                  \r
7157                 }\r
7158                 style[s1] = style[s2] = "0";                    \r
7159                 if(w){\r
7160                     ret.width = w\r
7161                 };\r
7162                 if(h){\r
7163                     ret.height = h;\r
7164                 }\r
7165                 if(p){\r
7166                     ret.points = p;\r
7167                 }\r
7168                 return ret;\r
7169             };\r
7170 \r
7171             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7172                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],\r
7173                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],\r
7174                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],\r
7175                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],\r
7176                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],\r
7177                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],\r
7178                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],\r
7179                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]\r
7180                 });\r
7181             \r
7182             st.visibility = VISIBLE;\r
7183             fly(wrap).show();\r
7184 \r
7185             arguments.callee.anim = fly(wrap).fxanim(args,\r
7186                 o,\r
7187                 MOTION,\r
7188                 .5,\r
7189                 EASEOUT, \r
7190                 after);\r
7191         });\r
7192         return me;\r
7193     },\r
7194     \r
7195     /**\r
7196      * Slides the element out of view.  An anchor point can be optionally passed to set the end point\r
7197      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = \r
7198      * 'hidden') but block elements will still take up space in the document.  The element must be removed\r
7199      * from the DOM using the 'remove' config option if desired.  This function automatically handles \r
7200      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7201      * Usage:\r
7202      *<pre><code>\r
7203 // default: slide the element out to the top\r
7204 el.slideOut();\r
7205 \r
7206 // custom: slide the element out to the right with a 2-second duration\r
7207 el.slideOut('r', { duration: 2 });\r
7208 \r
7209 // common config options shown with default values\r
7210 el.slideOut('t', {\r
7211     easing: 'easeOut',\r
7212     duration: .5,\r
7213     remove: false,\r
7214     useDisplay: false\r
7215 });\r
7216 </code></pre>\r
7217      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7218      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7219      * @return {Ext.Element} The Element\r
7220      */\r
7221     slideOut : function(anchor, o){\r
7222         o = getObject(o);\r
7223         var me = this,\r
7224             dom = me.dom,\r
7225             st = dom.style,\r
7226             xy = me.getXY(),\r
7227             wrap,\r
7228             r,\r
7229             b,\r
7230             a,\r
7231             zero = {to: 0}; \r
7232                     \r
7233         anchor = anchor || "t";\r
7234 \r
7235         me.queueFx(o, function(){\r
7236             \r
7237             // restore values after effect\r
7238             r = fly(dom).getFxRestore(); \r
7239             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7240             b.right = b.x + b.width;\r
7241             b.bottom = b.y + b.height;\r
7242                 \r
7243             // fixed size for slide   \r
7244             fly(dom).setWidth(b.width).setHeight(b.height);\r
7245 \r
7246             // wrap if needed\r
7247             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);\r
7248                 \r
7249             st.visibility = VISIBLE;\r
7250             st.position = ABSOLUTE;\r
7251             fly(wrap).setWidth(b.width).setHeight(b.height);            \r
7252 \r
7253             function after(){\r
7254                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                \r
7255                 fly(dom).fxUnwrap(wrap, r.pos, o);\r
7256                 st.width = r.width;\r
7257                 st.height = r.height;\r
7258                 fly(dom).afterFx(o);\r
7259             }            \r
7260             \r
7261             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    \r
7262                 var ret = {};\r
7263                 \r
7264                 style[s1] = style[s2] = "0";\r
7265                 ret[p1] = v1;               \r
7266                 if(p2){\r
7267                     ret[p2] = v2;               \r
7268                 }\r
7269                 if(p3){\r
7270                     ret[p3] = v3;\r
7271                 }\r
7272                 \r
7273                 return ret;\r
7274             };\r
7275             \r
7276             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7277                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],\r
7278                 l  : [st, RIGHT, TOP, WIDTH, zero],\r
7279                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],\r
7280                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7281                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],\r
7282                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7283                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],\r
7284                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]\r
7285             });\r
7286             \r
7287             arguments.callee.anim = fly(wrap).fxanim(a,\r
7288                 o,\r
7289                 MOTION,\r
7290                 .5,\r
7291                 EASEOUT, \r
7292                 after);\r
7293         });\r
7294         return me;\r
7295     },\r
7296 \r
7297     /**\r
7298      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the \r
7299      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. \r
7300      * The element must be removed from the DOM using the 'remove' config option if desired.\r
7301      * Usage:\r
7302      *<pre><code>\r
7303 // default\r
7304 el.puff();\r
7305 \r
7306 // common config options shown with default values\r
7307 el.puff({\r
7308     easing: 'easeOut',\r
7309     duration: .5,\r
7310     remove: false,\r
7311     useDisplay: false\r
7312 });\r
7313 </code></pre>\r
7314      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7315      * @return {Ext.Element} The Element\r
7316      */\r
7317     puff : function(o){\r
7318         o = getObject(o);\r
7319         var me = this,\r
7320             dom = me.dom,\r
7321             st = dom.style,\r
7322             width,\r
7323             height,\r
7324             r;\r
7325 \r
7326         me.queueFx(o, function(){\r
7327             width = fly(dom).getWidth();\r
7328             height = fly(dom).getHeight();\r
7329             fly(dom).clearOpacity();\r
7330             fly(dom).show();\r
7331 \r
7332             // restore values after effect\r
7333             r = fly(dom).getFxRestore();                   \r
7334             \r
7335             function after(){\r
7336                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  \r
7337                 fly(dom).clearOpacity();  \r
7338                 fly(dom).setPositioning(r.pos);\r
7339                 st.width = r.width;\r
7340                 st.height = r.height;\r
7341                 st.fontSize = '';\r
7342                 fly(dom).afterFx(o);\r
7343             }   \r
7344 \r
7345             arguments.callee.anim = fly(dom).fxanim({\r
7346                     width : {to : fly(dom).adjustWidth(width * 2)},\r
7347                     height : {to : fly(dom).adjustHeight(height * 2)},\r
7348                     points : {by : [-width * .5, -height * .5]},\r
7349                     opacity : {to : 0},\r
7350                     fontSize: {to : 200, unit: "%"}\r
7351                 },\r
7352                 o,\r
7353                 MOTION,\r
7354                 .5,\r
7355                 EASEOUT,\r
7356                  after);\r
7357         });\r
7358         return me;\r
7359     },\r
7360 \r
7361     /**\r
7362      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).\r
7363      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still \r
7364      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.\r
7365      * Usage:\r
7366      *<pre><code>\r
7367 // default\r
7368 el.switchOff();\r
7369 \r
7370 // all config options shown with default values\r
7371 el.switchOff({\r
7372     easing: 'easeIn',\r
7373     duration: .3,\r
7374     remove: false,\r
7375     useDisplay: false\r
7376 });\r
7377 </code></pre>\r
7378      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7379      * @return {Ext.Element} The Element\r
7380      */\r
7381     switchOff : function(o){\r
7382         o = getObject(o);\r
7383         var me = this,\r
7384             dom = me.dom,\r
7385             st = dom.style,\r
7386             r;\r
7387 \r
7388         me.queueFx(o, function(){\r
7389             fly(dom).clearOpacity();\r
7390             fly(dom).clip();\r
7391 \r
7392             // restore values after effect\r
7393             r = fly(dom).getFxRestore();\r
7394                 \r
7395             function after(){\r
7396                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  \r
7397                 fly(dom).clearOpacity();\r
7398                 fly(dom).setPositioning(r.pos);\r
7399                 st.width = r.width;\r
7400                 st.height = r.height;   \r
7401                 fly(dom).afterFx(o);\r
7402             };\r
7403 \r
7404             fly(dom).fxanim({opacity : {to : 0.3}}, \r
7405                 NULL, \r
7406                 NULL, \r
7407                 .1, \r
7408                 NULL, \r
7409                 function(){                                 \r
7410                     fly(dom).clearOpacity();\r
7411                         (function(){                            \r
7412                             fly(dom).fxanim({\r
7413                                 height : {to : 1},\r
7414                                 points : {by : [0, fly(dom).getHeight() * .5]}\r
7415                             }, \r
7416                             o, \r
7417                             MOTION, \r
7418                             0.3, \r
7419                             'easeIn', \r
7420                             after);\r
7421                         }).defer(100);\r
7422                 });\r
7423         });\r
7424         return me;\r
7425     },\r
7426 \r
7427     /**\r
7428      * Highlights the Element by setting a color (applies to the background-color by default, but can be\r
7429      * changed using the "attr" config option) and then fading back to the original color. If no original\r
7430      * color is available, you should provide the "endColor" config option which will be cleared after the animation.\r
7431      * Usage:\r
7432 <pre><code>\r
7433 // default: highlight background to yellow\r
7434 el.highlight();\r
7435 \r
7436 // custom: highlight foreground text to blue for 2 seconds\r
7437 el.highlight("0000ff", { attr: 'color', duration: 2 });\r
7438 \r
7439 // common config options shown with default values\r
7440 el.highlight("ffff9c", {\r
7441     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value\r
7442     endColor: (current color) or "ffffff",\r
7443     easing: 'easeIn',\r
7444     duration: 1\r
7445 });\r
7446 </code></pre>\r
7447      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')\r
7448      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7449      * @return {Ext.Element} The Element\r
7450      */ \r
7451     highlight : function(color, o){\r
7452         o = getObject(o);\r
7453         var me = this,\r
7454             dom = me.dom,\r
7455             attr = o.attr || "backgroundColor",\r
7456             a = {},\r
7457             restore;\r
7458 \r
7459         me.queueFx(o, function(){\r
7460             fly(dom).clearOpacity();\r
7461             fly(dom).show();\r
7462 \r
7463             function after(){\r
7464                 dom.style[attr] = restore;\r
7465                 fly(dom).afterFx(o);\r
7466             }            \r
7467             restore = dom.style[attr];\r
7468             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};\r
7469             arguments.callee.anim = fly(dom).fxanim(a,\r
7470                 o,\r
7471                 'color',\r
7472                 1,\r
7473                 'easeIn', \r
7474                 after);\r
7475         });\r
7476         return me;\r
7477     },\r
7478 \r
7479    /**\r
7480     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.\r
7481     * Usage:\r
7482 <pre><code>\r
7483 // default: a single light blue ripple\r
7484 el.frame();\r
7485 \r
7486 // custom: 3 red ripples lasting 3 seconds total\r
7487 el.frame("ff0000", 3, { duration: 3 });\r
7488 \r
7489 // common config options shown with default values\r
7490 el.frame("C3DAF9", 1, {\r
7491     duration: 1 //duration of each individual ripple.\r
7492     // Note: Easing is not configurable and will be ignored if included\r
7493 });\r
7494 </code></pre>\r
7495     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').\r
7496     * @param {Number} count (optional) The number of ripples to display (defaults to 1)\r
7497     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7498     * @return {Ext.Element} The Element\r
7499     */\r
7500     frame : function(color, count, o){\r
7501         o = getObject(o);\r
7502         var me = this,\r
7503             dom = me.dom,\r
7504             proxy,\r
7505             active;\r
7506 \r
7507         me.queueFx(o, function(){\r
7508             color = color || "#C3DAF9"\r
7509             if(color.length == 6){\r
7510                 color = "#" + color;\r
7511             }            \r
7512             count = count || 1;\r
7513             fly(dom).show();\r
7514 \r
7515             var xy = fly(dom).getXY(),\r
7516                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},\r
7517                 queue = function(){\r
7518                     proxy = fly(document.body || document.documentElement).createChild({\r
7519                         style:{\r
7520                             visbility: HIDDEN,\r
7521                             position : ABSOLUTE,\r
7522                             "z-index": 35000, // yee haw\r
7523                             border : "0px solid " + color\r
7524                         }\r
7525                     });\r
7526                     return proxy.queueFx({}, animFn);\r
7527                 };\r
7528             \r
7529             \r
7530             arguments.callee.anim = {\r
7531                 isAnimated: true,\r
7532                 stop: function() {\r
7533                     count = 0;\r
7534                     proxy.stopFx();\r
7535                 }\r
7536             };\r
7537             \r
7538             function animFn(){\r
7539                 var scale = Ext.isBorderBox ? 2 : 1;\r
7540                 active = proxy.anim({\r
7541                     top : {from : b.y, to : b.y - 20},\r
7542                     left : {from : b.x, to : b.x - 20},\r
7543                     borderWidth : {from : 0, to : 10},\r
7544                     opacity : {from : 1, to : 0},\r
7545                     height : {from : b.height, to : b.height + 20 * scale},\r
7546                     width : {from : b.width, to : b.width + 20 * scale}\r
7547                 },{\r
7548                     duration: o.duration || 1,\r
7549                     callback: function() {\r
7550                         proxy.remove();\r
7551                         --count > 0 ? queue() : fly(dom).afterFx(o);\r
7552                     }\r
7553                 });\r
7554                 arguments.callee.anim = {\r
7555                     isAnimated: true,\r
7556                     stop: function(){\r
7557                         active.stop();\r
7558                     }\r
7559                 };\r
7560             };\r
7561             queue();\r
7562         });\r
7563         return me;\r
7564     },\r
7565 \r
7566    /**\r
7567     * Creates a pause before any subsequent queued effects begin.  If there are\r
7568     * no effects queued after the pause it will have no effect.\r
7569     * Usage:\r
7570 <pre><code>\r
7571 el.pause(1);\r
7572 </code></pre>\r
7573     * @param {Number} seconds The length of time to pause (in seconds)\r
7574     * @return {Ext.Element} The Element\r
7575     */\r
7576     pause : function(seconds){        \r
7577         var dom = this.dom,\r
7578             t;\r
7579 \r
7580         this.queueFx({}, function(){\r
7581             t = setTimeout(function(){\r
7582                 fly(dom).afterFx({});\r
7583             }, seconds * 1000);\r
7584             arguments.callee.anim = {\r
7585                 isAnimated: true,\r
7586                 stop: function(){\r
7587                     clearTimeout(t);\r
7588                     fly(dom).afterFx({});\r
7589                 }\r
7590             };\r
7591         });\r
7592         return this;\r
7593     },\r
7594 \r
7595    /**\r
7596     * Fade an element in (from transparent to opaque).  The ending opacity can be specified\r
7597     * using the <tt>{@link #endOpacity}</tt> config option.\r
7598     * Usage:\r
7599 <pre><code>\r
7600 // default: fade in from opacity 0 to 100%\r
7601 el.fadeIn();\r
7602 \r
7603 // custom: fade in from opacity 0 to 75% over 2 seconds\r
7604 el.fadeIn({ endOpacity: .75, duration: 2});\r
7605 \r
7606 // common config options shown with default values\r
7607 el.fadeIn({\r
7608     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)\r
7609     easing: 'easeOut',\r
7610     duration: .5\r
7611 });\r
7612 </code></pre>\r
7613     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7614     * @return {Ext.Element} The Element\r
7615     */\r
7616     fadeIn : function(o){\r
7617         o = getObject(o);\r
7618         var me = this,\r
7619             dom = me.dom,\r
7620             to = o.endOpacity || 1;\r
7621         \r
7622         me.queueFx(o, function(){\r
7623             fly(dom).setOpacity(0);\r
7624             fly(dom).fixDisplay();\r
7625             dom.style.visibility = VISIBLE;\r
7626             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},\r
7627                 o, NULL, .5, EASEOUT, function(){\r
7628                 if(to == 1){\r
7629                     fly(dom).clearOpacity();\r
7630                 }\r
7631                 fly(dom).afterFx(o);\r
7632             });\r
7633         });\r
7634         return me;\r
7635     },\r
7636 \r
7637    /**\r
7638     * Fade an element out (from opaque to transparent).  The ending opacity can be specified\r
7639     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require\r
7640     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.\r
7641     * Usage:\r
7642 <pre><code>\r
7643 // default: fade out from the element's current opacity to 0\r
7644 el.fadeOut();\r
7645 \r
7646 // custom: fade out from the element's current opacity to 25% over 2 seconds\r
7647 el.fadeOut({ endOpacity: .25, duration: 2});\r
7648 \r
7649 // common config options shown with default values\r
7650 el.fadeOut({\r
7651     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)\r
7652     easing: 'easeOut',\r
7653     duration: .5,\r
7654     remove: false,\r
7655     useDisplay: false\r
7656 });\r
7657 </code></pre>\r
7658     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7659     * @return {Ext.Element} The Element\r
7660     */\r
7661     fadeOut : function(o){\r
7662         o = getObject(o);\r
7663         var me = this,\r
7664             dom = me.dom,\r
7665             style = dom.style,\r
7666             to = o.endOpacity || 0;         \r
7667         \r
7668         me.queueFx(o, function(){  \r
7669             arguments.callee.anim = fly(dom).fxanim({ \r
7670                 opacity : {to : to}},\r
7671                 o, \r
7672                 NULL, \r
7673                 .5, \r
7674                 EASEOUT, \r
7675                 function(){\r
7676                     if(to == 0){\r
7677                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? \r
7678                             style.display = "none" :\r
7679                             style.visibility = HIDDEN;\r
7680                             \r
7681                         fly(dom).clearOpacity();\r
7682                     }\r
7683                     fly(dom).afterFx(o);\r
7684             });\r
7685         });\r
7686         return me;\r
7687     },\r
7688 \r
7689    /**\r
7690     * Animates the transition of an element's dimensions from a starting height/width\r
7691     * to an ending height/width.  This method is a convenience implementation of {@link shift}.\r
7692     * Usage:\r
7693 <pre><code>\r
7694 // change height and width to 100x100 pixels\r
7695 el.scale(100, 100);\r
7696 \r
7697 // common config options shown with default values.  The height and width will default to\r
7698 // the element&#39;s existing values if passed as null.\r
7699 el.scale(\r
7700     [element&#39;s width],\r
7701     [element&#39;s height], {\r
7702         easing: 'easeOut',\r
7703         duration: .35\r
7704     }\r
7705 );\r
7706 </code></pre>\r
7707     * @param {Number} width  The new width (pass undefined to keep the original width)\r
7708     * @param {Number} height  The new height (pass undefined to keep the original height)\r
7709     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7710     * @return {Ext.Element} The Element\r
7711     */\r
7712     scale : function(w, h, o){\r
7713         this.shift(Ext.apply({}, o, {\r
7714             width: w,\r
7715             height: h\r
7716         }));\r
7717         return this;\r
7718     },\r
7719 \r
7720    /**\r
7721     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.\r
7722     * Any of these properties not specified in the config object will not be changed.  This effect \r
7723     * requires that at least one new dimension, position or opacity setting must be passed in on\r
7724     * the config object in order for the function to have any effect.\r
7725     * Usage:\r
7726 <pre><code>\r
7727 // slide the element horizontally to x position 200 while changing the height and opacity\r
7728 el.shift({ x: 200, height: 50, opacity: .8 });\r
7729 \r
7730 // common config options shown with default values.\r
7731 el.shift({\r
7732     width: [element&#39;s width],\r
7733     height: [element&#39;s height],\r
7734     x: [element&#39;s x position],\r
7735     y: [element&#39;s y position],\r
7736     opacity: [element&#39;s opacity],\r
7737     easing: 'easeOut',\r
7738     duration: .35\r
7739 });\r
7740 </code></pre>\r
7741     * @param {Object} options  Object literal with any of the Fx config options\r
7742     * @return {Ext.Element} The Element\r
7743     */\r
7744     shift : function(o){\r
7745         o = getObject(o);\r
7746         var dom = this.dom,\r
7747             a = {};\r
7748                 \r
7749         this.queueFx(o, function(){\r
7750             for (var prop in o) {\r
7751                 if (o[prop] != UNDEFINED) {                                                 \r
7752                     a[prop] = {to : o[prop]};                   \r
7753                 }\r
7754             } \r
7755             \r
7756             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;\r
7757             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   \r
7758             \r
7759             if (a.x || a.y || a.xy) {\r
7760                 a.points = a.xy || \r
7761                            {to : [ a.x ? a.x.to : fly(dom).getX(),\r
7762                                    a.y ? a.y.to : fly(dom).getY()]};                  \r
7763             }\r
7764 \r
7765             arguments.callee.anim = fly(dom).fxanim(a,\r
7766                 o, \r
7767                 MOTION, \r
7768                 .35, \r
7769                 EASEOUT, \r
7770                 function(){\r
7771                     fly(dom).afterFx(o);\r
7772                 });\r
7773         });\r
7774         return this;\r
7775     },\r
7776 \r
7777     /**\r
7778      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the \r
7779      * ending point of the effect.\r
7780      * Usage:\r
7781      *<pre><code>\r
7782 // default: slide the element downward while fading out\r
7783 el.ghost();\r
7784 \r
7785 // custom: slide the element out to the right with a 2-second duration\r
7786 el.ghost('r', { duration: 2 });\r
7787 \r
7788 // common config options shown with default values\r
7789 el.ghost('b', {\r
7790     easing: 'easeOut',\r
7791     duration: .5,\r
7792     remove: false,\r
7793     useDisplay: false\r
7794 });\r
7795 </code></pre>\r
7796      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')\r
7797      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7798      * @return {Ext.Element} The Element\r
7799      */\r
7800     ghost : function(anchor, o){\r
7801         o = getObject(o);\r
7802         var me = this,\r
7803             dom = me.dom,\r
7804             st = dom.style,\r
7805             a = {opacity: {to: 0}, points: {}},\r
7806             pt = a.points,\r
7807             r,\r
7808             w,\r
7809             h;\r
7810             \r
7811         anchor = anchor || "b";\r
7812 \r
7813         me.queueFx(o, function(){\r
7814             // restore values after effect\r
7815             r = fly(dom).getFxRestore();\r
7816             w = fly(dom).getWidth();\r
7817             h = fly(dom).getHeight();\r
7818             \r
7819             function after(){\r
7820                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   \r
7821                 fly(dom).clearOpacity();\r
7822                 fly(dom).setPositioning(r.pos);\r
7823                 st.width = r.width;\r
7824                 st.height = r.height;\r
7825                 fly(dom).afterFx(o);\r
7826             }\r
7827                 \r
7828             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {\r
7829                t  : [0, -h],\r
7830                l  : [-w, 0],\r
7831                r  : [w, 0],\r
7832                b  : [0, h],\r
7833                tl : [-w, -h],\r
7834                bl : [-w, h],\r
7835                br : [w, h],\r
7836                tr : [w, -h] \r
7837             });\r
7838                 \r
7839             arguments.callee.anim = fly(dom).fxanim(a,\r
7840                 o,\r
7841                 MOTION,\r
7842                 .5,\r
7843                 EASEOUT, after);\r
7844         });\r
7845         return me;\r
7846     },\r
7847 \r
7848     /**\r
7849      * Ensures that all effects queued after syncFx is called on the element are\r
7850      * run concurrently.  This is the opposite of {@link #sequenceFx}.\r
7851      * @return {Ext.Element} The Element\r
7852      */\r
7853     syncFx : function(){\r
7854         var me = this;\r
7855         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
7856             block : FALSE,\r
7857             concurrent : TRUE,\r
7858             stopFx : FALSE\r
7859         });\r
7860         return me;\r
7861     },\r
7862 \r
7863     /**\r
7864      * Ensures that all effects queued after sequenceFx is called on the element are\r
7865      * run in sequence.  This is the opposite of {@link #syncFx}.\r
7866      * @return {Ext.Element} The Element\r
7867      */\r
7868     sequenceFx : function(){\r
7869         var me = this;\r
7870         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
7871             block : FALSE,\r
7872             concurrent : FALSE,\r
7873             stopFx : FALSE\r
7874         });\r
7875         return me;\r
7876     },\r
7877 \r
7878     /* @private */\r
7879     nextFx : function(){        \r
7880         var ef = getQueue(this.dom.id)[0];\r
7881         if(ef){\r
7882             ef.call(this);\r
7883         }\r
7884     },\r
7885 \r
7886     /**\r
7887      * Returns true if the element has any effects actively running or queued, else returns false.\r
7888      * @return {Boolean} True if element has active effects, else false\r
7889      */\r
7890     hasActiveFx : function(){\r
7891         return getQueue(this.dom.id)[0];\r
7892     },\r
7893 \r
7894     /**\r
7895      * Stops any running effects and clears the element's internal effects queue if it contains\r
7896      * any additional effects that haven't started yet.\r
7897      * @return {Ext.Element} The Element\r
7898      */\r
7899     stopFx : function(finish){\r
7900         var me = this,\r
7901             id = me.dom.id;\r
7902         if(me.hasActiveFx()){\r
7903             var cur = getQueue(id)[0];\r
7904             if(cur && cur.anim){\r
7905                 if(cur.anim.isAnimated){\r
7906                     setQueue(id, [cur]); //clear\r
7907                     cur.anim.stop(finish !== undefined ? finish : TRUE);\r
7908                 }else{\r
7909                     setQueue(id, []);\r
7910                 }\r
7911             }\r
7912         }\r
7913         return me;\r
7914     },\r
7915 \r
7916     /* @private */\r
7917     beforeFx : function(o){\r
7918         if(this.hasActiveFx() && !o.concurrent){\r
7919            if(o.stopFx){\r
7920                this.stopFx();\r
7921                return TRUE;\r
7922            }\r
7923            return FALSE;\r
7924         }\r
7925         return TRUE;\r
7926     },\r
7927 \r
7928     /**\r
7929      * Returns true if the element is currently blocking so that no other effect can be queued\r
7930      * until this effect is finished, else returns false if blocking is not set.  This is commonly\r
7931      * used to ensure that an effect initiated by a user action runs to completion prior to the\r
7932      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).\r
7933      * @return {Boolean} True if blocking, else false\r
7934      */\r
7935     hasFxBlock : function(){\r
7936         var q = getQueue(this.dom.id);\r
7937         return q && q[0] && q[0].block;\r
7938     },\r
7939 \r
7940     /* @private */\r
7941     queueFx : function(o, fn){\r
7942         var me = this;\r
7943         if(!me.hasFxBlock()){\r
7944             Ext.applyIf(o, me.fxDefaults);\r
7945             if(!o.concurrent){\r
7946                 var run = me.beforeFx(o);\r
7947                 fn.block = o.block;\r
7948                 getQueue(me.dom.id).push(fn);\r
7949                 if(run){\r
7950                     me.nextFx();\r
7951                 }\r
7952             }else{\r
7953                 fn.call(me);\r
7954             }\r
7955         }\r
7956         return me;\r
7957     },\r
7958 \r
7959     /* @private */\r
7960     fxWrap : function(pos, o, vis){ \r
7961         var dom = this.dom,\r
7962             wrap,\r
7963             wrapXY;\r
7964         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            \r
7965             if(o.fixPosition){\r
7966                 wrapXY = fly(dom).getXY();\r
7967             }\r
7968             var div = document.createElement("div");\r
7969             div.style.visibility = vis;\r
7970             wrap = dom.parentNode.insertBefore(div, dom);\r
7971             fly(wrap).setPositioning(pos);\r
7972             if(fly(wrap).isStyle(POSITION, "static")){\r
7973                 fly(wrap).position("relative");\r
7974             }\r
7975             fly(dom).clearPositioning('auto');\r
7976             fly(wrap).clip();\r
7977             wrap.appendChild(dom);\r
7978             if(wrapXY){\r
7979                 fly(wrap).setXY(wrapXY);\r
7980             }\r
7981         }\r
7982         return wrap;\r
7983     },\r
7984 \r
7985     /* @private */\r
7986     fxUnwrap : function(wrap, pos, o){      \r
7987         var dom = this.dom;\r
7988         fly(dom).clearPositioning();\r
7989         fly(dom).setPositioning(pos);\r
7990         if(!o.wrap){\r
7991             wrap.parentNode.insertBefore(dom, wrap);\r
7992             fly(wrap).remove();\r
7993         }\r
7994     },\r
7995 \r
7996     /* @private */\r
7997     getFxRestore : function(){\r
7998         var st = this.dom.style;\r
7999         return {pos: this.getPositioning(), width: st.width, height : st.height};\r
8000     },\r
8001 \r
8002     /* @private */\r
8003     afterFx : function(o){\r
8004         var dom = this.dom,\r
8005             id = dom.id,\r
8006             notConcurrent = !o.concurrent;\r
8007         if(o.afterStyle){\r
8008             fly(dom).setStyle(o.afterStyle);            \r
8009         }\r
8010         if(o.afterCls){\r
8011             fly(dom).addClass(o.afterCls);\r
8012         }\r
8013         if(o.remove == TRUE){\r
8014             fly(dom).remove();\r
8015         }\r
8016         if(notConcurrent){\r
8017             getQueue(id).shift();\r
8018         }\r
8019         if(o.callback){\r
8020             o.callback.call(o.scope, fly(dom));\r
8021         }\r
8022         if(notConcurrent){\r
8023             fly(dom).nextFx();\r
8024         }\r
8025     },\r
8026 \r
8027     /* @private */\r
8028     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
8029         animType = animType || 'run';\r
8030         opt = opt || {};\r
8031         var anim = Ext.lib.Anim[animType](\r
8032                 this.dom, \r
8033                 args,\r
8034                 (opt.duration || defaultDur) || .35,\r
8035                 (opt.easing || defaultEase) || EASEOUT,\r
8036                 cb,            \r
8037                 this\r
8038             );\r
8039         opt.anim = anim;\r
8040         return anim;\r
8041     }\r
8042 };\r
8043 \r
8044 // backwards compat\r
8045 Ext.Fx.resize = Ext.Fx.scale;\r
8046 \r
8047 //When included, Ext.Fx is automatically applied to Element so that all basic\r
8048 //effects are available directly via the Element API\r
8049 Ext.Element.addMethods(Ext.Fx);\r
8050 })();/**\r
8051  * @class Ext.CompositeElementLite\r
8052  * Flyweight composite class. Reuses the same Ext.Element for element operations.\r
8053  <pre><code>\r
8054  var els = Ext.select("#some-el div.some-class");\r
8055  // or select directly from an existing element\r
8056  var el = Ext.get('some-el');\r
8057  el.select('div.some-class');\r
8058 \r
8059  els.setWidth(100); // all elements become 100 width\r
8060  els.hide(true); // all elements fade out and hide\r
8061  // or\r
8062  els.setWidth(100).hide(true);\r
8063  </code></pre><br><br>\r
8064  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element\r
8065  * actions will be performed on all the elements in this collection.</b>\r
8066  */\r
8067 Ext.CompositeElementLite = function(els, root){\r
8068     this.elements = [];\r
8069     this.add(els, root);\r
8070     this.el = new Ext.Element.Flyweight();\r
8071 };\r
8072 \r
8073 Ext.CompositeElementLite.prototype = {\r
8074         isComposite: true,      \r
8075         /**\r
8076      * Returns the number of elements in this composite\r
8077      * @return Number\r
8078      */\r
8079     getCount : function(){\r
8080         return this.elements.length;\r
8081     },    \r
8082         add : function(els){\r
8083         if(els){\r
8084             if (Ext.isArray(els)) {\r
8085                 this.elements = this.elements.concat(els);\r
8086             } else {\r
8087                 var yels = this.elements;                                       \r
8088                     Ext.each(els, function(e) {\r
8089                     yels.push(e);\r
8090                 });\r
8091             }\r
8092         }\r
8093         return this;\r
8094     },\r
8095     invoke : function(fn, args){\r
8096         var els = this.elements,\r
8097                 el = this.el;        \r
8098             Ext.each(els, function(e) {    \r
8099             el.dom = e;\r
8100                 Ext.Element.prototype[fn].apply(el, args);\r
8101         });\r
8102         return this;\r
8103     },\r
8104     /**\r
8105      * Returns a flyweight Element of the dom element object at the specified index\r
8106      * @param {Number} index\r
8107      * @return {Ext.Element}\r
8108      */\r
8109     item : function(index){\r
8110             var me = this;\r
8111         if(!me.elements[index]){\r
8112             return null;\r
8113         }\r
8114         me.el.dom = me.elements[index];\r
8115         return me.el;\r
8116     },\r
8117 \r
8118     // fixes scope with flyweight\r
8119     addListener : function(eventName, handler, scope, opt){\r
8120         Ext.each(this.elements, function(e) {\r
8121                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);\r
8122         });\r
8123         return this;\r
8124     },\r
8125     /**\r
8126     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element\r
8127     * passed is the flyweight (shared) Ext.Element instance, so if you require a\r
8128     * a reference to the dom node, use el.dom.</b>\r
8129     * @param {Function} fn The function to call\r
8130     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)\r
8131     * @return {CompositeElement} this\r
8132     */\r
8133     each : function(fn, scope){       \r
8134         var me = this,\r
8135                 el = me.el;\r
8136        \r
8137             Ext.each(me.elements, function(e,i) {    \r
8138             el.dom = e;\r
8139                 return fn.call(scope || el, el, me, i);\r
8140         });\r
8141         return me;\r
8142     },\r
8143     \r
8144     /**\r
8145      * Find the index of the passed element within the composite collection.\r
8146      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8147      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.\r
8148      */\r
8149     indexOf : function(el){\r
8150         return this.elements.indexOf(Ext.getDom(el));\r
8151     },\r
8152     \r
8153     /**\r
8154     * Replaces the specified element with the passed element.\r
8155     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8156     * to replace.\r
8157     * @param {Mixed} replacement The id of an element or the Element itself.\r
8158     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.\r
8159     * @return {CompositeElement} this\r
8160     */    \r
8161     replaceElement : function(el, replacement, domReplace){\r
8162         var index = !isNaN(el) ? el : this.indexOf(el),\r
8163                 d;\r
8164         if(index > -1){\r
8165             replacement = Ext.getDom(replacement);\r
8166             if(domReplace){\r
8167                 d = this.elements[index];\r
8168                 d.parentNode.insertBefore(replacement, d);\r
8169                 Ext.removeNode(d);\r
8170             }\r
8171             this.elements.splice(index, 1, replacement);\r
8172         }\r
8173         return this;\r
8174     },\r
8175     \r
8176     /**\r
8177      * Removes all elements.\r
8178      */\r
8179     clear : function(){\r
8180         this.elements = [];\r
8181     }\r
8182 };\r
8183 \r
8184 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;\r
8185 \r
8186 (function(){\r
8187 var fnName,\r
8188         ElProto = Ext.Element.prototype,\r
8189         CelProto = Ext.CompositeElementLite.prototype;\r
8190         \r
8191 for(fnName in ElProto){\r
8192     if(Ext.isFunction(ElProto[fnName])){\r
8193             (function(fnName){ \r
8194                     CelProto[fnName] = CelProto[fnName] || function(){\r
8195                         return this.invoke(fnName, arguments);\r
8196                 };\r
8197         }).call(CelProto, fnName);\r
8198         \r
8199     }\r
8200 }\r
8201 })();\r
8202 \r
8203 if(Ext.DomQuery){\r
8204     Ext.Element.selectorFunction = Ext.DomQuery.select;\r
8205\r
8206 \r
8207 /**\r
8208  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8209  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8210  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8211  * @param {String/Array} selector The CSS selector or an array of elements\r
8212  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) <b>Not supported in core</b>\r
8213  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8214  * @return {CompositeElementLite/CompositeElement}\r
8215  * @member Ext.Element\r
8216  * @method select\r
8217  */\r
8218 Ext.Element.select = function(selector, unique, root){\r
8219     var els;\r
8220     if(typeof selector == "string"){\r
8221         els = Ext.Element.selectorFunction(selector, root);\r
8222     }else if(selector.length !== undefined){\r
8223         els = selector;\r
8224     }else{\r
8225         throw "Invalid selector";\r
8226     }\r
8227     return new Ext.CompositeElementLite(els);\r
8228 };\r
8229 /**\r
8230  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8231  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8232  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8233  * @param {String/Array} selector The CSS selector or an array of elements\r
8234  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8235  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8236  * @return {CompositeElementLite/CompositeElement}\r
8237  * @member Ext\r
8238  * @method select\r
8239  */\r
8240 Ext.select = Ext.Element.select;/**\r
8241  * @class Ext.CompositeElementLite\r
8242  */\r
8243 Ext.apply(Ext.CompositeElementLite.prototype, { \r
8244         addElements : function(els, root){\r
8245         if(!els){\r
8246             return this;\r
8247         }\r
8248         if(typeof els == "string"){\r
8249             els = Ext.Element.selectorFunction(els, root);\r
8250         }\r
8251         var yels = this.elements;        \r
8252             Ext.each(els, function(e) {\r
8253                 yels.push(Ext.get(e));\r
8254         });\r
8255         return this;\r
8256     },\r
8257     \r
8258     /**\r
8259     * Clears this composite and adds the elements returned by the passed selector.\r
8260     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
8261     * @return {CompositeElement} this\r
8262     */\r
8263     fill : function(els){\r
8264         this.elements = [];\r
8265         this.add(els);\r
8266         return this;\r
8267     },\r
8268     \r
8269     /**\r
8270      * Returns the first Element\r
8271      * @return {Ext.Element}\r
8272      */\r
8273     first : function(){\r
8274         return this.item(0);\r
8275     },   \r
8276     \r
8277     /**\r
8278      * Returns the last Element\r
8279      * @return {Ext.Element}\r
8280      */\r
8281     last : function(){\r
8282         return this.item(this.getCount()-1);\r
8283     },\r
8284     \r
8285     /**\r
8286      * Returns true if this composite contains the passed element\r
8287      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8288      * @return Boolean\r
8289      */\r
8290     contains : function(el){\r
8291         return this.indexOf(el) != -1;\r
8292     },\r
8293 \r
8294     /**\r
8295     * Filters this composite to only elements that match the passed selector.\r
8296     * @param {String} selector A string CSS selector\r
8297     * @return {CompositeElement} this\r
8298     */\r
8299     filter : function(selector){\r
8300         var els = [];\r
8301         this.each(function(el){\r
8302             if(el.is(selector)){\r
8303                 els[els.length] = el.dom;\r
8304             }\r
8305         });\r
8306         this.fill(els);\r
8307         return this;\r
8308     },
8309     
8310     /**\r
8311     * Removes the specified element(s).\r
8312     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8313     * or an array of any of those.\r
8314     * @param {Boolean} removeDom (optional) True to also remove the element from the document\r
8315     * @return {CompositeElement} this\r
8316     */\r
8317     removeElement : function(keys, removeDom){\r
8318         var me = this,\r
8319                 els = this.elements,        \r
8320                 el;             \r
8321             Ext.each(keys, function(val){\r
8322                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {\r
8323                         if(removeDom){\r
8324                     if(el.dom){\r
8325                         el.remove();\r
8326                     }else{\r
8327                         Ext.removeNode(el);\r
8328                     }\r
8329                 }\r
8330                         els.splice(val, 1);                     \r
8331                         }\r
8332             });\r
8333         return this;\r
8334     }    \r
8335 });
8336 /**\r
8337  * @class Ext.CompositeElement\r
8338  * @extends Ext.CompositeElementLite\r
8339  * Standard composite class. Creates a Ext.Element for every element in the collection.\r
8340  * <br><br>\r
8341  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element\r
8342  * actions will be performed on all the elements in this collection.</b>\r
8343  * <br><br>\r
8344  * All methods return <i>this</i> and can be chained.\r
8345  <pre><code>\r
8346  var els = Ext.select("#some-el div.some-class", true);\r
8347  // or select directly from an existing element\r
8348  var el = Ext.get('some-el');\r
8349  el.select('div.some-class', true);\r
8350 \r
8351  els.setWidth(100); // all elements become 100 width\r
8352  els.hide(true); // all elements fade out and hide\r
8353  // or\r
8354  els.setWidth(100).hide(true);\r
8355  </code></pre>\r
8356  */\r
8357 Ext.CompositeElement = function(els, root){\r
8358     this.elements = [];\r
8359     this.add(els, root);\r
8360 };\r
8361 \r
8362 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {\r
8363     invoke : function(fn, args){\r
8364             Ext.each(this.elements, function(e) {\r
8365                 Ext.Element.prototype[fn].apply(e, args);\r
8366         });\r
8367         return this;\r
8368     },\r
8369     \r
8370     /**\r
8371     * Adds elements to this composite.\r
8372     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
8373     * @return {CompositeElement} this\r
8374     */\r
8375     add : function(els, root){\r
8376             if(!els){\r
8377             return this;\r
8378         }\r
8379         if(typeof els == "string"){\r
8380             els = Ext.Element.selectorFunction(els, root);\r
8381         }\r
8382         var yels = this.elements;        \r
8383             Ext.each(els, function(e) {\r
8384                 yels.push(Ext.get(e));\r
8385         });\r
8386         return this;\r
8387     },    \r
8388     \r
8389     /**\r
8390      * Returns the Element object at the specified index\r
8391      * @param {Number} index\r
8392      * @return {Ext.Element}\r
8393      */\r
8394     item : function(index){\r
8395         return this.elements[index] || null;\r
8396     },\r
8397 \r
8398 \r
8399     indexOf : function(el){\r
8400         return this.elements.indexOf(Ext.get(el));\r
8401     },\r
8402         \r
8403     filter : function(selector){\r
8404                 var me = this,\r
8405                         out = [];\r
8406                         \r
8407                 Ext.each(me.elements, function(el) {    \r
8408                         if(el.is(selector)){\r
8409                                 out.push(Ext.get(el));\r
8410                         }\r
8411                 });\r
8412                 me.elements = out;\r
8413                 return me;\r
8414         },\r
8415         \r
8416         /**\r
8417     * Calls the passed function passing (el, this, index) for each element in this composite.\r
8418     * @param {Function} fn The function to call\r
8419     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)\r
8420     * @return {CompositeElement} this\r
8421     */\r
8422     each : function(fn, scope){        \r
8423         Ext.each(this.elements, function(e,i) {\r
8424                 return fn.call(scope || e, e, this, i);\r
8425         }, this);\r
8426         return this;\r
8427     }\r
8428 });\r
8429 \r
8430 /**\r
8431  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8432  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8433  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8434  * @param {String/Array} selector The CSS selector or an array of elements\r
8435  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8436  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8437  * @return {CompositeElementLite/CompositeElement}\r
8438  * @member Ext.Element\r
8439  * @method select\r
8440  */\r
8441 Ext.Element.select = function(selector, unique, root){\r
8442     var els;\r
8443     if(typeof selector == "string"){\r
8444         els = Ext.Element.selectorFunction(selector, root);\r
8445     }else if(selector.length !== undefined){\r
8446         els = selector;\r
8447     }else{\r
8448         throw "Invalid selector";\r
8449     }\r
8450 \r
8451     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);\r
8452 };
8453 \r
8454 /**\r
8455  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8456  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8457  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8458  * @param {String/Array} selector The CSS selector or an array of elements\r
8459  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8460  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8461  * @return {CompositeElementLite/CompositeElement}\r
8462  * @member Ext.Element\r
8463  * @method select\r
8464  */\r
8465 Ext.select = Ext.Element.select;(function(){\r
8466     var BEFOREREQUEST = "beforerequest",\r
8467         REQUESTCOMPLETE = "requestcomplete",\r
8468         REQUESTEXCEPTION = "requestexception",\r
8469         UNDEFINED = undefined,\r
8470         LOAD = 'load',\r
8471         POST = 'POST',\r
8472         GET = 'GET',\r
8473         WINDOW = window;\r
8474     \r
8475     /**\r
8476      * @class Ext.data.Connection\r
8477      * @extends Ext.util.Observable\r
8478      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made\r
8479      * either to a configured URL, or to a URL specified at request time.</p>\r
8480      * <p>Requests made by this class are asynchronous, and will return immediately. No data from\r
8481      * the server will be available to the statement immediately following the {@link #request} call.\r
8482      * To process returned data, use a\r
8483      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>\r
8484      * in the request options object,\r
8485      * or an {@link #requestcomplete event listener}.</p>\r
8486      * <p><h3>File Uploads</h3><a href="#request-option-isUpload" ext:member="request-option-isUpload" ext:cls="Ext.data.Connection">File uploads</a> are not performed using normal "Ajax" techniques, that\r
8487      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard\r
8488      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its\r
8489      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
8490      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
8491      * but removed after the return data has been gathered.</p>\r
8492      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
8493      * server is using JSON to send the return object, then the\r
8494      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
8495      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
8496      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode\r
8497      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>\r
8498      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
8499      * is created containing a <tt>responseText</tt> property in order to conform to the\r
8500      * requirements of event handlers and callbacks.</p>\r
8501      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>\r
8502      * and some server technologies (notably JEE) may require some custom processing in order to\r
8503      * retrieve parameter names and parameter values from the packet content.</p>\r
8504      * @constructor\r
8505      * @param {Object} config a configuration object.\r
8506      */\r
8507     Ext.data.Connection = function(config){    \r
8508         Ext.apply(this, config);\r
8509         this.addEvents(\r
8510             /**\r
8511              * @event beforerequest\r
8512              * Fires before a network request is made to retrieve a data object.\r
8513              * @param {Connection} conn This Connection object.\r
8514              * @param {Object} options The options config object passed to the {@link #request} method.\r
8515              */\r
8516             BEFOREREQUEST,\r
8517             /**\r
8518              * @event requestcomplete\r
8519              * Fires if the request was successfully completed.\r
8520              * @param {Connection} conn This Connection object.\r
8521              * @param {Object} response The XHR object containing the response data.\r
8522              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
8523              * for details.\r
8524              * @param {Object} options The options config object passed to the {@link #request} method.\r
8525              */\r
8526             REQUESTCOMPLETE,\r
8527             /**\r
8528              * @event requestexception\r
8529              * Fires if an error HTTP status was returned from the server.\r
8530              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>\r
8531              * for details of HTTP status codes.\r
8532              * @param {Connection} conn This Connection object.\r
8533              * @param {Object} response The XHR object containing the response data.\r
8534              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
8535              * for details.\r
8536              * @param {Object} options The options config object passed to the {@link #request} method.\r
8537              */\r
8538             REQUESTEXCEPTION\r
8539         );\r
8540         Ext.data.Connection.superclass.constructor.call(this);\r
8541     };\r
8542 \r
8543     // private\r
8544     function handleResponse(response){\r
8545         this.transId = false;\r
8546         var options = response.argument.options;\r
8547         response.argument = options ? options.argument : null;\r
8548         this.fireEvent(REQUESTCOMPLETE, this, response, options);\r
8549         if(options.success){\r
8550             options.success.call(options.scope, response, options);\r
8551         }\r
8552         if(options.callback){\r
8553             options.callback.call(options.scope, options, true, response);\r
8554         }\r
8555     }\r
8556 \r
8557     // private\r
8558     function handleFailure(response, e){\r
8559         this.transId = false;\r
8560         var options = response.argument.options;\r
8561         response.argument = options ? options.argument : null;\r
8562         this.fireEvent(REQUESTEXCEPTION, this, response, options, e);\r
8563         if(options.failure){\r
8564             options.failure.call(options.scope, response, options);\r
8565         }\r
8566         if(options.callback){\r
8567             options.callback.call(options.scope, options, false, response);\r
8568         }\r
8569     }\r
8570 \r
8571     // private\r
8572     function doFormUpload(o, ps, url){\r
8573         var id = Ext.id(),\r
8574             doc = document,\r
8575             frame = doc.createElement('iframe'),\r
8576             form = Ext.getDom(o.form),\r
8577             hiddens = [],\r
8578             hd,\r
8579             encoding = 'multipart/form-data',\r
8580             buf = {\r
8581                 target: form.target,\r
8582                 method: form.method,\r
8583                 encoding: form.encoding,\r
8584                 enctype: form.enctype,\r
8585                 action: form.action\r
8586             };\r
8587             \r
8588         Ext.apply(frame, {\r
8589             id: id,\r
8590             name: id,\r
8591             className: 'x-hidden',\r
8592             src: Ext.SSL_SECURE_URL // for IE\r
8593         });     \r
8594         doc.body.appendChild(frame);\r
8595         \r
8596         // This is required so that IE doesn't pop the response up in a new window.\r
8597         if(Ext.isIE){\r
8598            document.frames[id].name = id;\r
8599         }\r
8600         \r
8601         Ext.apply(form, {\r
8602             target: id,\r
8603             method: POST,\r
8604             enctype: encoding,\r
8605             encoding: encoding,\r
8606             action: url || buf.action\r
8607         });\r
8608         \r
8609         // add dynamic params            \r
8610         ps = Ext.urlDecode(ps, false);\r
8611         for(var k in ps){\r
8612             if(ps.hasOwnProperty(k)){\r
8613                 hd = doc.createElement('input');\r
8614                 hd.type = 'hidden';                    \r
8615                 hd.value = ps[hd.name = k];\r
8616                 form.appendChild(hd);\r
8617                 hiddens.push(hd);\r
8618             }\r
8619         }        \r
8620 \r
8621         function cb(){\r
8622             var me = this,\r
8623                 // bogus response object\r
8624                 r = {responseText : '',\r
8625                      responseXML : null,\r
8626                      argument : o.argument},\r
8627                 doc,\r
8628                 firstChild;\r
8629 \r
8630             try{ \r
8631                 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;\r
8632                 if(doc){\r
8633                     if(doc.body){\r
8634                         if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea                        \r
8635                             r.responseText = firstChild.value;\r
8636                         }else{\r
8637                             r.responseText = doc.body.innerHTML;\r
8638                         }\r
8639                     }\r
8640                     //in IE the document may still have a body even if returns XML.\r
8641                     r.responseXML = doc.XMLDocument || doc;\r
8642                 }\r
8643             }\r
8644             catch(e) {}\r
8645 \r
8646             Ext.EventManager.removeListener(frame, LOAD, cb, me);\r
8647 \r
8648             me.fireEvent(REQUESTCOMPLETE, me, r, o);\r
8649             \r
8650             function runCallback(fn, scope, args){\r
8651                 if(Ext.isFunction(fn)){\r
8652                     fn.apply(scope, args);\r
8653                 }\r
8654             }\r
8655 \r
8656             runCallback(o.success, o.scope, [r, o]);\r
8657             runCallback(o.callback, o.scope, [o, true, r]);\r
8658 \r
8659             if(!me.debugUploads){\r
8660                 setTimeout(function(){Ext.removeNode(frame);}, 100);\r
8661             }\r
8662         }\r
8663 \r
8664         Ext.EventManager.on(frame, LOAD, cb, this);\r
8665         form.submit();\r
8666         \r
8667         Ext.apply(form, buf);\r
8668         Ext.each(hiddens, function(h) {\r
8669             Ext.removeNode(h);\r
8670         });\r
8671     }\r
8672 \r
8673     Ext.extend(Ext.data.Connection, Ext.util.Observable, {\r
8674         /**\r
8675          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>\r
8676          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope\r
8677          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>\r
8678          */\r
8679         /**\r
8680          * @cfg {Object} extraParams (Optional) An object containing properties which are used as\r
8681          * extra parameters to each request made by this object. (defaults to undefined)\r
8682          */\r
8683         /**\r
8684          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added\r
8685          *  to each request made by this object. (defaults to undefined)\r
8686          */\r
8687         /**\r
8688          * @cfg {String} method (Optional) The default HTTP method to be used for requests.\r
8689          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;\r
8690          * otherwise, GET will be used.)\r
8691          */\r
8692         /**\r
8693          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)\r
8694          */\r
8695         timeout : 30000,\r
8696         /**\r
8697          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)\r
8698          * @type Boolean\r
8699          */\r
8700         autoAbort:false,\r
8701     \r
8702         /**\r
8703          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)\r
8704          * @type Boolean\r
8705          */\r
8706         disableCaching: true,\r
8707         \r
8708         /**\r
8709          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching\r
8710          * through a cache buster. Defaults to '_dc'\r
8711          * @type String\r
8712          */\r
8713         disableCachingParam: '_dc',\r
8714         \r
8715         /**\r
8716          * <p>Sends an HTTP request to a remote server.</p>\r
8717          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will\r
8718          * return before the response has been received. Process any returned data\r
8719          * in a callback function.</p>\r
8720          * <pre><code>\r
8721 Ext.Ajax.request({\r
8722    url: 'ajax_demo/sample.json',\r
8723    success: function(response, opts) {\r
8724       var obj = Ext.decode(response.responseText);\r
8725       console.dir(obj);\r
8726    },\r
8727    failure: function(response, opts) {\r
8728       console.log('server-side failure with status code ' + response.status);\r
8729    }\r
8730 });\r
8731          * </code></pre>\r
8732          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>\r
8733          * @param {Object} options An object which may contain the following properties:<ul>\r
8734          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to\r
8735          * which to send the request, or a function to call which returns a URL string. The scope of the\r
8736          * function is specified by the <tt>scope</tt> option. Defaults to the configured\r
8737          * <tt>{@link #url}</tt>.</div></li>\r
8738          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">\r
8739          * An object containing properties which are used as parameters to the\r
8740          * request, a url encoded string or a function to call to get either. The scope of the function\r
8741          * is specified by the <tt>scope</tt> option.</div></li>\r
8742          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use\r
8743          * for the request. Defaults to the configured method, or if no method was configured,\r
8744          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that\r
8745          * the method name is case-sensitive and should be all caps.</div></li>\r
8746          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The\r
8747          * function to be called upon receipt of the HTTP response. The callback is\r
8748          * called regardless of success or failure and is passed the following\r
8749          * parameters:<ul>\r
8750          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8751          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>\r
8752          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data. \r
8753          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about \r
8754          * accessing elements of the response.</div></li>\r
8755          * </ul></div></li>\r
8756          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function\r
8757          * to be called upon success of the request. The callback is passed the following\r
8758          * parameters:<ul>\r
8759          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
8760          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8761          * </ul></div></li>\r
8762          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function\r
8763          * to be called upon failure of the request. The callback is passed the\r
8764          * following parameters:<ul>\r
8765          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
8766          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8767          * </ul></div></li>\r
8768          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in\r
8769          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were\r
8770          * specified as functions from which to draw values, then this also serves as the scope for those function calls.\r
8771          * Defaults to the browser window.</div></li>\r
8772          * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>\r
8773          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>\r
8774          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>\r
8775          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used \r
8776          * with the <tt>form</tt> option</b>.\r
8777          * <p>True if the form object is a file upload (will be set automatically if the form was\r
8778          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>\r
8779          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>\r
8780          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the\r
8781          * DOM <tt>&lt;form></tt> element temporarily modified to have its\r
8782          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
8783          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
8784          * but removed after the return data has been gathered.</p>\r
8785          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
8786          * server is using JSON to send the return object, then the\r
8787          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
8788          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
8789          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
8790          * is created containing a <tt>responseText</tt> property in order to conform to the\r
8791          * requirements of event handlers and callbacks.</p>\r
8792          * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>\r
8793          * and some server technologies (notably JEE) may require some custom processing in order to\r
8794          * retrieve parameter names and parameter values from the packet content.</p>\r
8795          * </div></li>\r
8796          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request\r
8797          * headers to set for the request.</div></li>\r
8798          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document\r
8799          * to use for the post. Note: This will be used instead of params for the post\r
8800          * data. Any params will be appended to the URL.</div></li>\r
8801          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON\r
8802          * data to use as the post. Note: This will be used instead of params for the post\r
8803          * data. Any params will be appended to the URL.</div></li>\r
8804          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True\r
8805          * to add a unique cache-buster param to GET requests.</div></li>\r
8806          * </ul></p>\r
8807          * <p>The options object may also contain any other property which might be needed to perform\r
8808          * postprocessing in a callback because it is passed to callback functions.</p>\r
8809          * @return {Number} transactionId The id of the server transaction. This may be used\r
8810          * to cancel the request.\r
8811          */\r
8812         request : function(o){\r
8813             var me = this;\r
8814             if(me.fireEvent(BEFOREREQUEST, me, o)){\r
8815                 if (o.el) {\r
8816                     if(!Ext.isEmpty(o.indicatorText)){\r
8817                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";\r
8818                     }\r
8819                     if(me.indicatorText) {\r
8820                         Ext.getDom(o.el).innerHTML = me.indicatorText;                        \r
8821                     }\r
8822                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {\r
8823                         Ext.getDom(o.el).innerHTML = response.responseText;\r
8824                     });\r
8825                 }\r
8826                 \r
8827                 var p = o.params,\r
8828                     url = o.url || me.url,                \r
8829                     method,\r
8830                     cb = {success: handleResponse,\r
8831                           failure: handleFailure,\r
8832                           scope: me,\r
8833                           argument: {options: o},\r
8834                           timeout : o.timeout || me.timeout\r
8835                     },\r
8836                     form,                    \r
8837                     serForm;                    \r
8838                   \r
8839                      \r
8840                 if (Ext.isFunction(p)) {\r
8841                     p = p.call(o.scope||WINDOW, o);\r
8842                 }\r
8843                                                            \r
8844                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);    \r
8845                 \r
8846                 if (Ext.isFunction(url)) {\r
8847                     url = url.call(o.scope || WINDOW, o);\r
8848                 }\r
8849     \r
8850                 if((form = Ext.getDom(o.form))){\r
8851                     url = url || form.action;\r
8852                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) { \r
8853                          return doFormUpload.call(me, o, p, url);\r
8854                      }\r
8855                     serForm = Ext.lib.Ajax.serializeForm(form);                    \r
8856                     p = p ? (p + '&' + serForm) : serForm;\r
8857                 }\r
8858                 \r
8859                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);\r
8860                 \r
8861                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){\r
8862                     var dcp = o.disableCachingParam || me.disableCachingParam;\r
8863                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));\r
8864                 }\r
8865                 \r
8866                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});\r
8867                 \r
8868                 if(o.autoAbort === true || me.autoAbort) {\r
8869                     me.abort();\r
8870                 }\r
8871                  \r
8872                 if((method == GET || o.xmlData || o.jsonData) && p){\r
8873                     url = Ext.urlAppend(url, p);  \r
8874                     p = '';\r
8875                 }\r
8876                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));\r
8877             }else{                \r
8878                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;\r
8879             }\r
8880         },\r
8881     \r
8882         /**\r
8883          * Determine whether this object has a request outstanding.\r
8884          * @param {Number} transactionId (Optional) defaults to the last transaction\r
8885          * @return {Boolean} True if there is an outstanding request.\r
8886          */\r
8887         isLoading : function(transId){\r
8888             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;            \r
8889         },\r
8890     \r
8891         /**\r
8892          * Aborts any outstanding request.\r
8893          * @param {Number} transactionId (Optional) defaults to the last transaction\r
8894          */\r
8895         abort : function(transId){\r
8896             if(transId || this.isLoading()){\r
8897                 Ext.lib.Ajax.abort(transId || this.transId);\r
8898             }\r
8899         }\r
8900     });\r
8901 })();\r
8902 \r
8903 /**\r
8904  * @class Ext.Ajax\r
8905  * @extends Ext.data.Connection\r
8906  * <p>The global Ajax request class that provides a simple way to make Ajax requests\r
8907  * with maximum flexibility.</p>\r
8908  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once\r
8909  * and override them at the request function level only if necessary.</p>\r
8910  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>\r
8911  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>\r
8912  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>\r
8913  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>\r
8914  * </ul></div>\r
8915  * <pre><code>\r
8916 // Default headers to pass in every request\r
8917 Ext.Ajax.defaultHeaders = {\r
8918     'Powered-By': 'Ext'\r
8919 };\r
8920  * </code></pre> \r
8921  * </p>\r
8922  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>\r
8923  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>\r
8924  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>\r
8925  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>\r
8926  * </ul></div>\r
8927  * <pre><code>\r
8928 // Example: show a spinner during all Ajax requests\r
8929 Ext.Ajax.on('beforerequest', this.showSpinner, this);\r
8930 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);\r
8931 Ext.Ajax.on('requestexception', this.hideSpinner, this);\r
8932  * </code></pre> \r
8933  * </p>\r
8934  * <p>An example request:</p>\r
8935  * <pre><code>\r
8936 // Basic request\r
8937 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
8938    url: 'foo.php',\r
8939    success: someFn,\r
8940    failure: otherFn,\r
8941    headers: {\r
8942        'my-header': 'foo'\r
8943    },\r
8944    params: { foo: 'bar' }\r
8945 });\r
8946 \r
8947 // Simple ajax form submission\r
8948 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
8949     form: 'some-form',\r
8950     params: 'foo=bar'\r
8951 });\r
8952  * </code></pre> \r
8953  * </p>\r
8954  * @singleton\r
8955  */\r
8956 Ext.Ajax = new Ext.data.Connection({\r
8957     /**\r
8958      * @cfg {String} url @hide\r
8959      */\r
8960     /**\r
8961      * @cfg {Object} extraParams @hide\r
8962      */\r
8963     /**\r
8964      * @cfg {Object} defaultHeaders @hide\r
8965      */\r
8966     /**\r
8967      * @cfg {String} method (Optional) @hide\r
8968      */\r
8969     /**\r
8970      * @cfg {Number} timeout (Optional) @hide\r
8971      */\r
8972     /**\r
8973      * @cfg {Boolean} autoAbort (Optional) @hide\r
8974      */\r
8975 \r
8976     /**\r
8977      * @cfg {Boolean} disableCaching (Optional) @hide\r
8978      */\r
8979 \r
8980     /**\r
8981      * @property  disableCaching\r
8982      * True to add a unique cache-buster param to GET requests. (defaults to true)\r
8983      * @type Boolean\r
8984      */\r
8985     /**\r
8986      * @property  url\r
8987      * The default URL to be used for requests to the server. (defaults to undefined)\r
8988      * If the server receives all requests through one URL, setting this once is easier than\r
8989      * entering it on every request.\r
8990      * @type String\r
8991      */\r
8992     /**\r
8993      * @property  extraParams\r
8994      * An object containing properties which are used as extra parameters to each request made\r
8995      * by this object (defaults to undefined). Session information and other data that you need\r
8996      * to pass with each request are commonly put here.\r
8997      * @type Object\r
8998      */\r
8999     /**\r
9000      * @property  defaultHeaders\r
9001      * An object containing request headers which are added to each request made by this object\r
9002      * (defaults to undefined).\r
9003      * @type Object\r
9004      */\r
9005     /**\r
9006      * @property  method\r
9007      * The default HTTP method to be used for requests. Note that this is case-sensitive and\r
9008      * should be all caps (defaults to undefined; if not set but params are present will use\r
9009      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)\r
9010      * @type String\r
9011      */\r
9012     /**\r
9013      * @property  timeout\r
9014      * The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9015      * @type Number\r
9016      */\r
9017 \r
9018     /**\r
9019      * @property  autoAbort\r
9020      * Whether a new request should abort any pending requests. (defaults to false)\r
9021      * @type Boolean\r
9022      */\r
9023     autoAbort : false,\r
9024 \r
9025     /**\r
9026      * Serialize the passed form into a url encoded string\r
9027      * @param {String/HTMLElement} form\r
9028      * @return {String}\r
9029      */\r
9030     serializeForm : function(form){\r
9031         return Ext.lib.Ajax.serializeForm(form);\r
9032     }\r
9033 });\r
9034 /**
9035  * @class Ext.Updater
9036  * @extends Ext.util.Observable
9037  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9038  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9039  * {@link Ext.Element Element} on a specific interval.<br><br>
9040  * Usage:<br>
9041  * <pre><code>
9042  * var el = Ext.get("foo"); // Get Ext.Element object
9043  * var mgr = el.getUpdater();
9044  * mgr.update({
9045         url: "http://myserver.com/index.php",
9046         params: {
9047             param1: "foo",
9048             param2: "bar"
9049         }
9050  * });
9051  * ...
9052  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9053  * <br>
9054  * // or directly (returns the same Updater instance)
9055  * var mgr = new Ext.Updater("myElementId");
9056  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9057  * mgr.on("update", myFcnNeedsToKnow);
9058  * <br>
9059  * // short handed call directly from the element object
9060  * Ext.get("foo").load({
9061         url: "bar.php",
9062         scripts: true,
9063         params: "param1=foo&amp;param2=bar",
9064         text: "Loading Foo..."
9065  * });
9066  * </code></pre>
9067  * @constructor
9068  * Create new Updater directly.
9069  * @param {Mixed} el The element to update
9070  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9071  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9072  */
9073 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable, 
9074 function() {
9075         var BEFOREUPDATE = "beforeupdate",
9076                 UPDATE = "update",
9077                 FAILURE = "failure";
9078                 
9079         // private
9080     function processSuccess(response){      
9081             var me = this;
9082         me.transaction = null;
9083         if (response.argument.form && response.argument.reset) {
9084             try { // put in try/catch since some older FF releases had problems with this
9085                 response.argument.form.reset();
9086             } catch(e){}
9087         }
9088         if (me.loadScripts) {
9089             me.renderer.render(me.el, response, me,
9090                updateComplete.createDelegate(me, [response]));
9091         } else {
9092             me.renderer.render(me.el, response, me);
9093             updateComplete.call(me, response);
9094         }
9095     }
9096     
9097     // private
9098     function updateComplete(response, type, success){
9099         this.fireEvent(type || UPDATE, this.el, response);
9100         if(Ext.isFunction(response.argument.callback)){
9101             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9102         }
9103     }
9104
9105     // private
9106     function processFailure(response){              
9107         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9108     }
9109             
9110         return {
9111             constructor: function(el, forceNew){
9112                     var me = this;
9113                 el = Ext.get(el);
9114                 if(!forceNew && el.updateManager){
9115                     return el.updateManager;
9116                 }
9117                 /**
9118                  * The Element object
9119                  * @type Ext.Element
9120                  */
9121                 me.el = el;
9122                 /**
9123                  * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9124                  * @type String
9125                  */
9126                 me.defaultUrl = null;
9127         
9128                 me.addEvents(
9129                     /**
9130                      * @event beforeupdate
9131                      * Fired before an update is made, return false from your handler and the update is cancelled.
9132                      * @param {Ext.Element} el
9133                      * @param {String/Object/Function} url
9134                      * @param {String/Object} params
9135                      */
9136                     BEFOREUPDATE,
9137                     /**
9138                      * @event update
9139                      * Fired after successful update is made.
9140                      * @param {Ext.Element} el
9141                      * @param {Object} oResponseObject The response Object
9142                      */
9143                     UPDATE,
9144                     /**
9145                      * @event failure
9146                      * Fired on update failure.
9147                      * @param {Ext.Element} el
9148                      * @param {Object} oResponseObject The response Object
9149                      */
9150                     FAILURE
9151                 );
9152         
9153                 Ext.apply(me, Ext.Updater.defaults);
9154                 /**
9155                  * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9156                  * @property sslBlankUrl
9157                  * @type String
9158                  */
9159                 /**
9160                  * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9161                  * @property disableCaching
9162                  * @type Boolean
9163                  */
9164                 /**
9165                  * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9166                  * @property indicatorText
9167                  * @type String
9168                  */
9169                 /**
9170                  * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9171                  * @property showLoadIndicator
9172                  * @type String
9173                  */
9174                 /**
9175                  * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9176                  * @property timeout
9177                  * @type Number
9178                  */
9179                 /**
9180                  * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9181                  * @property loadScripts
9182                  * @type Boolean
9183                  */
9184         
9185                 /**
9186                  * Transaction object of the current executing transaction, or null if there is no active transaction.
9187                  */
9188                 me.transaction = null;
9189                 /**
9190                  * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9191                  * @type Function
9192                  */
9193                 me.refreshDelegate = me.refresh.createDelegate(me);
9194                 /**
9195                  * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9196                  * @type Function
9197                  */
9198                 me.updateDelegate = me.update.createDelegate(me);
9199                 /**
9200                  * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9201                  * @type Function
9202                  */
9203                 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);     
9204                 
9205                         /**
9206                          * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9207                          */
9208                 me.renderer = me.renderer || me.getDefaultRenderer();
9209                 
9210                 Ext.Updater.superclass.constructor.call(me);
9211             },
9212         
9213                 /**
9214              * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9215              * @param {Object} renderer The object implementing the render() method
9216              */
9217             setRenderer : function(renderer){
9218                 this.renderer = renderer;
9219             },  
9220         
9221             /**
9222              * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9223              * @return {Object}
9224              */
9225             getRenderer : function(){
9226                return this.renderer;
9227             },
9228
9229             /**
9230              * This is an overrideable method which returns a reference to a default
9231              * renderer class if none is specified when creating the Ext.Updater.
9232              * Defaults to {@link Ext.Updater.BasicRenderer}
9233              */
9234             getDefaultRenderer: function() {
9235                 return new Ext.Updater.BasicRenderer();
9236             },
9237                 
9238             /**
9239              * Sets the default URL used for updates.
9240              * @param {String/Function} defaultUrl The url or a function to call to get the url
9241              */
9242             setDefaultUrl : function(defaultUrl){
9243                 this.defaultUrl = defaultUrl;
9244             },
9245         
9246             /**
9247              * Get the Element this Updater is bound to
9248              * @return {Ext.Element} The element
9249              */
9250             getEl : function(){
9251                 return this.el;
9252             },
9253         
9254                 /**
9255              * Performs an <b>asynchronous</b> request, updating this element with the response.
9256              * If params are specified it uses POST, otherwise it uses GET.<br><br>
9257              * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
9258              * will not have been fully updated when the function returns. To post-process the returned
9259              * data, use the callback option, or an <b><tt>update</tt></b> event handler.
9260              * @param {Object} options A config object containing any of the following options:<ul>
9261              * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
9262              * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
9263              * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
9264              * use. Defaults to POST if the <tt>params</tt> argument is present, otherwise GET.</p></li>
9265              * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
9266              * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
9267              * string, or as an object containing properties which represent parameters,
9268              * or as a function, which returns such an object.</p></li>
9269              * <li>scripts : <b>Boolean</b><p class="sub-desc">If <tt>true</tt>
9270              * any &lt;script&gt; tags embedded in the response text will be extracted
9271              * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
9272              * the callback will be called <i>after</i> the execution of the scripts.</p></li>
9273              * <li>callback : <b>Function</b><p class="sub-desc">A function to
9274              * be called when the response from the server arrives. The following
9275              * parameters are passed:<ul>
9276              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9277              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9278              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
9279              * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
9280              * </p></li>
9281              * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
9282              * to execute the callback (The callback's <tt>this</tt> reference.) If the
9283              * <tt>params</tt> argument is a function, this scope is used for that function also.</p></li>
9284              * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
9285              * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
9286              * calls.  To bypass this behavior, pass <tt>discardUrl:true</tt> (defaults to false).</p></li>
9287              * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
9288              * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
9289              * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
9290              * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
9291              * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
9292              * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
9293              * requests, this option causes an extra, auto-generated parameter to be appended to the request
9294              * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
9295              * <p>
9296              * For example:
9297         <pre><code>
9298         um.update({
9299             url: "your-url.php",
9300             params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9301             callback: yourFunction,
9302             scope: yourObject, //(optional scope)
9303             discardUrl: true,
9304             nocache: true,
9305             text: "Loading...",
9306             timeout: 60,
9307             scripts: false // Save time by avoiding RegExp execution.
9308         });
9309         </code></pre>
9310              */
9311             update : function(url, params, callback, discardUrl){
9312                     var me = this,
9313                         cfg, 
9314                         callerScope;
9315                         
9316                 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){               
9317                     if(Ext.isObject(url)){ // must be config object
9318                         cfg = url;
9319                         url = cfg.url;
9320                         params = params || cfg.params;
9321                         callback = callback || cfg.callback;
9322                         discardUrl = discardUrl || cfg.discardUrl;
9323                         callerScope = cfg.scope;                        
9324                         if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
9325                         if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
9326                         if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
9327                         if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
9328                     }
9329                     me.showLoading();
9330         
9331                     if(!discardUrl){
9332                         me.defaultUrl = url;
9333                     }
9334                     if(Ext.isFunction(url)){
9335                         url = url.call(me);
9336                     }
9337         
9338                     var o = Ext.apply({}, {
9339                         url : url,
9340                         params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
9341                         success: processSuccess,
9342                         failure: processFailure,
9343                         scope: me,
9344                         callback: undefined,
9345                         timeout: (me.timeout*1000),
9346                         disableCaching: me.disableCaching,
9347                         argument: {
9348                             "options": cfg,
9349                             "url": url,
9350                             "form": null,
9351                             "callback": callback,
9352                             "scope": callerScope || window,
9353                             "params": params
9354                         }
9355                     }, cfg);
9356         
9357                     me.transaction = Ext.Ajax.request(o);
9358                 }
9359             },          
9360
9361                 /**
9362              * <p>Performs an async form post, updating this element with the response. If the form has the attribute
9363              * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
9364              * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
9365              * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9366              * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9367              * DOM <tt>&lt;form></tt> element temporarily modified to have its
9368              * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9369              * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9370              * but removed after the return data has been gathered.</p>
9371              * <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>
9372              * and some server technologies (notably JEE) may require some custom processing in order to
9373              * retrieve parameter names and parameter values from the packet content.</p>
9374              * @param {String/HTMLElement} form The form Id or form element
9375              * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
9376              * @param {Boolean} reset (optional) Whether to try to reset the form after the update
9377              * @param {Function} callback (optional) Callback when transaction is complete. The following
9378              * parameters are passed:<ul>
9379              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9380              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9381              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
9382              */
9383             formUpdate : function(form, url, reset, callback){
9384                     var me = this;
9385                 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
9386                     if(Ext.isFunction(url)){
9387                         url = url.call(me);
9388                     }
9389                     form = Ext.getDom(form)
9390                     me.transaction = Ext.Ajax.request({
9391                         form: form,
9392                         url:url,
9393                         success: processSuccess,
9394                         failure: processFailure,
9395                         scope: me,
9396                         timeout: (me.timeout*1000),
9397                         argument: {
9398                             "url": url,
9399                             "form": form,
9400                             "callback": callback,
9401                             "reset": reset
9402                         }
9403                     });
9404                     me.showLoading.defer(1, me);
9405                 }
9406             },
9407                         
9408             /**
9409              * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
9410              * @param {Number} interval How often to update (in seconds).
9411              * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
9412              * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
9413              * the url used in a load call can be reused by this method, other load config options will not be reused and must be
9414              * sepcified as part of a config object passed as this paramter if needed.
9415              * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
9416              * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
9417              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9418              * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
9419              */
9420             startAutoRefresh : function(interval, url, params, callback, refreshNow){
9421                     var me = this;
9422                 if(refreshNow){
9423                     me.update(url || me.defaultUrl, params, callback, true);
9424                 }
9425                 if(me.autoRefreshProcId){
9426                     clearInterval(me.autoRefreshProcId);
9427                 }
9428                 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
9429             },
9430         
9431             /**
9432              * Stop auto refresh on this element.
9433              */
9434             stopAutoRefresh : function(){
9435                 if(this.autoRefreshProcId){
9436                     clearInterval(this.autoRefreshProcId);
9437                     delete this.autoRefreshProcId;
9438                 }
9439             },
9440         
9441             /**
9442              * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
9443              */
9444             isAutoRefreshing : function(){
9445                return !!this.autoRefreshProcId;
9446             },
9447         
9448             /**
9449              * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
9450              * method may be overridden to perform a custom action while this Updater is actively updating its contents.
9451              */
9452             showLoading : function(){
9453                 if(this.showLoadIndicator){
9454                 this.el.dom.innerHTML = this.indicatorText;
9455                 }
9456             },
9457         
9458             /**
9459              * Aborts the currently executing transaction, if any.
9460              */
9461             abort : function(){
9462                 if(this.transaction){
9463                     Ext.Ajax.abort(this.transaction);
9464                 }
9465             },
9466         
9467             /**
9468              * Returns true if an update is in progress, otherwise false.
9469              * @return {Boolean}
9470              */
9471             isUpdating : function(){        
9472                 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;        
9473             },
9474             
9475             /**
9476              * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
9477              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9478              */
9479             refresh : function(callback){
9480                 if(this.defaultUrl){
9481                         this.update(this.defaultUrl, null, callback, true);
9482                 }
9483             }
9484     }
9485 }());
9486
9487 /**
9488  * @class Ext.Updater.defaults
9489  * The defaults collection enables customizing the default properties of Updater
9490  */
9491 Ext.Updater.defaults = {
9492    /**
9493      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
9494      * @type Number
9495      */
9496     timeout : 30,    
9497     /**
9498      * True to append a unique parameter to GET requests to disable caching (defaults to false).
9499      * @type Boolean
9500      */
9501     disableCaching : false,
9502     /**
9503      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
9504      * @type Boolean
9505      */
9506     showLoadIndicator : true,
9507     /**
9508      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
9509      * @type String
9510      */
9511     indicatorText : '<div class="loading-indicator">Loading...</div>',
9512      /**
9513      * True to process scripts by default (defaults to false).
9514      * @type Boolean
9515      */
9516     loadScripts : false,
9517     /**
9518     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
9519     * @type String
9520     */
9521     sslBlankUrl : (Ext.SSL_SECURE_URL || "javascript:false")      
9522 };
9523
9524
9525 /**
9526  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
9527  * Usage:
9528  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
9529  * @param {Mixed} el The element to update
9530  * @param {String} url The url
9531  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
9532  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
9533  * example: {disableCaching:true, indicatorText: "Loading data..."}
9534  * @static
9535  * @deprecated
9536  * @member Ext.Updater
9537  */
9538 Ext.Updater.updateElement = function(el, url, params, options){
9539     var um = Ext.get(el).getUpdater();
9540     Ext.apply(um, options);
9541     um.update(url, params, options ? options.callback : null);
9542 };
9543
9544 /**
9545  * @class Ext.Updater.BasicRenderer
9546  * Default Content renderer. Updates the elements innerHTML with the responseText.
9547  */
9548 Ext.Updater.BasicRenderer = function(){};
9549
9550 Ext.Updater.BasicRenderer.prototype = {
9551     /**
9552      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
9553      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
9554      * create an object with a "render(el, response)" method and pass it to setRenderer on the Updater.
9555      * @param {Ext.Element} el The element being rendered
9556      * @param {Object} response The XMLHttpRequest object
9557      * @param {Updater} updateManager The calling update manager
9558      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
9559      */
9560      render : function(el, response, updateManager, callback){       
9561         el.update(response.responseText, updateManager.loadScripts, callback);
9562     }
9563 };/**
9564  * @class Date
9565  *
9566  * The date parsing and formatting syntax contains a subset of
9567  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
9568  * supported will provide results equivalent to their PHP versions.
9569  *
9570  * The following is a list of all currently supported formats:
9571  * <pre>
9572 Format  Description                                                               Example returned values
9573 ------  -----------------------------------------------------------------------   -----------------------
9574   d     Day of the month, 2 digits with leading zeros                             01 to 31
9575   D     A short textual representation of the day of the week                     Mon to Sun
9576   j     Day of the month without leading zeros                                    1 to 31
9577   l     A full textual representation of the day of the week                      Sunday to Saturday
9578   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
9579   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
9580   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
9581   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
9582   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
9583   F     A full textual representation of a month, such as January or March        January to December
9584   m     Numeric representation of a month, with leading zeros                     01 to 12
9585   M     A short textual representation of a month                                 Jan to Dec
9586   n     Numeric representation of a month, without leading zeros                  1 to 12
9587   t     Number of days in the given month                                         28 to 31
9588   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
9589   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
9590         belongs to the previous or next year, that year is used instead)
9591   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
9592   y     A two digit representation of a year                                      Examples: 99 or 03
9593   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
9594   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
9595   g     12-hour format of an hour without leading zeros                           1 to 12
9596   G     24-hour format of an hour without leading zeros                           0 to 23
9597   h     12-hour format of an hour with leading zeros                              01 to 12
9598   H     24-hour format of an hour with leading zeros                              00 to 23
9599   i     Minutes, with leading zeros                                               00 to 59
9600   s     Seconds, with leading zeros                                               00 to 59
9601   u     Decimal fraction of a second                                              Examples:
9602         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
9603                                                                                   100 (i.e. 0.100s) or
9604                                                                                   999 (i.e. 0.999s) or
9605                                                                                   999876543210 (i.e. 0.999876543210s)
9606   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
9607   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
9608   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
9609   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
9610   c     ISO 8601 date
9611         Notes:                                                                    Examples:
9612         1) If unspecified, the month / day defaults to the current month / day,   1991 or
9613            the time defaults to midnight, while the timezone defaults to the      1992-10 or
9614            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
9615            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
9616            are optional.                                                          1995-07-18T17:21:28-02:00 or
9617         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
9618            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
9619            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
9620         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
9621         date-time granularity which are supported, or see                         2000-02-13T21:25:33
9622         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
9623   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
9624   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
9625                                                                                   \/Date(1238606590509+0800)\/
9626 </pre>
9627  *
9628  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
9629  * <pre><code>
9630 // Sample date:
9631 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
9632
9633 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
9634 document.write(dt.format('Y-m-d'));                           // 2007-01-10
9635 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
9636 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
9637 </code></pre>
9638  *
9639  * Here are some standard date/time patterns that you might find helpful.  They
9640  * are not part of the source of Date.js, but to use them you can simply copy this
9641  * block of code into any script that is included after Date.js and they will also become
9642  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
9643  * <pre><code>
9644 Date.patterns = {
9645     ISO8601Long:"Y-m-d H:i:s",
9646     ISO8601Short:"Y-m-d",
9647     ShortDate: "n/j/Y",
9648     LongDate: "l, F d, Y",
9649     FullDateTime: "l, F d, Y g:i:s A",
9650     MonthDay: "F d",
9651     ShortTime: "g:i A",
9652     LongTime: "g:i:s A",
9653     SortableDateTime: "Y-m-d\\TH:i:s",
9654     UniversalSortableDateTime: "Y-m-d H:i:sO",
9655     YearMonth: "F, Y"
9656 };
9657 </code></pre>
9658  *
9659  * Example usage:
9660  * <pre><code>
9661 var dt = new Date();
9662 document.write(dt.format(Date.patterns.ShortDate));
9663 </code></pre>
9664  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
9665  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
9666  */
9667
9668 /*
9669  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
9670  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
9671  * They generate precompiled functions from format patterns instead of parsing and
9672  * processing each pattern every time a date is formatted. These functions are available
9673  * on every Date object.
9674  */
9675
9676 (function() {
9677
9678 /**
9679  * Global flag which determines if strict date parsing should be used.
9680  * Strict date parsing will not roll-over invalid dates, which is the
9681  * default behaviour of javascript Date objects.
9682  * (see {@link #parseDate} for more information)
9683  * Defaults to <tt>false</tt>.
9684  * @static
9685  * @type Boolean
9686 */
9687 Date.useStrict = false;
9688
9689
9690 // create private copy of Ext's String.format() method
9691 // - to remove unnecessary dependency
9692 // - to resolve namespace conflict with M$-Ajax's implementation
9693 function xf(format) {
9694     var args = Array.prototype.slice.call(arguments, 1);
9695     return format.replace(/\{(\d+)\}/g, function(m, i) {
9696         return args[i];
9697     });
9698 }
9699
9700
9701 // private
9702 Date.formatCodeToRegex = function(character, currentGroup) {
9703     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
9704     var p = Date.parseCodes[character];
9705
9706     if (p) {
9707       p = typeof p == 'function'? p() : p;
9708       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
9709     }
9710
9711     return p? Ext.applyIf({
9712       c: p.c? xf(p.c, currentGroup || "{0}") : p.c
9713     }, p) : {
9714         g:0,
9715         c:null,
9716         s:Ext.escapeRe(character) // treat unrecognised characters as literals
9717     }
9718 }
9719
9720 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
9721 var $f = Date.formatCodeToRegex;
9722
9723 Ext.apply(Date, {
9724     /**
9725      * <p>An object hash in which each property is a date parsing function. The property name is the
9726      * format string which that function parses.</p>
9727      * <p>This object is automatically populated with date parsing functions as
9728      * date formats are requested for Ext standard formatting strings.</p>
9729      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
9730      * may be used as a format string to {@link #parseDate}.<p>
9731      * <p>Example:</p><pre><code>
9732 Date.parseFunctions['x-date-format'] = myDateParser;
9733 </code></pre>
9734      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
9735      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
9736      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
9737      * (i.e. prevent javascript Date "rollover") (The default must be false).
9738      * Invalid date strings should return null when parsed.</div></li>
9739      * </ul></div></p>
9740      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
9741      * formatting function must be placed into the {@link #formatFunctions} property.
9742      * @property parseFunctions
9743      * @static
9744      * @type Object
9745      */
9746     parseFunctions: {
9747         "M$": function(input, strict) {
9748             // note: the timezone offset is ignored since the M$ Ajax server sends
9749             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
9750             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
9751             var r = (input || '').match(re);
9752             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
9753         }
9754     },
9755     parseRegexes: [],
9756
9757     /**
9758      * <p>An object hash in which each property is a date formatting function. The property name is the
9759      * format string which corresponds to the produced formatted date string.</p>
9760      * <p>This object is automatically populated with date formatting functions as
9761      * date formats are requested for Ext standard formatting strings.</p>
9762      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
9763      * may be used as a format string to {@link #format}. Example:</p><pre><code>
9764 Date.formatFunctions['x-date-format'] = myDateFormatter;
9765 </code></pre>
9766      * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
9767      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
9768      * </ul></div></p>
9769      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
9770      * parsing function must be placed into the {@link #parseFunctions} property.
9771      * @property formatFunctions
9772      * @static
9773      * @type Object
9774      */
9775     formatFunctions: {
9776         "M$": function() {
9777             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
9778             return '\\/Date(' + this.getTime() + ')\\/';
9779         }
9780     },
9781
9782     y2kYear : 50,
9783
9784     /**
9785      * Date interval constant
9786      * @static
9787      * @type String
9788      */
9789     MILLI : "ms",
9790
9791     /**
9792      * Date interval constant
9793      * @static
9794      * @type String
9795      */
9796     SECOND : "s",
9797
9798     /**
9799      * Date interval constant
9800      * @static
9801      * @type String
9802      */
9803     MINUTE : "mi",
9804
9805     /** Date interval constant
9806      * @static
9807      * @type String
9808      */
9809     HOUR : "h",
9810
9811     /**
9812      * Date interval constant
9813      * @static
9814      * @type String
9815      */
9816     DAY : "d",
9817
9818     /**
9819      * Date interval constant
9820      * @static
9821      * @type String
9822      */
9823     MONTH : "mo",
9824
9825     /**
9826      * Date interval constant
9827      * @static
9828      * @type String
9829      */
9830     YEAR : "y",
9831
9832     /**
9833      * <p>An object hash containing default date values used during date parsing.</p>
9834      * <p>The following properties are available:<div class="mdetail-params"><ul>
9835      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
9836      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
9837      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
9838      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
9839      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
9840      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
9841      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
9842      * </ul></div></p>
9843      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
9844      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
9845      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
9846      * It is the responsiblity of the developer to account for this.</b></p>
9847      * Example Usage:
9848      * <pre><code>
9849 // set default day value to the first day of the month
9850 Date.defaults.d = 1;
9851
9852 // parse a February date string containing only year and month values.
9853 // setting the default day value to 1 prevents weird date rollover issues
9854 // when attempting to parse the following date string on, for example, March 31st 2009.
9855 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
9856 </code></pre>
9857      * @property defaults
9858      * @static
9859      * @type Object
9860      */
9861     defaults: {},
9862
9863     /**
9864      * An array of textual day names.
9865      * Override these values for international dates.
9866      * Example:
9867      * <pre><code>
9868 Date.dayNames = [
9869     'SundayInYourLang',
9870     'MondayInYourLang',
9871     ...
9872 ];
9873 </code></pre>
9874      * @type Array
9875      * @static
9876      */
9877     dayNames : [
9878         "Sunday",
9879         "Monday",
9880         "Tuesday",
9881         "Wednesday",
9882         "Thursday",
9883         "Friday",
9884         "Saturday"
9885     ],
9886
9887     /**
9888      * An array of textual month names.
9889      * Override these values for international dates.
9890      * Example:
9891      * <pre><code>
9892 Date.monthNames = [
9893     'JanInYourLang',
9894     'FebInYourLang',
9895     ...
9896 ];
9897 </code></pre>
9898      * @type Array
9899      * @static
9900      */
9901     monthNames : [
9902         "January",
9903         "February",
9904         "March",
9905         "April",
9906         "May",
9907         "June",
9908         "July",
9909         "August",
9910         "September",
9911         "October",
9912         "November",
9913         "December"
9914     ],
9915
9916     /**
9917      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
9918      * Override these values for international dates.
9919      * Example:
9920      * <pre><code>
9921 Date.monthNumbers = {
9922     'ShortJanNameInYourLang':0,
9923     'ShortFebNameInYourLang':1,
9924     ...
9925 };
9926 </code></pre>
9927      * @type Object
9928      * @static
9929      */
9930     monthNumbers : {
9931         Jan:0,
9932         Feb:1,
9933         Mar:2,
9934         Apr:3,
9935         May:4,
9936         Jun:5,
9937         Jul:6,
9938         Aug:7,
9939         Sep:8,
9940         Oct:9,
9941         Nov:10,
9942         Dec:11
9943     },
9944
9945     /**
9946      * Get the short month name for the given month number.
9947      * Override this function for international dates.
9948      * @param {Number} month A zero-based javascript month number.
9949      * @return {String} The short month name.
9950      * @static
9951      */
9952     getShortMonthName : function(month) {
9953         return Date.monthNames[month].substring(0, 3);
9954     },
9955
9956     /**
9957      * Get the short day name for the given day number.
9958      * Override this function for international dates.
9959      * @param {Number} day A zero-based javascript day number.
9960      * @return {String} The short day name.
9961      * @static
9962      */
9963     getShortDayName : function(day) {
9964         return Date.dayNames[day].substring(0, 3);
9965     },
9966
9967     /**
9968      * Get the zero-based javascript month number for the given short/full month name.
9969      * Override this function for international dates.
9970      * @param {String} name The short/full month name.
9971      * @return {Number} The zero-based javascript month number.
9972      * @static
9973      */
9974     getMonthNumber : function(name) {
9975         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
9976         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
9977     },
9978
9979     /**
9980      * The base format-code to formatting-function hashmap used by the {@link #format} method.
9981      * Formatting functions are strings (or functions which return strings) which
9982      * will return the appropriate value when evaluated in the context of the Date object
9983      * from which the {@link #format} method is called.
9984      * Add to / override these mappings for custom date formatting.
9985      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
9986      * Example:
9987      * <pre><code>
9988 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
9989 (new Date()).format("X"); // returns the current day of the month
9990 </code></pre>
9991      * @type Object
9992      * @static
9993      */
9994     formatCodes : {
9995         d: "String.leftPad(this.getDate(), 2, '0')",
9996         D: "Date.getShortDayName(this.getDay())", // get localised short day name
9997         j: "this.getDate()",
9998         l: "Date.dayNames[this.getDay()]",
9999         N: "(this.getDay() ? this.getDay() : 7)",
10000         S: "this.getSuffix()",
10001         w: "this.getDay()",
10002         z: "this.getDayOfYear()",
10003         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10004         F: "Date.monthNames[this.getMonth()]",
10005         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10006         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10007         n: "(this.getMonth() + 1)",
10008         t: "this.getDaysInMonth()",
10009         L: "(this.isLeapYear() ? 1 : 0)",
10010         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10011         Y: "this.getFullYear()",
10012         y: "('' + this.getFullYear()).substring(2, 4)",
10013         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10014         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10015         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10016         G: "this.getHours()",
10017         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10018         H: "String.leftPad(this.getHours(), 2, '0')",
10019         i: "String.leftPad(this.getMinutes(), 2, '0')",
10020         s: "String.leftPad(this.getSeconds(), 2, '0')",
10021         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10022         O: "this.getGMTOffset()",
10023         P: "this.getGMTOffset(true)",
10024         T: "this.getTimezone()",
10025         Z: "(this.getTimezoneOffset() * -60)",
10026
10027         c: function() { // ISO-8601 -- GMT format
10028             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10029                 var e = c.charAt(i);
10030                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10031             }
10032             return code.join(" + ");
10033         },
10034         /*
10035         c: function() { // ISO-8601 -- UTC format
10036             return [
10037               "this.getUTCFullYear()", "'-'",
10038               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10039               "String.leftPad(this.getUTCDate(), 2, '0')",
10040               "'T'",
10041               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10042               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10043               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10044               "'Z'"
10045             ].join(" + ");
10046         },
10047         */
10048
10049         U: "Math.round(this.getTime() / 1000)"
10050     },
10051
10052     /**
10053      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10054      * @param {Number} year 4-digit year
10055      * @param {Number} month 1-based month-of-year
10056      * @param {Number} day Day of month
10057      * @param {Number} hour (optional) Hour
10058      * @param {Number} minute (optional) Minute
10059      * @param {Number} second (optional) Second
10060      * @param {Number} millisecond (optional) Millisecond
10061      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10062      * @static
10063      */
10064     isValid : function(y, m, d, h, i, s, ms) {
10065         // setup defaults
10066         h = h || 0;
10067         i = i || 0;
10068         s = s || 0;
10069         ms = ms || 0;
10070
10071         var dt = new Date(y, m - 1, d, h, i, s, ms);
10072
10073         return y == dt.getFullYear() &&
10074             m == dt.getMonth() + 1 &&
10075             d == dt.getDate() &&
10076             h == dt.getHours() &&
10077             i == dt.getMinutes() &&
10078             s == dt.getSeconds() &&
10079             ms == dt.getMilliseconds();
10080     },
10081
10082     /**
10083      * Parses the passed string using the specified date format.
10084      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10085      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10086      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10087      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10088      * Keep in mind that the input date string must precisely match the specified format string
10089      * in order for the parse operation to be successful (failed parse operations return a null value).
10090      * <p>Example:</p><pre><code>
10091 //dt = Fri May 25 2007 (current date)
10092 var dt = new Date();
10093
10094 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10095 dt = Date.parseDate("2006", "Y");
10096
10097 //dt = Sun Jan 15 2006 (all date parts specified)
10098 dt = Date.parseDate("2006-01-15", "Y-m-d");
10099
10100 //dt = Sun Jan 15 2006 15:20:01
10101 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10102
10103 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10104 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10105 </code></pre>
10106      * @param {String} input The raw date string.
10107      * @param {String} format The expected date string format.
10108      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10109                         (defaults to false). Invalid date strings will return null when parsed.
10110      * @return {Date} The parsed Date.
10111      * @static
10112      */
10113     parseDate : function(input, format, strict) {
10114         var p = Date.parseFunctions;
10115         if (p[format] == null) {
10116             Date.createParser(format);
10117         }
10118         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10119     },
10120
10121     // private
10122     getFormatCode : function(character) {
10123         var f = Date.formatCodes[character];
10124
10125         if (f) {
10126           f = typeof f == 'function'? f() : f;
10127           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10128         }
10129
10130         // note: unknown characters are treated as literals
10131         return f || ("'" + String.escape(character) + "'");
10132     },
10133
10134     // private
10135     createFormat : function(format) {
10136         var code = [],
10137             special = false,
10138             ch = '';
10139
10140         for (var i = 0; i < format.length; ++i) {
10141             ch = format.charAt(i);
10142             if (!special && ch == "\\") {
10143                 special = true;
10144             } else if (special) {
10145                 special = false;
10146                 code.push("'" + String.escape(ch) + "'");
10147             } else {
10148                 code.push(Date.getFormatCode(ch))
10149             }
10150         }
10151         Date.formatFunctions[format] = new Function("return " + code.join('+'));
10152     },
10153
10154     // private
10155     createParser : function() {
10156         var code = [
10157             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10158                 "def = Date.defaults,",
10159                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10160
10161             "if(results){",
10162                 "{1}",
10163
10164                 "if(u != null){", // i.e. unix time is defined
10165                     "v = new Date(u * 1000);", // give top priority to UNIX time
10166                 "}else{",
10167                     // create Date object representing midnight of the current day;
10168                     // this will provide us with our date defaults
10169                     // (note: clearTime() handles Daylight Saving Time automatically)
10170                     "dt = (new Date()).clearTime();",
10171
10172                     // date calculations (note: these calculations create a dependency on Ext.num())
10173                     "y = y >= 0? y : Ext.num(def.y, dt.getFullYear());",
10174                     "m = m >= 0? m : Ext.num(def.m - 1, dt.getMonth());",
10175                     "d = d >= 0? d : Ext.num(def.d, dt.getDate());",
10176
10177                     // time calculations (note: these calculations create a dependency on Ext.num())
10178                     "h  = h || Ext.num(def.h, dt.getHours());",
10179                     "i  = i || Ext.num(def.i, dt.getMinutes());",
10180                     "s  = s || Ext.num(def.s, dt.getSeconds());",
10181                     "ms = ms || Ext.num(def.ms, dt.getMilliseconds());",
10182
10183                     "if(z >= 0 && y >= 0){",
10184                         // both the year and zero-based day of year are defined and >= 0.
10185                         // these 2 values alone provide sufficient info to create a full date object
10186
10187                         // create Date object representing January 1st for the given year
10188                         "v = new Date(y, 0, 1, h, i, s, ms);",
10189
10190                         // then add day of year, checking for Date "rollover" if necessary
10191                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10192                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10193                         "v = null;", // invalid date, so return null
10194                     "}else{",
10195                         // plain old Date object
10196                         "v = new Date(y, m, d, h, i, s, ms);",
10197                     "}",
10198                 "}",
10199             "}",
10200
10201             "if(v){",
10202                 // favour UTC offset over GMT offset
10203                 "if(zz != null){",
10204                     // reset to UTC, then add offset
10205                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10206                 "}else if(o){",
10207                     // reset to GMT, then add offset
10208                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10209                 "}",
10210             "}",
10211
10212             "return v;"
10213         ].join('\n');
10214
10215         return function(format) {
10216             var regexNum = Date.parseRegexes.length,
10217                 currentGroup = 1,
10218                 calc = [],
10219                 regex = [],
10220                 special = false,
10221                 ch = "";
10222
10223             for (var i = 0; i < format.length; ++i) {
10224                 ch = format.charAt(i);
10225                 if (!special && ch == "\\") {
10226                     special = true;
10227                 } else if (special) {
10228                     special = false;
10229                     regex.push(String.escape(ch));
10230                 } else {
10231                     var obj = $f(ch, currentGroup);
10232                     currentGroup += obj.g;
10233                     regex.push(obj.s);
10234                     if (obj.g && obj.c) {
10235                         calc.push(obj.c);
10236                     }
10237                 }
10238             }
10239
10240             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10241             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10242         }
10243     }(),
10244
10245     // private
10246     parseCodes : {
10247         /*
10248          * Notes:
10249          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10250          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10251          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
10252          */
10253         d: {
10254             g:1,
10255             c:"d = parseInt(results[{0}], 10);\n",
10256             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
10257         },
10258         j: {
10259             g:1,
10260             c:"d = parseInt(results[{0}], 10);\n",
10261             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
10262         },
10263         D: function() {
10264             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
10265             return {
10266                 g:0,
10267                 c:null,
10268                 s:"(?:" + a.join("|") +")"
10269             }
10270         },
10271         l: function() {
10272             return {
10273                 g:0,
10274                 c:null,
10275                 s:"(?:" + Date.dayNames.join("|") + ")"
10276             }
10277         },
10278         N: {
10279             g:0,
10280             c:null,
10281             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
10282         },
10283         S: {
10284             g:0,
10285             c:null,
10286             s:"(?:st|nd|rd|th)"
10287         },
10288         w: {
10289             g:0,
10290             c:null,
10291             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
10292         },
10293         z: {
10294             g:1,
10295             c:"z = parseInt(results[{0}], 10);\n",
10296             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
10297         },
10298         W: {
10299             g:0,
10300             c:null,
10301             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
10302         },
10303         F: function() {
10304             return {
10305                 g:1,
10306                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
10307                 s:"(" + Date.monthNames.join("|") + ")"
10308             }
10309         },
10310         M: function() {
10311             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
10312             return Ext.applyIf({
10313                 s:"(" + a.join("|") + ")"
10314             }, $f("F"));
10315         },
10316         m: {
10317             g:1,
10318             c:"m = parseInt(results[{0}], 10) - 1;\n",
10319             s:"(\\d{2})" // month number with leading zeros (01 - 12)
10320         },
10321         n: {
10322             g:1,
10323             c:"m = parseInt(results[{0}], 10) - 1;\n",
10324             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
10325         },
10326         t: {
10327             g:0,
10328             c:null,
10329             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
10330         },
10331         L: {
10332             g:0,
10333             c:null,
10334             s:"(?:1|0)"
10335         },
10336         o: function() {
10337             return $f("Y");
10338         },
10339         Y: {
10340             g:1,
10341             c:"y = parseInt(results[{0}], 10);\n",
10342             s:"(\\d{4})" // 4-digit year
10343         },
10344         y: {
10345             g:1,
10346             c:"var ty = parseInt(results[{0}], 10);\n"
10347                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
10348             s:"(\\d{1,2})"
10349         },
10350         a: {
10351             g:1,
10352             c:"if (results[{0}] == 'am') {\n"
10353                 + "if (h == 12) { h = 0; }\n"
10354                 + "} else { if (h < 12) { h += 12; }}",
10355             s:"(am|pm)"
10356         },
10357         A: {
10358             g:1,
10359             c:"if (results[{0}] == 'AM') {\n"
10360                 + "if (h == 12) { h = 0; }\n"
10361                 + "} else { if (h < 12) { h += 12; }}",
10362             s:"(AM|PM)"
10363         },
10364         g: function() {
10365             return $f("G");
10366         },
10367         G: {
10368             g:1,
10369             c:"h = parseInt(results[{0}], 10);\n",
10370             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
10371         },
10372         h: function() {
10373             return $f("H");
10374         },
10375         H: {
10376             g:1,
10377             c:"h = parseInt(results[{0}], 10);\n",
10378             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
10379         },
10380         i: {
10381             g:1,
10382             c:"i = parseInt(results[{0}], 10);\n",
10383             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
10384         },
10385         s: {
10386             g:1,
10387             c:"s = parseInt(results[{0}], 10);\n",
10388             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
10389         },
10390         u: {
10391             g:1,
10392             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
10393             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
10394         },
10395         O: {
10396             g:1,
10397             c:[
10398                 "o = results[{0}];",
10399                 "var sn = o.substring(0,1),", // get + / - sign
10400                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10401                     "mn = o.substring(3,5) % 60;", // get minutes
10402                 "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
10403             ].join("\n"),
10404             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
10405         },
10406         P: {
10407             g:1,
10408             c:[
10409                 "o = results[{0}];",
10410                 "var sn = o.substring(0,1),", // get + / - sign
10411                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10412                     "mn = o.substring(4,6) % 60;", // get minutes
10413                 "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
10414             ].join("\n"),
10415             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
10416         },
10417         T: {
10418             g:0,
10419             c:null,
10420             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
10421         },
10422         Z: {
10423             g:1,
10424             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
10425                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
10426             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
10427         },
10428         c: function() {
10429             var calc = [],
10430                 arr = [
10431                     $f("Y", 1), // year
10432                     $f("m", 2), // month
10433                     $f("d", 3), // day
10434                     $f("h", 4), // hour
10435                     $f("i", 5), // minute
10436                     $f("s", 6), // second
10437                     {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)
10438                     {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
10439                         "if(results[8]) {", // timezone specified
10440                             "if(results[8] == 'Z'){",
10441                                 "zz = 0;", // UTC
10442                             "}else if (results[8].indexOf(':') > -1){",
10443                                 $f("P", 8).c, // timezone offset with colon separator
10444                             "}else{",
10445                                 $f("O", 8).c, // timezone offset without colon separator
10446                             "}",
10447                         "}"
10448                     ].join('\n')}
10449                 ];
10450
10451             for (var i = 0, l = arr.length; i < l; ++i) {
10452                 calc.push(arr[i].c);
10453             }
10454
10455             return {
10456                 g:1,
10457                 c:calc.join(""),
10458                 s:[
10459                     arr[0].s, // year (required)
10460                     "(?:", "-", arr[1].s, // month (optional)
10461                         "(?:", "-", arr[2].s, // day (optional)
10462                             "(?:",
10463                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
10464                                 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
10465                                 "(?::", arr[5].s, ")?", // seconds (optional)
10466                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
10467                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
10468                             ")?",
10469                         ")?",
10470                     ")?"
10471                 ].join("")
10472             }
10473         },
10474         U: {
10475             g:1,
10476             c:"u = parseInt(results[{0}], 10);\n",
10477             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
10478         }
10479     }
10480 });
10481
10482 }());
10483
10484 Ext.apply(Date.prototype, {
10485     // private
10486     dateFormat : function(format) {
10487         if (Date.formatFunctions[format] == null) {
10488             Date.createFormat(format);
10489         }
10490         return Date.formatFunctions[format].call(this);
10491     },
10492
10493     /**
10494      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
10495      *
10496      * Note: The date string returned by the javascript Date object's toString() method varies
10497      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
10498      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
10499      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
10500      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
10501      * from the GMT offset portion of the date string.
10502      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
10503      */
10504     getTimezone : function() {
10505         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
10506         //
10507         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
10508         // 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)
10509         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
10510         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
10511         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
10512         //
10513         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
10514         // step 1: (?:\((.*)\) -- find timezone in parentheses
10515         // 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
10516         // step 3: remove all non uppercase characters found in step 1 and 2
10517         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
10518     },
10519
10520     /**
10521      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
10522      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
10523      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
10524      */
10525     getGMTOffset : function(colon) {
10526         return (this.getTimezoneOffset() > 0 ? "-" : "+")
10527             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
10528             + (colon ? ":" : "")
10529             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
10530     },
10531
10532     /**
10533      * Get the numeric day number of the year, adjusted for leap year.
10534      * @return {Number} 0 to 364 (365 in leap years).
10535      */
10536     getDayOfYear: function() {
10537         var i = 0,
10538             num = 0,
10539             d = this.clone(),
10540             m = this.getMonth();
10541
10542         for (i = 0, d.setMonth(0); i < m; d.setMonth(++i)) {
10543             num += d.getDaysInMonth();
10544         }
10545         return num + this.getDate() - 1;
10546     },
10547
10548     /**
10549      * Get the numeric ISO-8601 week number of the year.
10550      * (equivalent to the format specifier 'W', but without a leading zero).
10551      * @return {Number} 1 to 53
10552      */
10553     getWeekOfYear : function() {
10554         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
10555         var ms1d = 864e5, // milliseconds in a day
10556             ms7d = 7 * ms1d; // milliseconds in a week
10557
10558         return function() { // return a closure so constants get calculated only once
10559             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
10560                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
10561                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
10562
10563             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
10564         }
10565     }(),
10566
10567     /**
10568      * Checks if the current date falls within a leap year.
10569      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
10570      */
10571     isLeapYear : function() {
10572         var year = this.getFullYear();
10573         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
10574     },
10575
10576     /**
10577      * Get the first day of the current month, adjusted for leap year.  The returned value
10578      * is the numeric day index within the week (0-6) which can be used in conjunction with
10579      * the {@link #monthNames} array to retrieve the textual day name.
10580      * Example:
10581      * <pre><code>
10582 var dt = new Date('1/10/2007');
10583 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
10584 </code></pre>
10585      * @return {Number} The day number (0-6).
10586      */
10587     getFirstDayOfMonth : function() {
10588         var day = (this.getDay() - (this.getDate() - 1)) % 7;
10589         return (day < 0) ? (day + 7) : day;
10590     },
10591
10592     /**
10593      * Get the last day of the current month, adjusted for leap year.  The returned value
10594      * is the numeric day index within the week (0-6) which can be used in conjunction with
10595      * the {@link #monthNames} array to retrieve the textual day name.
10596      * Example:
10597      * <pre><code>
10598 var dt = new Date('1/10/2007');
10599 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
10600 </code></pre>
10601      * @return {Number} The day number (0-6).
10602      */
10603     getLastDayOfMonth : function() {
10604         return this.getLastDateOfMonth().getDay();
10605     },
10606
10607
10608     /**
10609      * Get the date of the first day of the month in which this date resides.
10610      * @return {Date}
10611      */
10612     getFirstDateOfMonth : function() {
10613         return new Date(this.getFullYear(), this.getMonth(), 1);
10614     },
10615
10616     /**
10617      * Get the date of the last day of the month in which this date resides.
10618      * @return {Date}
10619      */
10620     getLastDateOfMonth : function() {
10621         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
10622     },
10623
10624     /**
10625      * Get the number of days in the current month, adjusted for leap year.
10626      * @return {Number} The number of days in the month.
10627      */
10628     getDaysInMonth: function() {
10629         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
10630
10631         return function() { // return a closure for efficiency
10632             var m = this.getMonth();
10633
10634             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
10635         }
10636     }(),
10637
10638     /**
10639      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
10640      * @return {String} 'st, 'nd', 'rd' or 'th'.
10641      */
10642     getSuffix : function() {
10643         switch (this.getDate()) {
10644             case 1:
10645             case 21:
10646             case 31:
10647                 return "st";
10648             case 2:
10649             case 22:
10650                 return "nd";
10651             case 3:
10652             case 23:
10653                 return "rd";
10654             default:
10655                 return "th";
10656         }
10657     },
10658
10659     /**
10660      * Creates and returns a new Date instance with the exact same date value as the called instance.
10661      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
10662      * variable will also be changed.  When the intention is to create a new variable that will not
10663      * modify the original instance, you should create a clone.
10664      *
10665      * Example of correctly cloning a date:
10666      * <pre><code>
10667 //wrong way:
10668 var orig = new Date('10/1/2006');
10669 var copy = orig;
10670 copy.setDate(5);
10671 document.write(orig);  //returns 'Thu Oct 05 2006'!
10672
10673 //correct way:
10674 var orig = new Date('10/1/2006');
10675 var copy = orig.clone();
10676 copy.setDate(5);
10677 document.write(orig);  //returns 'Thu Oct 01 2006'
10678 </code></pre>
10679      * @return {Date} The new Date instance.
10680      */
10681     clone : function() {
10682         return new Date(this.getTime());
10683     },
10684
10685     /**
10686      * Checks if the current date is affected by Daylight Saving Time (DST).
10687      * @return {Boolean} True if the current date is affected by DST.
10688      */
10689     isDST : function() {
10690         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
10691         // courtesy of @geoffrey.mcgill
10692         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
10693     },
10694
10695     /**
10696      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
10697      * automatically adjusting for Daylight Saving Time (DST) where applicable.
10698      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
10699      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
10700      * @return {Date} this or the clone.
10701      */
10702     clearTime : function(clone) {
10703         if (clone) {
10704             return this.clone().clearTime();
10705         }
10706
10707         // get current date before clearing time
10708         var d = this.getDate();
10709
10710         // clear time
10711         this.setHours(0);
10712         this.setMinutes(0);
10713         this.setSeconds(0);
10714         this.setMilliseconds(0);
10715
10716         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
10717             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
10718             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
10719
10720             // increment hour until cloned date == current date
10721             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
10722
10723             this.setDate(d);
10724             this.setHours(c.getHours());
10725         }
10726
10727         return this;
10728     },
10729
10730     /**
10731      * Provides a convenient method for performing basic date arithmetic. This method
10732      * does not modify the Date instance being called - it creates and returns
10733      * a new Date instance containing the resulting date value.
10734      *
10735      * Examples:
10736      * <pre><code>
10737 // Basic usage:
10738 var dt = new Date('10/29/2006').add(Date.DAY, 5);
10739 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
10740
10741 // Negative values will be subtracted:
10742 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
10743 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
10744
10745 // You can even chain several calls together in one line:
10746 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
10747 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
10748 </code></pre>
10749      *
10750      * @param {String} interval A valid date interval enum value.
10751      * @param {Number} value The amount to add to the current date.
10752      * @return {Date} The new Date instance.
10753      */
10754     add : function(interval, value) {
10755         var d = this.clone();
10756         if (!interval || value === 0) return d;
10757
10758         switch(interval.toLowerCase()) {
10759             case Date.MILLI:
10760                 d.setMilliseconds(this.getMilliseconds() + value);
10761                 break;
10762             case Date.SECOND:
10763                 d.setSeconds(this.getSeconds() + value);
10764                 break;
10765             case Date.MINUTE:
10766                 d.setMinutes(this.getMinutes() + value);
10767                 break;
10768             case Date.HOUR:
10769                 d.setHours(this.getHours() + value);
10770                 break;
10771             case Date.DAY:
10772                 d.setDate(this.getDate() + value);
10773                 break;
10774             case Date.MONTH:
10775                 var day = this.getDate();
10776                 if (day > 28) {
10777                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
10778                 }
10779                 d.setDate(day);
10780                 d.setMonth(this.getMonth() + value);
10781                 break;
10782             case Date.YEAR:
10783                 d.setFullYear(this.getFullYear() + value);
10784                 break;
10785         }
10786         return d;
10787     },
10788
10789     /**
10790      * Checks if this date falls on or between the given start and end dates.
10791      * @param {Date} start Start date
10792      * @param {Date} end End date
10793      * @return {Boolean} true if this date falls on or between the given start and end dates.
10794      */
10795     between : function(start, end) {
10796         var t = this.getTime();
10797         return start.getTime() <= t && t <= end.getTime();
10798     }
10799 });
10800
10801
10802 /**
10803  * Formats a date given the supplied format string.
10804  * @param {String} format The format string.
10805  * @return {String} The formatted date.
10806  * @method format
10807  */
10808 Date.prototype.format = Date.prototype.dateFormat;
10809
10810
10811 // private
10812 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
10813     Ext.apply(Date.prototype, {
10814         _xMonth : Date.prototype.setMonth,
10815         _xDate  : Date.prototype.setDate,
10816
10817         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
10818         // Date.setMonth does not work consistently if iMonth is not 0-11
10819         setMonth : function(num) {
10820             if (num <= -1) {
10821                 var n = Math.ceil(-num),
10822                     back_year = Math.ceil(n / 12),
10823                     month = (n % 12) ? 12 - n % 12 : 0;
10824
10825                 this.setFullYear(this.getFullYear() - back_year);
10826
10827                 return this._xMonth(month);
10828             } else {
10829                 return this._xMonth(num);
10830             }
10831         },
10832
10833         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
10834         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
10835         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
10836         setDate : function(d) {
10837             // use setTime() to workaround setDate() bug
10838             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
10839             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
10840         }
10841     });
10842 }
10843
10844
10845
10846 /* Some basic Date tests... (requires Firebug)
10847
10848 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
10849 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
10850
10851 // standard tests
10852 console.group('Standard Date.parseDate() Tests');
10853     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
10854     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
10855     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
10856     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
10857     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
10858 console.groupEnd();
10859
10860 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
10861 // -- accepts ALL 6 levels of date-time granularity
10862 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
10863     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
10864     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
10865     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
10866     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)
10867     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)
10868     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)
10869     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)
10870     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
10871 console.groupEnd();
10872
10873 //*//**
10874  * @class Ext.util.DelayedTask
10875  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
10876  * performing setTimeout where a new timeout cancels the old timeout. When called, the
10877  * task will wait the specified time period before executing. If durng that time period,
10878  * the task is called again, the original call will be cancelled. This continues so that
10879  * the function is only called a single time for each iteration.</p>
10880  * <p>This method is especially useful for things like detecting whether a user has finished
10881  * typing in a text field. An example would be performing validation on a keypress. You can
10882  * use this class to buffer the keypress events for a certain number of milliseconds, and
10883  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
10884 var task = new Ext.util.DelayedTask(function(){
10885     alert(Ext.getDom('myInputField').value.length);
10886 });
10887 // Wait 500ms before calling our function. If the user presses another key 
10888 // during that 500ms, it will be cancelled and we'll wait another 500ms.
10889 Ext.get('myInputField').on('keypress', function(){
10890     task.{@link #delay}(500); 
10891 });
10892  * </code></pre> 
10893  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
10894  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
10895  * also setup a delayed task for you to buffer events.</p> 
10896  * @constructor The parameters to this constructor serve as defaults and are not required.
10897  * @param {Function} fn (optional) The default function to timeout
10898  * @param {Object} scope (optional) The default scope of that timeout
10899  * @param {Array} args (optional) The default Array of arguments
10900  */
10901 Ext.util.DelayedTask = function(fn, scope, args){
10902     var me = this,
10903         id,     
10904         call = function(){
10905                 clearInterval(id);
10906                 id = null;
10907                 fn.apply(scope, args || []);
10908             };
10909             
10910     /**
10911      * Cancels any pending timeout and queues a new one
10912      * @param {Number} delay The milliseconds to delay
10913      * @param {Function} newFn (optional) Overrides function passed to constructor
10914      * @param {Object} newScope (optional) Overrides scope passed to constructor
10915      * @param {Array} newArgs (optional) Overrides args passed to constructor
10916      */
10917     me.delay = function(delay, newFn, newScope, newArgs){
10918         me.cancel();
10919         fn = newFn || fn;
10920         scope = newScope || scope;
10921         args = newArgs || args;
10922         id = setInterval(call, delay);
10923     };
10924
10925     /**
10926      * Cancel the last queued timeout
10927      */
10928     me.cancel = function(){
10929         if(id){
10930             clearInterval(id);
10931             id = null;
10932         }
10933     };
10934 };/**\r
10935  * @class Ext.util.MixedCollection\r
10936  * @extends Ext.util.Observable\r
10937  * A Collection class that maintains both numeric indexes and keys and exposes events.\r
10938  * @constructor\r
10939  * @param {Boolean} allowFunctions True if the addAll function should add function references to the\r
10940  * collection (defaults to false)\r
10941  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection\r
10942  * and return the key value for that item.  This is used when available to look up the key on items that\r
10943  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is\r
10944  * equivalent to providing an implementation for the {@link #getKey} method.\r
10945  */\r
10946 Ext.util.MixedCollection = function(allowFunctions, keyFn){\r
10947     this.items = [];\r
10948     this.map = {};\r
10949     this.keys = [];\r
10950     this.length = 0;\r
10951     this.addEvents(\r
10952         /**\r
10953          * @event clear\r
10954          * Fires when the collection is cleared.\r
10955          */\r
10956         "clear",\r
10957         /**\r
10958          * @event add\r
10959          * Fires when an item is added to the collection.\r
10960          * @param {Number} index The index at which the item was added.\r
10961          * @param {Object} o The item added.\r
10962          * @param {String} key The key associated with the added item.\r
10963          */\r
10964         "add",\r
10965         /**\r
10966          * @event replace\r
10967          * Fires when an item is replaced in the collection.\r
10968          * @param {String} key he key associated with the new added.\r
10969          * @param {Object} old The item being replaced.\r
10970          * @param {Object} new The new item.\r
10971          */\r
10972         "replace",\r
10973         /**\r
10974          * @event remove\r
10975          * Fires when an item is removed from the collection.\r
10976          * @param {Object} o The item being removed.\r
10977          * @param {String} key (optional) The key associated with the removed item.\r
10978          */\r
10979         "remove",\r
10980         "sort"\r
10981     );\r
10982     this.allowFunctions = allowFunctions === true;\r
10983     if(keyFn){\r
10984         this.getKey = keyFn;\r
10985     }\r
10986     Ext.util.MixedCollection.superclass.constructor.call(this);\r
10987 };\r
10988 \r
10989 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {\r
10990     allowFunctions : false,\r
10991 \r
10992 /**\r
10993  * Adds an item to the collection. Fires the {@link #add} event when complete.\r
10994  * @param {String} key <p>The key to associate with the item, or the new item.</p>\r
10995  * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key\r
10996  * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection\r
10997  * will be able to <i>derive</i> the key for the new item. In this case just pass the new item in\r
10998  * this parameter.</p>\r
10999  * @param {Object} o The item to add.\r
11000  * @return {Object} The item added.\r
11001  */\r
11002     add: function(key, o){\r
11003         if(arguments.length == 1){\r
11004             o = arguments[0];\r
11005             key = this.getKey(o);\r
11006         }\r
11007         if(typeof key != 'undefined' && key !== null){\r
11008             var old = this.map[key];\r
11009             if(typeof old != 'undefined'){\r
11010                 return this.replace(key, o);\r
11011             }\r
11012             this.map[key] = o;\r
11013         }\r
11014         this.length++;\r
11015         this.items.push(o);\r
11016         this.keys.push(key);\r
11017         this.fireEvent('add', this.length-1, o, key);\r
11018         return o;\r
11019     },\r
11020 \r
11021 /**\r
11022   * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation\r
11023   * simply returns <tt style="font-weight:bold;">item.id</tt> but you can provide your own implementation\r
11024   * to return a different value as in the following examples:\r
11025 <pre><code>\r
11026 // normal way\r
11027 var mc = new Ext.util.MixedCollection();\r
11028 mc.add(someEl.dom.id, someEl);\r
11029 mc.add(otherEl.dom.id, otherEl);\r
11030 //and so on\r
11031 \r
11032 // using getKey\r
11033 var mc = new Ext.util.MixedCollection();\r
11034 mc.getKey = function(el){\r
11035    return el.dom.id;\r
11036 };\r
11037 mc.add(someEl);\r
11038 mc.add(otherEl);\r
11039 \r
11040 // or via the constructor\r
11041 var mc = new Ext.util.MixedCollection(false, function(el){\r
11042    return el.dom.id;\r
11043 });\r
11044 mc.add(someEl);\r
11045 mc.add(otherEl);\r
11046 </code></pre>\r
11047  * @param {Object} item The item for which to find the key.\r
11048  * @return {Object} The key for the passed item.\r
11049  */\r
11050     getKey : function(o){\r
11051          return o.id;\r
11052     },\r
11053 \r
11054 /**\r
11055  * Replaces an item in the collection. Fires the {@link #replace} event when complete.\r
11056  * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>\r
11057  * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key\r
11058  * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection\r
11059  * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item\r
11060  * with one having the same key value, then just pass the replacement item in this parameter.</p>\r
11061  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate\r
11062  * with that key.\r
11063  * @return {Object}  The new item.\r
11064  */\r
11065     replace : function(key, o){\r
11066         if(arguments.length == 1){\r
11067             o = arguments[0];\r
11068             key = this.getKey(o);\r
11069         }\r
11070         var old = this.map[key];\r
11071         if(typeof key == "undefined" || key === null || typeof old == "undefined"){\r
11072              return this.add(key, o);\r
11073         }\r
11074         var index = this.indexOfKey(key);\r
11075         this.items[index] = o;\r
11076         this.map[key] = o;\r
11077         this.fireEvent("replace", key, old, o);\r
11078         return o;\r
11079     },\r
11080 \r
11081 /**\r
11082  * Adds all elements of an Array or an Object to the collection.\r
11083  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or\r
11084  * an Array of values, each of which are added to the collection.\r
11085  */\r
11086     addAll : function(objs){\r
11087         if(arguments.length > 1 || Ext.isArray(objs)){\r
11088             var args = arguments.length > 1 ? arguments : objs;\r
11089             for(var i = 0, len = args.length; i < len; i++){\r
11090                 this.add(args[i]);\r
11091             }\r
11092         }else{\r
11093             for(var key in objs){\r
11094                 if(this.allowFunctions || typeof objs[key] != "function"){\r
11095                     this.add(key, objs[key]);\r
11096                 }\r
11097             }\r
11098         }\r
11099     },\r
11100 \r
11101 /**\r
11102  * Executes the specified function once for every item in the collection, passing the following arguments:\r
11103  * <div class="mdetail-params"><ul>\r
11104  * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>\r
11105  * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>\r
11106  * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>\r
11107  * </ul></div>\r
11108  * The function should return a boolean value. Returning false from the function will stop the iteration.\r
11109  * @param {Function} fn The function to execute for each item.\r
11110  * @param {Object} scope (optional) The scope in which to execute the function.\r
11111  */\r
11112     each : function(fn, scope){\r
11113         var items = [].concat(this.items); // each safe for removal\r
11114         for(var i = 0, len = items.length; i < len; i++){\r
11115             if(fn.call(scope || items[i], items[i], i, len) === false){\r
11116                 break;\r
11117             }\r
11118         }\r
11119     },\r
11120 \r
11121 /**\r
11122  * Executes the specified function once for every key in the collection, passing each\r
11123  * key, and its associated item as the first two parameters.\r
11124  * @param {Function} fn The function to execute for each item.\r
11125  * @param {Object} scope (optional) The scope in which to execute the function.\r
11126  */\r
11127     eachKey : function(fn, scope){\r
11128         for(var i = 0, len = this.keys.length; i < len; i++){\r
11129             fn.call(scope || window, this.keys[i], this.items[i], i, len);\r
11130         }\r
11131     },\r
11132 \r
11133     /**\r
11134      * Returns the first item in the collection which elicits a true return value from the\r
11135      * passed selection function.\r
11136      * @param {Function} fn The selection function to execute for each item.\r
11137      * @param {Object} scope (optional) The scope in which to execute the function.\r
11138      * @return {Object} The first item in the collection which returned true from the selection function.\r
11139      */\r
11140     find : function(fn, scope){\r
11141         for(var i = 0, len = this.items.length; i < len; i++){\r
11142             if(fn.call(scope || window, this.items[i], this.keys[i])){\r
11143                 return this.items[i];\r
11144             }\r
11145         }\r
11146         return null;\r
11147     },\r
11148 \r
11149 /**\r
11150  * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.\r
11151  * @param {Number} index The index to insert the item at.\r
11152  * @param {String} key The key to associate with the new item, or the item itself.\r
11153  * @param {Object} o (optional) If the second parameter was a key, the new item.\r
11154  * @return {Object} The item inserted.\r
11155  */\r
11156     insert : function(index, key, o){\r
11157         if(arguments.length == 2){\r
11158             o = arguments[1];\r
11159             key = this.getKey(o);\r
11160         }\r
11161         if(this.containsKey(key)){\r
11162             this.suspendEvents();\r
11163             this.removeKey(key);\r
11164             this.resumeEvents();\r
11165         }\r
11166         if(index >= this.length){\r
11167             return this.add(key, o);\r
11168         }\r
11169         this.length++;\r
11170         this.items.splice(index, 0, o);\r
11171         if(typeof key != "undefined" && key !== null){\r
11172             this.map[key] = o;\r
11173         }\r
11174         this.keys.splice(index, 0, key);\r
11175         this.fireEvent("add", index, o, key);\r
11176         return o;\r
11177     },\r
11178 \r
11179 /**\r
11180  * Remove an item from the collection.\r
11181  * @param {Object} o The item to remove.\r
11182  * @return {Object} The item removed or false if no item was removed.\r
11183  */\r
11184     remove : function(o){\r
11185         return this.removeAt(this.indexOf(o));\r
11186     },\r
11187 \r
11188 /**\r
11189  * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.\r
11190  * @param {Number} index The index within the collection of the item to remove.\r
11191  * @return {Object} The item removed or false if no item was removed.\r
11192  */\r
11193     removeAt : function(index){\r
11194         if(index < this.length && index >= 0){\r
11195             this.length--;\r
11196             var o = this.items[index];\r
11197             this.items.splice(index, 1);\r
11198             var key = this.keys[index];\r
11199             if(typeof key != "undefined"){\r
11200                 delete this.map[key];\r
11201             }\r
11202             this.keys.splice(index, 1);\r
11203             this.fireEvent("remove", o, key);\r
11204             return o;\r
11205         }\r
11206         return false;\r
11207     },\r
11208 \r
11209 /**\r
11210  * Removed an item associated with the passed key fom the collection.\r
11211  * @param {String} key The key of the item to remove.\r
11212  * @return {Object} The item removed or false if no item was removed.\r
11213  */\r
11214     removeKey : function(key){\r
11215         return this.removeAt(this.indexOfKey(key));\r
11216     },\r
11217 \r
11218 /**\r
11219  * Returns the number of items in the collection.\r
11220  * @return {Number} the number of items in the collection.\r
11221  */\r
11222     getCount : function(){\r
11223         return this.length;\r
11224     },\r
11225 \r
11226 /**\r
11227  * Returns index within the collection of the passed Object.\r
11228  * @param {Object} o The item to find the index of.\r
11229  * @return {Number} index of the item. Returns -1 if not found.\r
11230  */\r
11231     indexOf : function(o){\r
11232         return this.items.indexOf(o);\r
11233     },\r
11234 \r
11235 /**\r
11236  * Returns index within the collection of the passed key.\r
11237  * @param {String} key The key to find the index of.\r
11238  * @return {Number} index of the key.\r
11239  */\r
11240     indexOfKey : function(key){\r
11241         return this.keys.indexOf(key);\r
11242     },\r
11243 \r
11244 /**\r
11245  * Returns the item associated with the passed key OR index. Key has priority over index.  This is the equivalent\r
11246  * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.\r
11247  * @param {String/Number} key The key or index of the item.\r
11248  * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.\r
11249  * If an item was found, but is a Class, returns <tt>null</tt>.\r
11250  */\r
11251     item : function(key){\r
11252         var mk = this.map[key],\r
11253             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;\r
11254         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!\r
11255     },\r
11256 \r
11257 /**\r
11258  * Returns the item at the specified index.\r
11259  * @param {Number} index The index of the item.\r
11260  * @return {Object} The item at the specified index.\r
11261  */\r
11262     itemAt : function(index){\r
11263         return this.items[index];\r
11264     },\r
11265 \r
11266 /**\r
11267  * Returns the item associated with the passed key.\r
11268  * @param {String/Number} key The key of the item.\r
11269  * @return {Object} The item associated with the passed key.\r
11270  */\r
11271     key : function(key){\r
11272         return this.map[key];\r
11273     },\r
11274 \r
11275 /**\r
11276  * Returns true if the collection contains the passed Object as an item.\r
11277  * @param {Object} o  The Object to look for in the collection.\r
11278  * @return {Boolean} True if the collection contains the Object as an item.\r
11279  */\r
11280     contains : function(o){\r
11281         return this.indexOf(o) != -1;\r
11282     },\r
11283 \r
11284 /**\r
11285  * Returns true if the collection contains the passed Object as a key.\r
11286  * @param {String} key The key to look for in the collection.\r
11287  * @return {Boolean} True if the collection contains the Object as a key.\r
11288  */\r
11289     containsKey : function(key){\r
11290         return typeof this.map[key] != "undefined";\r
11291     },\r
11292 \r
11293 /**\r
11294  * Removes all items from the collection.  Fires the {@link #clear} event when complete.\r
11295  */\r
11296     clear : function(){\r
11297         this.length = 0;\r
11298         this.items = [];\r
11299         this.keys = [];\r
11300         this.map = {};\r
11301         this.fireEvent("clear");\r
11302     },\r
11303 \r
11304 /**\r
11305  * Returns the first item in the collection.\r
11306  * @return {Object} the first item in the collection..\r
11307  */\r
11308     first : function(){\r
11309         return this.items[0];\r
11310     },\r
11311 \r
11312 /**\r
11313  * Returns the last item in the collection.\r
11314  * @return {Object} the last item in the collection..\r
11315  */\r
11316     last : function(){\r
11317         return this.items[this.length-1];\r
11318     },\r
11319 \r
11320     // private\r
11321     _sort : function(property, dir, fn){\r
11322         var i,\r
11323             len,\r
11324             dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1,\r
11325             c = [], k = this.keys, items = this.items;\r
11326             \r
11327         fn = fn || function(a, b){\r
11328             return a-b;\r
11329         };\r
11330         for(i = 0, len = items.length; i < len; i++){\r
11331             c[c.length] = {key: k[i], value: items[i], index: i};\r
11332         }\r
11333         c.sort(function(a, b){\r
11334             var v = fn(a[property], b[property]) * dsc;\r
11335             if(v === 0){\r
11336                 v = (a.index < b.index ? -1 : 1);\r
11337             }\r
11338             return v;\r
11339         });\r
11340         for(i = 0, len = c.length; i < len; i++){\r
11341             items[i] = c[i].value;\r
11342             k[i] = c[i].key;\r
11343         }\r
11344         this.fireEvent("sort", this);\r
11345     },\r
11346 \r
11347     /**\r
11348      * Sorts this collection with the passed comparison function\r
11349      * @param {String} direction (optional) "ASC" or "DESC"\r
11350      * @param {Function} fn (optional) comparison function\r
11351      */\r
11352     sort : function(dir, fn){\r
11353         this._sort("value", dir, fn);\r
11354     },\r
11355 \r
11356     /**\r
11357      * Sorts this collection by keys\r
11358      * @param {String} direction (optional) "ASC" or "DESC"\r
11359      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)\r
11360      */\r
11361     keySort : function(dir, fn){\r
11362         this._sort("key", dir, fn || function(a, b){\r
11363             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();\r
11364             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);\r
11365         });\r
11366     },\r
11367 \r
11368     /**\r
11369      * Returns a range of items in this collection\r
11370      * @param {Number} startIndex (optional) defaults to 0\r
11371      * @param {Number} endIndex (optional) default to the last item\r
11372      * @return {Array} An array of items\r
11373      */\r
11374     getRange : function(start, end){\r
11375         var items = this.items;\r
11376         if(items.length < 1){\r
11377             return [];\r
11378         }\r
11379         start = start || 0;\r
11380         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);\r
11381         var i, r = [];\r
11382         if(start <= end){\r
11383             for(i = start; i <= end; i++) {\r
11384                 r[r.length] = items[i];\r
11385             }\r
11386         }else{\r
11387             for(i = start; i >= end; i--) {\r
11388                 r[r.length] = items[i];\r
11389             }\r
11390         }\r
11391         return r;\r
11392     },\r
11393 \r
11394     /**\r
11395      * Filter the <i>objects</i> in this collection by a specific property.\r
11396      * Returns a new collection that has been filtered.\r
11397      * @param {String} property A property on your objects\r
11398      * @param {String/RegExp} value Either string that the property values\r
11399      * should start with or a RegExp to test against the property\r
11400      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning\r
11401      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).\r
11402      * @return {MixedCollection} The new filtered collection\r
11403      */\r
11404     filter : function(property, value, anyMatch, caseSensitive){\r
11405         if(Ext.isEmpty(value, false)){\r
11406             return this.clone();\r
11407         }\r
11408         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
11409         return this.filterBy(function(o){\r
11410             return o && value.test(o[property]);\r
11411         });\r
11412     },\r
11413 \r
11414     /**\r
11415      * Filter by a function. Returns a <i>new</i> collection that has been filtered.\r
11416      * The passed function will be called with each object in the collection.\r
11417      * If the function returns true, the value is included otherwise it is filtered.\r
11418      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)\r
11419      * @param {Object} scope (optional) The scope of the function (defaults to this)\r
11420      * @return {MixedCollection} The new filtered collection\r
11421      */\r
11422     filterBy : function(fn, scope){\r
11423         var r = new Ext.util.MixedCollection();\r
11424         r.getKey = this.getKey;\r
11425         var k = this.keys, it = this.items;\r
11426         for(var i = 0, len = it.length; i < len; i++){\r
11427             if(fn.call(scope||this, it[i], k[i])){\r
11428                 r.add(k[i], it[i]);\r
11429             }\r
11430         }\r
11431         return r;\r
11432     },\r
11433 \r
11434     /**\r
11435      * Finds the index of the first matching object in this collection by a specific property/value.\r
11436      * @param {String} property The name of a property on your objects.\r
11437      * @param {String/RegExp} value A string that the property values\r
11438      * should start with or a RegExp to test against the property.\r
11439      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
11440      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.\r
11441      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.\r
11442      * @return {Number} The matched index or -1\r
11443      */\r
11444     findIndex : function(property, value, start, anyMatch, caseSensitive){\r
11445         if(Ext.isEmpty(value, false)){\r
11446             return -1;\r
11447         }\r
11448         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
11449         return this.findIndexBy(function(o){\r
11450             return o && value.test(o[property]);\r
11451         }, null, start);\r
11452     },\r
11453 \r
11454     /**\r
11455      * Find the index of the first matching object in this collection by a function.\r
11456      * If the function returns <i>true</i> it is considered a match.\r
11457      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).\r
11458      * @param {Object} scope (optional) The scope of the function (defaults to this).\r
11459      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
11460      * @return {Number} The matched index or -1\r
11461      */\r
11462     findIndexBy : function(fn, scope, start){\r
11463         var k = this.keys, it = this.items;\r
11464         for(var i = (start||0), len = it.length; i < len; i++){\r
11465             if(fn.call(scope||this, it[i], k[i])){\r
11466                 return i;\r
11467             }\r
11468         }\r
11469         return -1;\r
11470     },\r
11471 \r
11472     // private\r
11473     createValueMatcher : function(value, anyMatch, caseSensitive){\r
11474         if(!value.exec){ // not a regex\r
11475             value = String(value);\r
11476             value = new RegExp((anyMatch === true ? '' : '^') + Ext.escapeRe(value), caseSensitive ? '' : 'i');\r
11477         }\r
11478         return value;\r
11479     },\r
11480 \r
11481     /**\r
11482      * Creates a shallow copy of this collection\r
11483      * @return {MixedCollection}\r
11484      */\r
11485     clone : function(){\r
11486         var r = new Ext.util.MixedCollection();\r
11487         var k = this.keys, it = this.items;\r
11488         for(var i = 0, len = it.length; i < len; i++){\r
11489             r.add(k[i], it[i]);\r
11490         }\r
11491         r.getKey = this.getKey;\r
11492         return r;\r
11493     }\r
11494 });\r
11495 /**\r
11496  * This method calls {@link #item item()}.\r
11497  * Returns the item associated with the passed key OR index. Key has priority over index.  This is the equivalent\r
11498  * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.\r
11499  * @param {String/Number} key The key or index of the item.\r
11500  * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.\r
11501  * If an item was found, but is a Class, returns <tt>null</tt>.\r
11502  */\r
11503 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
11504  * @class Ext.util.JSON
11505  * Modified version of Douglas Crockford"s json.js that doesn"t
11506  * mess with the Object prototype
11507  * http://www.json.org/js.html
11508  * @singleton
11509  */
11510 Ext.util.JSON = new (function(){
11511     var useHasOwn = !!{}.hasOwnProperty,
11512         isNative = function() {
11513             var useNative = null;
11514
11515             return function() {
11516                 if (useNative === null) {
11517                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
11518                 }
11519         
11520                 return useNative;
11521             };
11522         }(),
11523         pad = function(n) {
11524             return n < 10 ? "0" + n : n;
11525         },
11526         doDecode = function(json){
11527             return eval("(" + json + ')');    
11528         },
11529         doEncode = function(o){
11530             if(typeof o == "undefined" || o === null){
11531                 return "null";
11532             }else if(Ext.isArray(o)){
11533                 return encodeArray(o);
11534             }else if(Object.prototype.toString.apply(o) === '[object Date]'){
11535                 return Ext.util.JSON.encodeDate(o);
11536             }else if(typeof o == "string"){
11537                 return encodeString(o);
11538             }else if(typeof o == "number"){
11539                 return isFinite(o) ? String(o) : "null";
11540             }else if(typeof o == "boolean"){
11541                 return String(o);
11542             }else {
11543                 var a = ["{"], b, i, v;
11544                 for (i in o) {
11545                     if(!useHasOwn || o.hasOwnProperty(i)) {
11546                         v = o[i];
11547                         switch (typeof v) {
11548                         case "undefined":
11549                         case "function":
11550                         case "unknown":
11551                             break;
11552                         default:
11553                             if(b){
11554                                 a.push(',');
11555                             }
11556                             a.push(doEncode(i), ":",
11557                                     v === null ? "null" : doEncode(v));
11558                             b = true;
11559                         }
11560                     }
11561                 }
11562                 a.push("}");
11563                 return a.join("");
11564             }    
11565         },
11566         m = {
11567             "\b": '\\b',
11568             "\t": '\\t',
11569             "\n": '\\n',
11570             "\f": '\\f',
11571             "\r": '\\r',
11572             '"' : '\\"',
11573             "\\": '\\\\'
11574         },
11575         encodeString = function(s){
11576             if (/["\\\x00-\x1f]/.test(s)) {
11577                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
11578                     var c = m[b];
11579                     if(c){
11580                         return c;
11581                     }
11582                     c = b.charCodeAt();
11583                     return "\\u00" +
11584                         Math.floor(c / 16).toString(16) +
11585                         (c % 16).toString(16);
11586                 }) + '"';
11587             }
11588             return '"' + s + '"';
11589         },
11590         encodeArray = function(o){
11591             var a = ["["], b, i, l = o.length, v;
11592                 for (i = 0; i < l; i += 1) {
11593                     v = o[i];
11594                     switch (typeof v) {
11595                         case "undefined":
11596                         case "function":
11597                         case "unknown":
11598                             break;
11599                         default:
11600                             if (b) {
11601                                 a.push(',');
11602                             }
11603                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
11604                             b = true;
11605                     }
11606                 }
11607                 a.push("]");
11608                 return a.join("");
11609         };
11610
11611     this.encodeDate = function(o){
11612         return '"' + o.getFullYear() + "-" +
11613                 pad(o.getMonth() + 1) + "-" +
11614                 pad(o.getDate()) + "T" +
11615                 pad(o.getHours()) + ":" +
11616                 pad(o.getMinutes()) + ":" +
11617                 pad(o.getSeconds()) + '"';
11618     };
11619
11620     /**
11621      * Encodes an Object, Array or other value
11622      * @param {Mixed} o The variable to encode
11623      * @return {String} The JSON string
11624      */
11625     this.encode = function() {
11626         var ec;
11627         return function(o) {
11628             if (!ec) {
11629                 // setup encoding function on first access
11630                 ec = isNative() ? JSON.stringify : doEncode;
11631             }
11632             return ec(o);
11633         };
11634     }();
11635
11636
11637     /**
11638      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
11639      * @param {String} json The JSON string
11640      * @return {Object} The resulting object
11641      */
11642     this.decode = function() {
11643         var dc;
11644         return function(json) {
11645             if (!dc) {
11646                 // setup decoding function on first access
11647                 dc = isNative() ? JSON.parse : doDecode;
11648             }
11649             return dc(json);
11650         };
11651     }();
11652
11653 })();
11654 /**
11655  * Shorthand for {@link Ext.util.JSON#encode}
11656  * @param {Mixed} o The variable to encode
11657  * @return {String} The JSON string
11658  * @member Ext
11659  * @method encode
11660  */
11661 Ext.encode = Ext.util.JSON.encode;
11662 /**
11663  * Shorthand for {@link Ext.util.JSON#decode}
11664  * @param {String} json The JSON string
11665  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
11666  * @return {Object} The resulting object
11667  * @member Ext
11668  * @method decode
11669  */
11670 Ext.decode = Ext.util.JSON.decode;
11671 /**\r
11672  * @class Ext.util.Format\r
11673  * Reusable data formatting functions\r
11674  * @singleton\r
11675  */\r
11676 Ext.util.Format = function(){\r
11677     var trimRe = /^\s+|\s+$/g;\r
11678     return {\r
11679         /**\r
11680          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length\r
11681          * @param {String} value The string to truncate\r
11682          * @param {Number} length The maximum length to allow before truncating\r
11683          * @param {Boolean} word True to try to find a common work break\r
11684          * @return {String} The converted text\r
11685          */\r
11686         ellipsis : function(value, len, word){\r
11687             if(value && value.length > len){\r
11688                 if(word){\r
11689                     var vs = value.substr(0, len - 2);\r
11690                     var index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));\r
11691                     if(index == -1 || index < (len - 15)){\r
11692                         return value.substr(0, len - 3) + "...";\r
11693                     }else{\r
11694                         return vs.substr(0, index) + "...";\r
11695                     }\r
11696                 } else{\r
11697                     return value.substr(0, len - 3) + "...";\r
11698                 }\r
11699             }\r
11700             return value;\r
11701         },\r
11702 \r
11703         /**\r
11704          * Checks a reference and converts it to empty string if it is undefined\r
11705          * @param {Mixed} value Reference to check\r
11706          * @return {Mixed} Empty string if converted, otherwise the original value\r
11707          */\r
11708         undef : function(value){\r
11709             return value !== undefined ? value : "";\r
11710         },\r
11711 \r
11712         /**\r
11713          * Checks a reference and converts it to the default value if it's empty\r
11714          * @param {Mixed} value Reference to check\r
11715          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")\r
11716          * @return {String}\r
11717          */\r
11718         defaultValue : function(value, defaultValue){\r
11719             return value !== undefined && value !== '' ? value : defaultValue;\r
11720         },\r
11721 \r
11722         /**\r
11723          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.\r
11724          * @param {String} value The string to encode\r
11725          * @return {String} The encoded text\r
11726          */\r
11727         htmlEncode : function(value){\r
11728             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");\r
11729         },\r
11730 \r
11731         /**\r
11732          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.\r
11733          * @param {String} value The string to decode\r
11734          * @return {String} The decoded text\r
11735          */\r
11736         htmlDecode : function(value){\r
11737             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");\r
11738         },\r
11739 \r
11740         /**\r
11741          * Trims any whitespace from either side of a string\r
11742          * @param {String} value The text to trim\r
11743          * @return {String} The trimmed text\r
11744          */\r
11745         trim : function(value){\r
11746             return String(value).replace(trimRe, "");\r
11747         },\r
11748 \r
11749         /**\r
11750          * Returns a substring from within an original string\r
11751          * @param {String} value The original text\r
11752          * @param {Number} start The start index of the substring\r
11753          * @param {Number} length The length of the substring\r
11754          * @return {String} The substring\r
11755          */\r
11756         substr : function(value, start, length){\r
11757             return String(value).substr(start, length);\r
11758         },\r
11759 \r
11760         /**\r
11761          * Converts a string to all lower case letters\r
11762          * @param {String} value The text to convert\r
11763          * @return {String} The converted text\r
11764          */\r
11765         lowercase : function(value){\r
11766             return String(value).toLowerCase();\r
11767         },\r
11768 \r
11769         /**\r
11770          * Converts a string to all upper case letters\r
11771          * @param {String} value The text to convert\r
11772          * @return {String} The converted text\r
11773          */\r
11774         uppercase : function(value){\r
11775             return String(value).toUpperCase();\r
11776         },\r
11777 \r
11778         /**\r
11779          * Converts the first character only of a string to upper case\r
11780          * @param {String} value The text to convert\r
11781          * @return {String} The converted text\r
11782          */\r
11783         capitalize : function(value){\r
11784             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();\r
11785         },\r
11786 \r
11787         // private\r
11788         call : function(value, fn){\r
11789             if(arguments.length > 2){\r
11790                 var args = Array.prototype.slice.call(arguments, 2);\r
11791                 args.unshift(value);\r
11792                 return eval(fn).apply(window, args);\r
11793             }else{\r
11794                 return eval(fn).call(window, value);\r
11795             }\r
11796         },\r
11797 \r
11798         /**\r
11799          * Format a number as US currency\r
11800          * @param {Number/String} value The numeric value to format\r
11801          * @return {String} The formatted currency string\r
11802          */\r
11803         usMoney : function(v){\r
11804             v = (Math.round((v-0)*100))/100;\r
11805             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);\r
11806             v = String(v);\r
11807             var ps = v.split('.');\r
11808             var whole = ps[0];\r
11809             var sub = ps[1] ? '.'+ ps[1] : '.00';\r
11810             var r = /(\d+)(\d{3})/;\r
11811             while (r.test(whole)) {\r
11812                 whole = whole.replace(r, '$1' + ',' + '$2');\r
11813             }\r
11814             v = whole + sub;\r
11815             if(v.charAt(0) == '-'){\r
11816                 return '-$' + v.substr(1);\r
11817             }\r
11818             return "$" +  v;\r
11819         },\r
11820 \r
11821         /**\r
11822          * Parse a value into a formatted date using the specified format pattern.\r
11823          * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)\r
11824          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')\r
11825          * @return {String} The formatted date string\r
11826          */\r
11827         date : function(v, format){\r
11828             if(!v){\r
11829                 return "";\r
11830             }\r
11831             if(!Ext.isDate(v)){\r
11832                 v = new Date(Date.parse(v));\r
11833             }\r
11834             return v.dateFormat(format || "m/d/Y");\r
11835         },\r
11836 \r
11837         /**\r
11838          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently\r
11839          * @param {String} format Any valid date format string\r
11840          * @return {Function} The date formatting function\r
11841          */\r
11842         dateRenderer : function(format){\r
11843             return function(v){\r
11844                 return Ext.util.Format.date(v, format);\r
11845             };\r
11846         },\r
11847 \r
11848         // private\r
11849         stripTagsRE : /<\/?[^>]+>/gi,\r
11850         \r
11851         /**\r
11852          * Strips all HTML tags\r
11853          * @param {Mixed} value The text from which to strip tags\r
11854          * @return {String} The stripped text\r
11855          */\r
11856         stripTags : function(v){\r
11857             return !v ? v : String(v).replace(this.stripTagsRE, "");\r
11858         },\r
11859 \r
11860         stripScriptsRe : /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
11861 \r
11862         /**\r
11863          * Strips all script tags\r
11864          * @param {Mixed} value The text from which to strip script tags\r
11865          * @return {String} The stripped text\r
11866          */\r
11867         stripScripts : function(v){\r
11868             return !v ? v : String(v).replace(this.stripScriptsRe, "");\r
11869         },\r
11870 \r
11871         /**\r
11872          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)\r
11873          * @param {Number/String} size The numeric value to format\r
11874          * @return {String} The formatted file size\r
11875          */\r
11876         fileSize : function(size){\r
11877             if(size < 1024) {\r
11878                 return size + " bytes";\r
11879             } else if(size < 1048576) {\r
11880                 return (Math.round(((size*10) / 1024))/10) + " KB";\r
11881             } else {\r
11882                 return (Math.round(((size*10) / 1048576))/10) + " MB";\r
11883             }\r
11884         },\r
11885 \r
11886         /**\r
11887          * It does simple math for use in a template, for example:<pre><code>\r
11888          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');\r
11889          * </code></pre>\r
11890          * @return {Function} A function that operates on the passed value.\r
11891          */\r
11892         math : function(){\r
11893             var fns = {};\r
11894             return function(v, a){\r
11895                 if(!fns[a]){\r
11896                     fns[a] = new Function('v', 'return v ' + a + ';');\r
11897                 }\r
11898                 return fns[a](v);\r
11899             }\r
11900         }(),\r
11901 \r
11902         /**\r
11903          * Rounds the passed number to the required decimal precision.\r
11904          * @param {Number/String} value The numeric value to round.\r
11905          * @param {Number} precision The number of decimal places to which to round the first parameter's value.\r
11906          * @return {Number} The rounded value.\r
11907          */\r
11908         round : function(value, precision) {\r
11909             var result = Number(value);\r
11910             if (typeof precision == 'number') {\r
11911                 precision = Math.pow(10, precision);\r
11912                 result = Math.round(value * precision) / precision;\r
11913             }\r
11914             return result;\r
11915         },\r
11916 \r
11917         /**\r
11918          * Formats the number according to the format string.\r
11919          * <div style="margin-left:40px">examples (123456.789):\r
11920          * <div style="margin-left:10px">\r
11921          * 0 - (123456) show only digits, no precision<br>\r
11922          * 0.00 - (123456.78) show only digits, 2 precision<br>\r
11923          * 0.0000 - (123456.7890) show only digits, 4 precision<br>\r
11924          * 0,000 - (123,456) show comma and digits, no precision<br>\r
11925          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>\r
11926          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>\r
11927          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.\r
11928          * For example: 0.000,00/i\r
11929          * </div></div>\r
11930          * @param {Number} v The number to format.\r
11931          * @param {String} format The way you would like to format this text.\r
11932          * @return {String} The formatted number.\r
11933          */\r
11934         number: function(v, format) {\r
11935             if(!format){\r
11936                         return v;\r
11937                     }\r
11938                     v = Ext.num(v, NaN);\r
11939             if (isNaN(v)){\r
11940                 return '';\r
11941             }\r
11942                     var comma = ',',\r
11943                         dec = '.',\r
11944                         i18n = false,\r
11945                         neg = v < 0;\r
11946                 \r
11947                     v = Math.abs(v);\r
11948                     if(format.substr(format.length - 2) == '/i'){\r
11949                         format = format.substr(0, format.length - 2);\r
11950                         i18n = true;\r
11951                         comma = '.';\r
11952                         dec = ',';\r
11953                     }\r
11954                 \r
11955                     var hasComma = format.indexOf(comma) != -1, \r
11956                         psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);\r
11957                 \r
11958                     if(1 < psplit.length){\r
11959                         v = v.toFixed(psplit[1].length);\r
11960                     }else if(2 < psplit.length){\r
11961                         throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);\r
11962                     }else{\r
11963                         v = v.toFixed(0);\r
11964                     }\r
11965                 \r
11966                     var fnum = v.toString();\r
11967                     if(hasComma){\r
11968                         psplit = fnum.split('.');\r
11969                 \r
11970                         var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;\r
11971                 \r
11972                         for(var i = 0; i < j; i += n){\r
11973                             if(i != 0){\r
11974                                 n = 3;\r
11975                             }\r
11976                             parr[parr.length] = cnum.substr(i, n);\r
11977                             m -= 1;\r
11978                         }\r
11979                         fnum = parr.join(comma);\r
11980                         if(psplit[1]){\r
11981                             fnum += dec + psplit[1];\r
11982                         }\r
11983                     }\r
11984                 \r
11985                     return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);\r
11986         },\r
11987 \r
11988         /**\r
11989          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently\r
11990          * @param {String} format Any valid number format string for {@link #number}\r
11991          * @return {Function} The number formatting function\r
11992          */\r
11993         numberRenderer : function(format){\r
11994             return function(v){\r
11995                 return Ext.util.Format.number(v, format);\r
11996             };\r
11997         },\r
11998 \r
11999         /**\r
12000          * Selectively do a plural form of a word based on a numeric value. For example, in a template,\r
12001          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"\r
12002          * if the value is 0 or greater than 1.\r
12003          * @param {Number} value The value to compare against\r
12004          * @param {String} singular The singular form of the word\r
12005          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")\r
12006          */\r
12007         plural : function(v, s, p){\r
12008             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));\r
12009         },\r
12010         \r
12011         /**\r
12012          * Converts newline characters to the HTML tag &lt;br/>\r
12013          * @param {String} The string value to format.\r
12014          * @return {String} The string with embedded &lt;br/> tags in place of newlines.\r
12015          */\r
12016         nl2br : function(v){\r
12017             return v === undefined || v === null ? '' : v.replace(/\n/g, '<br/>');\r
12018         }\r
12019     }\r
12020 }();/**
12021  * @class Ext.XTemplate
12022  * @extends Ext.Template
12023  * <p>A template class that supports advanced functionality like autofilling arrays, conditional processing with
12024  * basic comparison operators, sub-templates, basic math function support, special built-in template variables,
12025  * inline code execution and more.  XTemplate also provides the templating mechanism built into {@link Ext.DataView}.</p>
12026  * <p>XTemplate supports many special tags and built-in operators that aren't defined as part of the API, but are
12027  * supported in the templates that can be created.  The following examples demonstrate all of the supported features.
12028  * This is the data object used for reference in each code example:</p>
12029  * <pre><code>
12030 var data = {
12031     name: 'Jack Slocum',
12032     title: 'Lead Developer',
12033     company: 'Ext JS, LLC',
12034     email: 'jack@extjs.com',
12035     address: '4 Red Bulls Drive',
12036     city: 'Cleveland',
12037     state: 'Ohio',
12038     zip: '44102',
12039     drinks: ['Red Bull', 'Coffee', 'Water'],
12040     kids: [{
12041         name: 'Sara Grace',
12042         age:3
12043     },{
12044         name: 'Zachary',
12045         age:2
12046     },{
12047         name: 'John James',
12048         age:0
12049     }]
12050 };
12051  * </code></pre>
12052  * <p><b>Auto filling of arrays</b><br/>The <tt>tpl</tt> tag and the <tt>for</tt> operator are used
12053  * to process the provided data object. If <tt>for="."</tt> is specified, the data object provided
12054  * is examined. If the variable in <tt>for</tt> is an array, it will auto-fill, repeating the template
12055  * block inside the <tt>tpl</tt> tag for each item in the array:</p>
12056  * <pre><code>
12057 var tpl = new Ext.XTemplate(
12058     '&lt;p>Kids: ',
12059     '&lt;tpl for=".">',
12060         '&lt;p>{name}&lt;/p>',
12061     '&lt;/tpl>&lt;/p>'
12062 );
12063 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12064  * </code></pre>
12065  * <p><b>Scope switching</b><br/>The <tt>for</tt> property can be leveraged to access specified members
12066  * of the provided data object to populate the template:</p>
12067  * <pre><code>
12068 var tpl = new Ext.XTemplate(
12069     '&lt;p>Name: {name}&lt;/p>',
12070     '&lt;p>Title: {title}&lt;/p>',
12071     '&lt;p>Company: {company}&lt;/p>',
12072     '&lt;p>Kids: ',
12073     '&lt;tpl <b>for="kids"</b>>', // interrogate the kids property within the data
12074         '&lt;p>{name}&lt;/p>',
12075     '&lt;/tpl>&lt;/p>'
12076 );
12077 tpl.overwrite(panel.body, data);
12078  * </code></pre>
12079  * <p><b>Access to parent object from within sub-template scope</b><br/>When processing a sub-template, for example while
12080  * looping through a child array, you can access the parent object's members via the <tt>parent</tt> object:</p>
12081  * <pre><code>
12082 var tpl = new Ext.XTemplate(
12083     '&lt;p>Name: {name}&lt;/p>',
12084     '&lt;p>Kids: ',
12085     '&lt;tpl for="kids">',
12086         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12087             '&lt;p>{name}&lt;/p>',
12088             '&lt;p>Dad: {parent.name}&lt;/p>',
12089         '&lt;/tpl>',
12090     '&lt;/tpl>&lt;/p>'
12091 );
12092 tpl.overwrite(panel.body, data);
12093 </code></pre>
12094  * <p><b>Array item index and basic math support</b> <br/>While processing an array, the special variable <tt>{#}</tt>
12095  * will provide the current array index + 1 (starts at 1, not 0). Templates also support the basic math operators
12096  * + - * and / that can be applied directly on numeric data values:</p>
12097  * <pre><code>
12098 var tpl = new Ext.XTemplate(
12099     '&lt;p>Name: {name}&lt;/p>',
12100     '&lt;p>Kids: ',
12101     '&lt;tpl for="kids">',
12102         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12103             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
12104             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
12105             '&lt;p>Dad: {parent.name}&lt;/p>',
12106         '&lt;/tpl>',
12107     '&lt;/tpl>&lt;/p>'
12108 );
12109 tpl.overwrite(panel.body, data);
12110 </code></pre>
12111  * <p><b>Auto-rendering of flat arrays</b> <br/>Flat arrays that contain values (and not objects) can be auto-rendered
12112  * using the special <tt>{.}</tt> variable inside a loop.  This variable will represent the value of
12113  * the array at the current index:</p>
12114  * <pre><code>
12115 var tpl = new Ext.XTemplate(
12116     '&lt;p>{name}\'s favorite beverages:&lt;/p>',
12117     '&lt;tpl for="drinks">',
12118        '&lt;div> - {.}&lt;/div>',
12119     '&lt;/tpl>'
12120 );
12121 tpl.overwrite(panel.body, data);
12122 </code></pre>
12123  * <p><b>Basic conditional logic</b> <br/>Using the <tt>tpl</tt> tag and the <tt>if</tt>
12124  * operator you can provide conditional checks for deciding whether or not to render specific parts of the template.
12125  * Note that there is no <tt>else</tt> operator &mdash; if needed, you should use two opposite <tt>if</tt> statements.
12126  * Properly-encoded attributes are required as seen in the following example:</p>
12127  * <pre><code>
12128 var tpl = new Ext.XTemplate(
12129     '&lt;p>Name: {name}&lt;/p>',
12130     '&lt;p>Kids: ',
12131     '&lt;tpl for="kids">',
12132         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12133             '&lt;p>{name}&lt;/p>',
12134         '&lt;/tpl>',
12135     '&lt;/tpl>&lt;/p>'
12136 );
12137 tpl.overwrite(panel.body, data);
12138 </code></pre>
12139  * <p><b>Ability to execute arbitrary inline code</b> <br/>In an XTemplate, anything between {[ ... ]}  is considered
12140  * code to be executed in the scope of the template. There are some special variables available in that code:
12141  * <ul>
12142  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using scope changing sub-templates, you
12143  * can change what <tt>values</tt> is.</li>
12144  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12145  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the loop you are in (1-based).</li>
12146  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length of the array you are looping.</li>
12147  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12148  * </ul>
12149  * This example demonstrates basic row striping using an inline code block and the <tt>xindex</tt> variable:</p>
12150  * <pre><code>
12151 var tpl = new Ext.XTemplate(
12152     '&lt;p>Name: {name}&lt;/p>',
12153     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
12154     '&lt;p>Kids: ',
12155     '&lt;tpl for="kids">',
12156        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12157         '{name}',
12158         '&lt;/div>',
12159     '&lt;/tpl>&lt;/p>'
12160 );
12161 tpl.overwrite(panel.body, data);
12162 </code></pre>
12163  * <p><b>Template member functions</b> <br/>One or more member functions can be defined directly on the config
12164  * object passed into the XTemplate constructor for more complex processing:</p>
12165  * <pre><code>
12166 var tpl = new Ext.XTemplate(
12167     '&lt;p>Name: {name}&lt;/p>',
12168     '&lt;p>Kids: ',
12169     '&lt;tpl for="kids">',
12170         '&lt;tpl if="this.isGirl(name)">',
12171             '&lt;p>Girl: {name} - {age}&lt;/p>',
12172         '&lt;/tpl>',
12173         '&lt;tpl if="this.isGirl(name) == false">',
12174             '&lt;p>Boy: {name} - {age}&lt;/p>',
12175         '&lt;/tpl>',
12176         '&lt;tpl if="this.isBaby(age)">',
12177             '&lt;p>{name} is a baby!&lt;/p>',
12178         '&lt;/tpl>',
12179     '&lt;/tpl>&lt;/p>', {
12180      isGirl: function(name){
12181          return name == 'Sara Grace';
12182      },
12183      isBaby: function(age){
12184         return age < 1;
12185      }
12186 });
12187 tpl.overwrite(panel.body, data);
12188 </code></pre>
12189  * @constructor
12190  * @param {String/Array/Object} parts The HTML fragment or an array of fragments to join(""), or multiple arguments
12191  * to join("") that can also include a config object
12192  */
12193 Ext.XTemplate = function(){
12194     Ext.XTemplate.superclass.constructor.apply(this, arguments);
12195
12196     var me = this,
12197         s = me.html,
12198         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
12199         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
12200         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
12201         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
12202         m,
12203         id = 0,
12204         tpls = [],
12205         VALUES = 'values',
12206         PARENT = 'parent',
12207         XINDEX = 'xindex',
12208         XCOUNT = 'xcount',
12209         RETURN = 'return ',
12210         WITHVALUES = 'with(values){ ';
12211
12212     s = ['<tpl>', s, '</tpl>'].join('');
12213
12214     while((m = s.match(re))){
12215         var m2 = m[0].match(nameRe),
12216                         m3 = m[0].match(ifRe),
12217                 m4 = m[0].match(execRe),
12218                 exp = null,
12219                 fn = null,
12220                 exec = null,
12221                 name = m2 && m2[1] ? m2[1] : '';
12222
12223        if (m3) {
12224            exp = m3 && m3[1] ? m3[1] : null;
12225            if(exp){
12226                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
12227            }
12228        }
12229        if (m4) {
12230            exp = m4 && m4[1] ? m4[1] : null;
12231            if(exp){
12232                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
12233            }
12234        }
12235        if(name){
12236            switch(name){
12237                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
12238                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
12239                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
12240            }
12241        }
12242        tpls.push({
12243             id: id,
12244             target: name,
12245             exec: exec,
12246             test: fn,
12247             body: m[1]||''
12248         });
12249        s = s.replace(m[0], '{xtpl'+ id + '}');
12250        ++id;
12251     }
12252         Ext.each(tpls, function(t) {
12253         me.compileTpl(t);
12254     });
12255     me.master = tpls[tpls.length-1];
12256     me.tpls = tpls;
12257 };
12258 Ext.extend(Ext.XTemplate, Ext.Template, {
12259     // private
12260     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
12261     // private
12262     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
12263
12264     // private
12265     applySubTemplate : function(id, values, parent, xindex, xcount){
12266         var me = this,
12267                 len,
12268                 t = me.tpls[id],
12269                 vs,
12270                 buf = [];
12271         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
12272             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
12273             return '';
12274         }
12275         vs = t.target ? t.target.call(me, values, parent) : values;
12276         len = vs.length;
12277         parent = t.target ? values : parent;
12278         if(t.target && Ext.isArray(vs)){
12279                 Ext.each(vs, function(v, i) {
12280                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
12281             });
12282             return buf.join('');
12283         }
12284         return t.compiled.call(me, vs, parent, xindex, xcount);
12285     },
12286
12287     // private
12288     compileTpl : function(tpl){
12289         var fm = Ext.util.Format,
12290                 useF = this.disableFormats !== true,
12291             sep = Ext.isGecko ? "+" : ",",
12292             body;
12293
12294         function fn(m, name, format, args, math){
12295             if(name.substr(0, 4) == 'xtpl'){
12296                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
12297             }
12298             var v;
12299             if(name === '.'){
12300                 v = 'values';
12301             }else if(name === '#'){
12302                 v = 'xindex';
12303             }else if(name.indexOf('.') != -1){
12304                 v = name;
12305             }else{
12306                 v = "values['" + name + "']";
12307             }
12308             if(math){
12309                 v = '(' + v + math + ')';
12310             }
12311             if (format && useF) {
12312                 args = args ? ',' + args : "";
12313                 if(format.substr(0, 5) != "this."){
12314                     format = "fm." + format + '(';
12315                 }else{
12316                     format = 'this.call("'+ format.substr(5) + '", ';
12317                     args = ", values";
12318                 }
12319             } else {
12320                 args= ''; format = "("+v+" === undefined ? '' : ";
12321             }
12322             return "'"+ sep + format + v + args + ")"+sep+"'";
12323         }
12324
12325         function codeFn(m, code){
12326             return "'"+ sep +'('+code+')'+sep+"'";
12327         }
12328
12329         // branched to use + in gecko and [].join() in others
12330         if(Ext.isGecko){
12331             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
12332                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
12333                     "';};";
12334         }else{
12335             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
12336             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
12337             body.push("'].join('');};");
12338             body = body.join('');
12339         }
12340         eval(body);
12341         return this;
12342     },
12343
12344     /**
12345      * Returns an HTML fragment of this template with the specified values applied.
12346      * @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'})
12347      * @return {String} The HTML fragment
12348      */
12349     applyTemplate : function(values){
12350         return this.master.compiled.call(this, values, {}, 1, 1);
12351     },
12352
12353     /**
12354      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
12355      * @return {Function} The compiled function
12356      */
12357     compile : function(){return this;}
12358
12359     /**
12360      * @property re
12361      * @hide
12362      */
12363     /**
12364      * @property disableFormats
12365      * @hide
12366      */
12367     /**
12368      * @method set
12369      * @hide
12370      */
12371
12372 });
12373 /**
12374  * Alias for {@link #applyTemplate}
12375  * Returns an HTML fragment of this template with the specified values applied.
12376  * @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'})
12377  * @return {String} The HTML fragment
12378  * @member Ext.XTemplate
12379  * @method apply
12380  */
12381 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
12382
12383 /**
12384  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
12385  * @param {String/HTMLElement} el A DOM element or its id
12386  * @return {Ext.Template} The created template
12387  * @static
12388  */
12389 Ext.XTemplate.from = function(el){
12390     el = Ext.getDom(el);
12391     return new Ext.XTemplate(el.value || el.innerHTML);
12392 };/**\r
12393  * @class Ext.util.CSS\r
12394  * Utility class for manipulating CSS rules\r
12395  * @singleton\r
12396  */\r
12397 Ext.util.CSS = function(){\r
12398         var rules = null;\r
12399         var doc = document;\r
12400 \r
12401     var camelRe = /(-[a-z])/gi;\r
12402     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
12403 \r
12404    return {\r
12405    /**\r
12406     * Creates a stylesheet from a text blob of rules.\r
12407     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.\r
12408     * @param {String} cssText The text containing the css rules\r
12409     * @param {String} id An id to add to the stylesheet for later removal\r
12410     * @return {StyleSheet}\r
12411     */\r
12412    createStyleSheet : function(cssText, id){\r
12413        var ss;\r
12414        var head = doc.getElementsByTagName("head")[0];\r
12415        var rules = doc.createElement("style");\r
12416        rules.setAttribute("type", "text/css");\r
12417        if(id){\r
12418            rules.setAttribute("id", id);\r
12419        }\r
12420        if(Ext.isIE){\r
12421            head.appendChild(rules);\r
12422            ss = rules.styleSheet;\r
12423            ss.cssText = cssText;\r
12424        }else{\r
12425            try{\r
12426                 rules.appendChild(doc.createTextNode(cssText));\r
12427            }catch(e){\r
12428                rules.cssText = cssText;\r
12429            }\r
12430            head.appendChild(rules);\r
12431            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);\r
12432        }\r
12433        this.cacheStyleSheet(ss);\r
12434        return ss;\r
12435    },\r
12436 \r
12437    /**\r
12438     * Removes a style or link tag by id\r
12439     * @param {String} id The id of the tag\r
12440     */\r
12441    removeStyleSheet : function(id){\r
12442        var existing = doc.getElementById(id);\r
12443        if(existing){\r
12444            existing.parentNode.removeChild(existing);\r
12445        }\r
12446    },\r
12447 \r
12448    /**\r
12449     * Dynamically swaps an existing stylesheet reference for a new one\r
12450     * @param {String} id The id of an existing link tag to remove\r
12451     * @param {String} url The href of the new stylesheet to include\r
12452     */\r
12453    swapStyleSheet : function(id, url){\r
12454        this.removeStyleSheet(id);\r
12455        var ss = doc.createElement("link");\r
12456        ss.setAttribute("rel", "stylesheet");\r
12457        ss.setAttribute("type", "text/css");\r
12458        ss.setAttribute("id", id);\r
12459        ss.setAttribute("href", url);\r
12460        doc.getElementsByTagName("head")[0].appendChild(ss);\r
12461    },\r
12462    \r
12463    /**\r
12464     * Refresh the rule cache if you have dynamically added stylesheets\r
12465     * @return {Object} An object (hash) of rules indexed by selector\r
12466     */\r
12467    refreshCache : function(){\r
12468        return this.getRules(true);\r
12469    },\r
12470 \r
12471    // private\r
12472    cacheStyleSheet : function(ss){\r
12473        if(!rules){\r
12474            rules = {};\r
12475        }\r
12476        try{// try catch for cross domain access issue\r
12477            var ssRules = ss.cssRules || ss.rules;\r
12478            for(var j = ssRules.length-1; j >= 0; --j){\r
12479                rules[ssRules[j].selectorText] = ssRules[j];\r
12480            }\r
12481        }catch(e){}\r
12482    },\r
12483    \r
12484    /**\r
12485     * Gets all css rules for the document\r
12486     * @param {Boolean} refreshCache true to refresh the internal cache\r
12487     * @return {Object} An object (hash) of rules indexed by selector\r
12488     */\r
12489    getRules : function(refreshCache){\r
12490                 if(rules === null || refreshCache){\r
12491                         rules = {};\r
12492                         var ds = doc.styleSheets;\r
12493                         for(var i =0, len = ds.length; i < len; i++){\r
12494                             try{\r
12495                         this.cacheStyleSheet(ds[i]);\r
12496                     }catch(e){} \r
12497                 }\r
12498                 }\r
12499                 return rules;\r
12500         },\r
12501         \r
12502         /**\r
12503     * Gets an an individual CSS rule by selector(s)\r
12504     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.\r
12505     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically\r
12506     * @return {CSSRule} The CSS rule or null if one is not found\r
12507     */\r
12508    getRule : function(selector, refreshCache){\r
12509                 var rs = this.getRules(refreshCache);\r
12510                 if(!Ext.isArray(selector)){\r
12511                     return rs[selector];\r
12512                 }\r
12513                 for(var i = 0; i < selector.length; i++){\r
12514                         if(rs[selector[i]]){\r
12515                                 return rs[selector[i]];\r
12516                         }\r
12517                 }\r
12518                 return null;\r
12519         },\r
12520         \r
12521         \r
12522         /**\r
12523     * Updates a rule property\r
12524     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.\r
12525     * @param {String} property The css property\r
12526     * @param {String} value The new value for the property\r
12527     * @return {Boolean} true If a rule was found and updated\r
12528     */\r
12529    updateRule : function(selector, property, value){\r
12530                 if(!Ext.isArray(selector)){\r
12531                         var rule = this.getRule(selector);\r
12532                         if(rule){\r
12533                                 rule.style[property.replace(camelRe, camelFn)] = value;\r
12534                                 return true;\r
12535                         }\r
12536                 }else{\r
12537                         for(var i = 0; i < selector.length; i++){\r
12538                                 if(this.updateRule(selector[i], property, value)){\r
12539                                         return true;\r
12540                                 }\r
12541                         }\r
12542                 }\r
12543                 return false;\r
12544         }\r
12545    };   \r
12546 }();/**
12547  @class Ext.util.ClickRepeater
12548  @extends Ext.util.Observable
12549
12550  A wrapper class which can be applied to any element. Fires a "click" event while the
12551  mouse is pressed. The interval between firings may be specified in the config but
12552  defaults to 20 milliseconds.
12553
12554  Optionally, a CSS class may be applied to the element during the time it is pressed.
12555
12556  @cfg {Mixed} el The element to act as a button.
12557  @cfg {Number} delay The initial delay before the repeating event begins firing.
12558  Similar to an autorepeat key delay.
12559  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
12560  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
12561  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
12562            "interval" and "delay" are ignored.
12563  @cfg {Boolean} preventDefault True to prevent the default click event
12564  @cfg {Boolean} stopDefault True to stop the default click event
12565
12566  @history
12567     2007-02-02 jvs Original code contributed by Nige "Animal" White
12568     2007-02-02 jvs Renamed to ClickRepeater
12569     2007-02-03 jvs Modifications for FF Mac and Safari
12570
12571  @constructor
12572  @param {Mixed} el The element to listen on
12573  @param {Object} config
12574  */
12575 Ext.util.ClickRepeater = function(el, config)
12576 {
12577     this.el = Ext.get(el);
12578     this.el.unselectable();
12579
12580     Ext.apply(this, config);
12581
12582     this.addEvents(
12583     /**
12584      * @event mousedown
12585      * Fires when the mouse button is depressed.
12586      * @param {Ext.util.ClickRepeater} this
12587      */
12588         "mousedown",
12589     /**
12590      * @event click
12591      * Fires on a specified interval during the time the element is pressed.
12592      * @param {Ext.util.ClickRepeater} this
12593      */
12594         "click",
12595     /**
12596      * @event mouseup
12597      * Fires when the mouse key is released.
12598      * @param {Ext.util.ClickRepeater} this
12599      */
12600         "mouseup"
12601     );
12602
12603     if(!this.disabled){
12604         this.disabled = true;
12605         this.enable();
12606     }
12607
12608     // allow inline handler
12609     if(this.handler){
12610         this.on("click", this.handler,  this.scope || this);
12611     }
12612
12613     Ext.util.ClickRepeater.superclass.constructor.call(this);
12614 };
12615
12616 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
12617     interval : 20,
12618     delay: 250,
12619     preventDefault : true,
12620     stopDefault : false,
12621     timer : 0,
12622
12623     /**
12624      * Enables the repeater and allows events to fire.
12625      */
12626     enable: function(){
12627         if(this.disabled){
12628             this.el.on('mousedown', this.handleMouseDown, this);
12629             if(this.preventDefault || this.stopDefault){
12630                 this.el.on('click', this.eventOptions, this);
12631             }
12632         }
12633         this.disabled = false;
12634     },
12635     
12636     /**
12637      * Disables the repeater and stops events from firing.
12638      */
12639     disable: function(/* private */ force){
12640         if(force || !this.disabled){
12641             clearTimeout(this.timer);
12642             if(this.pressClass){
12643                 this.el.removeClass(this.pressClass);
12644             }
12645             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
12646             this.el.removeAllListeners();
12647         }
12648         this.disabled = true;
12649     },
12650     
12651     /**
12652      * Convenience function for setting disabled/enabled by boolean.
12653      * @param {Boolean} disabled
12654      */
12655     setDisabled: function(disabled){
12656         this[disabled ? 'disable' : 'enable']();    
12657     },
12658     
12659     eventOptions: function(e){
12660         if(this.preventDefault){
12661             e.preventDefault();
12662         }
12663         if(this.stopDefault){
12664             e.stopEvent();
12665         }       
12666     },
12667     
12668     // private
12669     destroy : function() {
12670         this.disable(true);
12671         Ext.destroy(this.el);
12672         this.purgeListeners();
12673     },
12674     
12675     // private
12676     handleMouseDown : function(){
12677         clearTimeout(this.timer);
12678         this.el.blur();
12679         if(this.pressClass){
12680             this.el.addClass(this.pressClass);
12681         }
12682         this.mousedownTime = new Date();
12683
12684         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
12685         this.el.on("mouseout", this.handleMouseOut, this);
12686
12687         this.fireEvent("mousedown", this);
12688         this.fireEvent("click", this);
12689
12690 //      Do not honor delay or interval if acceleration wanted.
12691         if (this.accelerate) {
12692             this.delay = 400;
12693             }
12694         this.timer = this.click.defer(this.delay || this.interval, this);
12695     },
12696
12697     // private
12698     click : function(){
12699         this.fireEvent("click", this);
12700         this.timer = this.click.defer(this.accelerate ?
12701             this.easeOutExpo(this.mousedownTime.getElapsed(),
12702                 400,
12703                 -390,
12704                 12000) :
12705             this.interval, this);
12706     },
12707
12708     easeOutExpo : function (t, b, c, d) {
12709         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
12710     },
12711
12712     // private
12713     handleMouseOut : function(){
12714         clearTimeout(this.timer);
12715         if(this.pressClass){
12716             this.el.removeClass(this.pressClass);
12717         }
12718         this.el.on("mouseover", this.handleMouseReturn, this);
12719     },
12720
12721     // private
12722     handleMouseReturn : function(){
12723         this.el.un("mouseover", this.handleMouseReturn, this);
12724         if(this.pressClass){
12725             this.el.addClass(this.pressClass);
12726         }
12727         this.click();
12728     },
12729
12730     // private
12731     handleMouseUp : function(){
12732         clearTimeout(this.timer);
12733         this.el.un("mouseover", this.handleMouseReturn, this);
12734         this.el.un("mouseout", this.handleMouseOut, this);
12735         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
12736         this.el.removeClass(this.pressClass);
12737         this.fireEvent("mouseup", this);
12738     }
12739 });/**
12740  * @class Ext.KeyNav
12741  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
12742  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
12743  * way to implement custom navigation schemes for any UI component.</p>
12744  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
12745  * pageUp, pageDown, del, home, end.  Usage:</p>
12746  <pre><code>
12747 var nav = new Ext.KeyNav("my-element", {
12748     "left" : function(e){
12749         this.moveLeft(e.ctrlKey);
12750     },
12751     "right" : function(e){
12752         this.moveRight(e.ctrlKey);
12753     },
12754     "enter" : function(e){
12755         this.save();
12756     },
12757     scope : this
12758 });
12759 </code></pre>
12760  * @constructor
12761  * @param {Mixed} el The element to bind to
12762  * @param {Object} config The config
12763  */
12764 Ext.KeyNav = function(el, config){
12765     this.el = Ext.get(el);
12766     Ext.apply(this, config);
12767     if(!this.disabled){
12768         this.disabled = true;
12769         this.enable();
12770     }
12771 };
12772
12773 Ext.KeyNav.prototype = {
12774     /**
12775      * @cfg {Boolean} disabled
12776      * True to disable this KeyNav instance (defaults to false)
12777      */
12778     disabled : false,
12779     /**
12780      * @cfg {String} defaultEventAction
12781      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
12782      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
12783      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
12784      */
12785     defaultEventAction: "stopEvent",
12786     /**
12787      * @cfg {Boolean} forceKeyDown
12788      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
12789      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
12790      * handle keydown instead of keypress.
12791      */
12792     forceKeyDown : false,
12793
12794     // private
12795     prepareEvent : function(e){
12796         var k = e.getKey();
12797         var h = this.keyToHandler[k];
12798         if(Ext.isSafari2 && h && k >= 37 && k <= 40){
12799             e.stopEvent();
12800         }
12801     },
12802
12803     // private
12804     relay : function(e){
12805         var k = e.getKey();
12806         var h = this.keyToHandler[k];
12807         if(h && this[h]){
12808             if(this.doRelay(e, this[h], h) !== true){
12809                 e[this.defaultEventAction]();
12810             }
12811         }
12812     },
12813
12814     // private
12815     doRelay : function(e, h, hname){
12816         return h.call(this.scope || this, e);
12817     },
12818
12819     // possible handlers
12820     enter : false,
12821     left : false,
12822     right : false,
12823     up : false,
12824     down : false,
12825     tab : false,
12826     esc : false,
12827     pageUp : false,
12828     pageDown : false,
12829     del : false,
12830     home : false,
12831     end : false,
12832
12833     // quick lookup hash
12834     keyToHandler : {
12835         37 : "left",
12836         39 : "right",
12837         38 : "up",
12838         40 : "down",
12839         33 : "pageUp",
12840         34 : "pageDown",
12841         46 : "del",
12842         36 : "home",
12843         35 : "end",
12844         13 : "enter",
12845         27 : "esc",
12846         9  : "tab"
12847     },
12848
12849         /**
12850          * Enable this KeyNav
12851          */
12852         enable: function(){
12853                 if(this.disabled){
12854             // ie won't do special keys on keypress, no one else will repeat keys with keydown
12855             // the EventObject will normalize Safari automatically
12856             if(this.isKeydown()){
12857                 this.el.on("keydown", this.relay,  this);
12858             }else{
12859                 this.el.on("keydown", this.prepareEvent,  this);
12860                 this.el.on("keypress", this.relay,  this);
12861             }
12862                     this.disabled = false;
12863                 }
12864         },
12865
12866         /**
12867          * Disable this KeyNav
12868          */
12869         disable: function(){
12870                 if(!this.disabled){
12871                     if(this.isKeydown()){
12872                 this.el.un("keydown", this.relay, this);
12873             }else{
12874                 this.el.un("keydown", this.prepareEvent, this);
12875                 this.el.un("keypress", this.relay, this);
12876             }
12877                     this.disabled = true;
12878                 }
12879         },
12880     
12881     /**
12882      * Convenience function for setting disabled/enabled by boolean.
12883      * @param {Boolean} disabled
12884      */
12885     setDisabled : function(disabled){
12886         this[disabled ? "disable" : "enable"]();
12887     },
12888     
12889     // private
12890     isKeydown: function(){
12891         return this.forceKeyDown || Ext.EventManager.useKeydown;
12892     }
12893 };
12894 /**\r
12895  * @class Ext.KeyMap\r
12896  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.\r
12897  * The constructor accepts the same config object as defined by {@link #addBinding}.\r
12898  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key\r
12899  * combination it will call the function with this signature (if the match is a multi-key\r
12900  * combination the callback will still be called only once): (String key, Ext.EventObject e)\r
12901  * A KeyMap can also handle a string representation of keys.<br />\r
12902  * Usage:\r
12903  <pre><code>\r
12904 // map one key by key code\r
12905 var map = new Ext.KeyMap("my-element", {\r
12906     key: 13, // or Ext.EventObject.ENTER\r
12907     fn: myHandler,\r
12908     scope: myObject\r
12909 });\r
12910 \r
12911 // map multiple keys to one action by string\r
12912 var map = new Ext.KeyMap("my-element", {\r
12913     key: "a\r\n\t",\r
12914     fn: myHandler,\r
12915     scope: myObject\r
12916 });\r
12917 \r
12918 // map multiple keys to multiple actions by strings and array of codes\r
12919 var map = new Ext.KeyMap("my-element", [\r
12920     {\r
12921         key: [10,13],\r
12922         fn: function(){ alert("Return was pressed"); }\r
12923     }, {\r
12924         key: "abc",\r
12925         fn: function(){ alert('a, b or c was pressed'); }\r
12926     }, {\r
12927         key: "\t",\r
12928         ctrl:true,\r
12929         shift:true,\r
12930         fn: function(){ alert('Control + shift + tab was pressed.'); }\r
12931     }\r
12932 ]);\r
12933 </code></pre>\r
12934  * <b>Note: A KeyMap starts enabled</b>\r
12935  * @constructor\r
12936  * @param {Mixed} el The element to bind to\r
12937  * @param {Object} config The config (see {@link #addBinding})\r
12938  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")\r
12939  */\r
12940 Ext.KeyMap = function(el, config, eventName){\r
12941     this.el  = Ext.get(el);\r
12942     this.eventName = eventName || "keydown";\r
12943     this.bindings = [];\r
12944     if(config){\r
12945         this.addBinding(config);\r
12946     }\r
12947     this.enable();\r
12948 };\r
12949 \r
12950 Ext.KeyMap.prototype = {\r
12951     /**\r
12952      * True to stop the event from bubbling and prevent the default browser action if the\r
12953      * key was handled by the KeyMap (defaults to false)\r
12954      * @type Boolean\r
12955      */\r
12956     stopEvent : false,\r
12957 \r
12958     /**\r
12959      * Add a new binding to this KeyMap. The following config object properties are supported:\r
12960      * <pre>\r
12961 Property    Type             Description\r
12962 ----------  ---------------  ----------------------------------------------------------------------\r
12963 key         String/Array     A single keycode or an array of keycodes to handle\r
12964 shift       Boolean          True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)\r
12965 ctrl        Boolean          True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)\r
12966 alt         Boolean          True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)\r
12967 handler     Function         The function to call when KeyMap finds the expected key combination\r
12968 fn          Function         Alias of handler (for backwards-compatibility)\r
12969 scope       Object           The scope of the callback function\r
12970 stopEvent   Boolean          True to stop the event from bubbling and prevent the default browser action if the key was handled by the KeyMap (defaults to false)\r
12971 </pre>\r
12972      *\r
12973      * Usage:\r
12974      * <pre><code>\r
12975 // Create a KeyMap\r
12976 var map = new Ext.KeyMap(document, {\r
12977     key: Ext.EventObject.ENTER,\r
12978     fn: handleKey,\r
12979     scope: this\r
12980 });\r
12981 \r
12982 //Add a new binding to the existing KeyMap later\r
12983 map.addBinding({\r
12984     key: 'abc',\r
12985     shift: true,\r
12986     fn: handleKey,\r
12987     scope: this\r
12988 });\r
12989 </code></pre>\r
12990      * @param {Object/Array} config A single KeyMap config or an array of configs\r
12991      */\r
12992         addBinding : function(config){\r
12993         if(Ext.isArray(config)){\r
12994             Ext.each(config, function(c){\r
12995                 this.addBinding(c);\r
12996             }, this);\r
12997             return;\r
12998         }\r
12999         var keyCode = config.key,\r
13000             fn = config.fn || config.handler,\r
13001             scope = config.scope;\r
13002 \r
13003         if (config.stopEvent) {\r
13004             this.stopEvent = config.stopEvent;    \r
13005         }       \r
13006 \r
13007         if(typeof keyCode == "string"){\r
13008             var ks = [];\r
13009             var keyString = keyCode.toUpperCase();\r
13010             for(var j = 0, len = keyString.length; j < len; j++){\r
13011                 ks.push(keyString.charCodeAt(j));\r
13012             }\r
13013             keyCode = ks;\r
13014         }\r
13015         var keyArray = Ext.isArray(keyCode);\r
13016         \r
13017         var handler = function(e){\r
13018             if(this.checkModifiers(config, e)){\r
13019                 var k = e.getKey();\r
13020                 if(keyArray){\r
13021                     for(var i = 0, len = keyCode.length; i < len; i++){\r
13022                         if(keyCode[i] == k){\r
13023                           if(this.stopEvent){\r
13024                               e.stopEvent();\r
13025                           }\r
13026                           fn.call(scope || window, k, e);\r
13027                           return;\r
13028                         }\r
13029                     }\r
13030                 }else{\r
13031                     if(k == keyCode){\r
13032                         if(this.stopEvent){\r
13033                            e.stopEvent();\r
13034                         }\r
13035                         fn.call(scope || window, k, e);\r
13036                     }\r
13037                 }\r
13038             }\r
13039         };\r
13040         this.bindings.push(handler);\r
13041         },\r
13042     \r
13043     // private\r
13044     checkModifiers: function(config, e){\r
13045         var val, key, keys = ['shift', 'ctrl', 'alt'];\r
13046         for (var i = 0, len = keys.length; i < len; ++i){\r
13047             key = keys[i];\r
13048             val = config[key];\r
13049             if(!(val === undefined || (val === e[key + 'Key']))){\r
13050                 return false;\r
13051             }\r
13052         }\r
13053         return true;\r
13054     },\r
13055 \r
13056     /**\r
13057      * Shorthand for adding a single key listener\r
13058      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the\r
13059      * following options:\r
13060      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
13061      * @param {Function} fn The function to call\r
13062      * @param {Object} scope (optional) The scope of the function\r
13063      */\r
13064     on : function(key, fn, scope){\r
13065         var keyCode, shift, ctrl, alt;\r
13066         if(typeof key == "object" && !Ext.isArray(key)){\r
13067             keyCode = key.key;\r
13068             shift = key.shift;\r
13069             ctrl = key.ctrl;\r
13070             alt = key.alt;\r
13071         }else{\r
13072             keyCode = key;\r
13073         }\r
13074         this.addBinding({\r
13075             key: keyCode,\r
13076             shift: shift,\r
13077             ctrl: ctrl,\r
13078             alt: alt,\r
13079             fn: fn,\r
13080             scope: scope\r
13081         });\r
13082     },\r
13083 \r
13084     // private\r
13085     handleKeyDown : function(e){\r
13086             if(this.enabled){ //just in case\r
13087             var b = this.bindings;\r
13088             for(var i = 0, len = b.length; i < len; i++){\r
13089                 b[i].call(this, e);\r
13090             }\r
13091             }\r
13092         },\r
13093 \r
13094         /**\r
13095          * Returns true if this KeyMap is enabled\r
13096          * @return {Boolean}\r
13097          */\r
13098         isEnabled : function(){\r
13099             return this.enabled;\r
13100         },\r
13101 \r
13102         /**\r
13103          * Enables this KeyMap\r
13104          */\r
13105         enable: function(){\r
13106                 if(!this.enabled){\r
13107                     this.el.on(this.eventName, this.handleKeyDown, this);\r
13108                     this.enabled = true;\r
13109                 }\r
13110         },\r
13111 \r
13112         /**\r
13113          * Disable this KeyMap\r
13114          */\r
13115         disable: function(){\r
13116                 if(this.enabled){\r
13117                     this.el.removeListener(this.eventName, this.handleKeyDown, this);\r
13118                     this.enabled = false;\r
13119                 }\r
13120         },\r
13121     \r
13122     /**\r
13123      * Convenience function for setting disabled/enabled by boolean.\r
13124      * @param {Boolean} disabled\r
13125      */\r
13126     setDisabled : function(disabled){\r
13127         this[disabled ? "disable" : "enable"]();\r
13128     }\r
13129 };/**
13130  * @class Ext.util.TextMetrics
13131  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13132  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13133  * should not contain any HTML, otherwise it may not be measured correctly.
13134  * @singleton
13135  */
13136 Ext.util.TextMetrics = function(){
13137     var shared;
13138     return {
13139         /**
13140          * Measures the size of the specified text
13141          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13142          * that can affect the size of the rendered text
13143          * @param {String} text The text to measure
13144          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13145          * in order to accurately measure the text height
13146          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13147          */
13148         measure : function(el, text, fixedWidth){
13149             if(!shared){
13150                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13151             }
13152             shared.bind(el);
13153             shared.setFixedWidth(fixedWidth || 'auto');
13154             return shared.getSize(text);
13155         },
13156
13157         /**
13158          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13159          * the overhead of multiple calls to initialize the style properties on each measurement.
13160          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13161          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13162          * in order to accurately measure the text height
13163          * @return {Ext.util.TextMetrics.Instance} instance The new instance
13164          */
13165         createInstance : function(el, fixedWidth){
13166             return Ext.util.TextMetrics.Instance(el, fixedWidth);
13167         }
13168     };
13169 }();
13170
13171 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13172     var ml = new Ext.Element(document.createElement('div'));
13173     document.body.appendChild(ml.dom);
13174     ml.position('absolute');
13175     ml.setLeftTop(-1000, -1000);
13176     ml.hide();
13177
13178     if(fixedWidth){
13179         ml.setWidth(fixedWidth);
13180     }
13181
13182     var instance = {
13183         /**
13184          * Returns the size of the specified text based on the internal element's style and width properties
13185          * @param {String} text The text to measure
13186          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13187          */
13188         getSize : function(text){
13189             ml.update(text);
13190             var s = ml.getSize();
13191             ml.update('');
13192             return s;
13193         },
13194
13195         /**
13196          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13197          * that can affect the size of the rendered text
13198          * @param {String/HTMLElement} el The element, dom node or id
13199          */
13200         bind : function(el){
13201             ml.setStyle(
13202                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
13203             );
13204         },
13205
13206         /**
13207          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13208          * to set a fixed width in order to accurately measure the text height.
13209          * @param {Number} width The width to set on the element
13210          */
13211         setFixedWidth : function(width){
13212             ml.setWidth(width);
13213         },
13214
13215         /**
13216          * Returns the measured width of the specified text
13217          * @param {String} text The text to measure
13218          * @return {Number} width The width in pixels
13219          */
13220         getWidth : function(text){
13221             ml.dom.style.width = 'auto';
13222             return this.getSize(text).width;
13223         },
13224
13225         /**
13226          * Returns the measured height of the specified text.  For multiline text, be sure to call
13227          * {@link #setFixedWidth} if necessary.
13228          * @param {String} text The text to measure
13229          * @return {Number} height The height in pixels
13230          */
13231         getHeight : function(text){
13232             return this.getSize(text).height;
13233         }
13234     };
13235
13236     instance.bind(bindTo);
13237
13238     return instance;
13239 };
13240
13241 Ext.Element.addMethods({
13242     /**
13243      * Returns the width in pixels of the passed text, or the width of the text in this Element.
13244      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
13245      * @param {Number} min (Optional) The minumum value to return.
13246      * @param {Number} max (Optional) The maximum value to return.
13247      * @return {Number} The text width in pixels.
13248      * @member Ext.Element getTextWidth
13249      */
13250     getTextWidth : function(text, min, max){
13251         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
13252     }
13253 });
13254 /**\r
13255  * @class Ext.util.Cookies\r
13256  * Utility class for managing and interacting with cookies.\r
13257  * @singleton\r
13258  */\r
13259 Ext.util.Cookies = {\r
13260     /**\r
13261      * Create a cookie with the specified name and value. Additional settings\r
13262      * for the cookie may be optionally specified (for example: expiration,\r
13263      * access restriction, SSL).\r
13264      * @param {Object} name\r
13265      * @param {Object} value\r
13266      * @param {Object} expires (Optional) Specify an expiration date the\r
13267      * cookie is to persist until.  Note that the specified Date object will\r
13268      * be converted to Greenwich Mean Time (GMT). \r
13269      * @param {String} path (Optional) Setting a path on the cookie restricts\r
13270      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). \r
13271      * @param {String} domain (Optional) Setting a domain restricts access to\r
13272      * pages on a given domain (typically used to allow cookie access across\r
13273      * subdomains). For example, "extjs.com" will create a cookie that can be\r
13274      * accessed from any subdomain of extjs.com, including www.extjs.com,\r
13275      * support.extjs.com, etc.\r
13276      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie\r
13277      * should only be accessible via SSL on a page using the HTTPS protocol.\r
13278      * Defaults to <tt>false</tt>. Note that this will only work if the page\r
13279      * calling this code uses the HTTPS protocol, otherwise the cookie will be\r
13280      * created with default options.\r
13281      */\r
13282     set : function(name, value){\r
13283         var argv = arguments;\r
13284         var argc = arguments.length;\r
13285         var expires = (argc > 2) ? argv[2] : null;\r
13286         var path = (argc > 3) ? argv[3] : '/';\r
13287         var domain = (argc > 4) ? argv[4] : null;\r
13288         var secure = (argc > 5) ? argv[5] : false;\r
13289         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");\r
13290     },\r
13291 \r
13292     /**\r
13293      * Retrieves cookies that are accessible by the current page. If a cookie\r
13294      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following\r
13295      * example retrieves the cookie called "valid" and stores the String value\r
13296      * in the variable <tt>validStatus</tt>.\r
13297      * <pre><code>\r
13298      * var validStatus = Ext.util.Cookies.get("valid");\r
13299      * </code></pre>\r
13300      * @param {Object} name The name of the cookie to get\r
13301      * @return {Mixed} Returns the cookie value for the specified name;\r
13302      * null if the cookie name does not exist.\r
13303      */\r
13304     get : function(name){\r
13305         var arg = name + "=";\r
13306         var alen = arg.length;\r
13307         var clen = document.cookie.length;\r
13308         var i = 0;\r
13309         var j = 0;\r
13310         while(i < clen){\r
13311             j = i + alen;\r
13312             if(document.cookie.substring(i, j) == arg){\r
13313                 return Ext.util.Cookies.getCookieVal(j);\r
13314             }\r
13315             i = document.cookie.indexOf(" ", i) + 1;\r
13316             if(i === 0){\r
13317                 break;\r
13318             }\r
13319         }\r
13320         return null;\r
13321     },\r
13322 \r
13323     /**\r
13324      * Removes a cookie with the provided name from the browser\r
13325      * if found.\r
13326      * @param {Object} name The name of the cookie to remove\r
13327      */\r
13328     clear : function(name){\r
13329         if(Ext.util.Cookies.get(name)){\r
13330             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";\r
13331         }\r
13332     },\r
13333     /**\r
13334      * @private\r
13335      */\r
13336     getCookieVal : function(offset){\r
13337         var endstr = document.cookie.indexOf(";", offset);\r
13338         if(endstr == -1){\r
13339             endstr = document.cookie.length;\r
13340         }\r
13341         return unescape(document.cookie.substring(offset, endstr));\r
13342     }\r
13343 };/**
13344  * Framework-wide error-handler.  Developers can override this method to provide
13345  * custom exception-handling.  Framework errors will often extend from the base
13346  * Ext.Error class.
13347  * @param {Object/Error} e The thrown exception object.
13348  */
13349 Ext.handleError = function(e) {
13350     throw e;
13351 };
13352
13353 /**
13354  * @class Ext.Error
13355  * @extends Error
13356  * <p>A base error class. Future implementations are intended to provide more
13357  * robust error handling throughout the framework (<b>in the debug build only</b>)
13358  * to check for common errors and problems. The messages issued by this class
13359  * will aid error checking. Error checks will be automatically removed in the
13360  * production build so that performance is not negatively impacted.</p>
13361  * <p>Some sample messages currently implemented:</p><pre>
13362 "DataProxy attempted to execute an API-action but found an undefined
13363 url / function. Please review your Proxy url/api-configuration."
13364  * </pre><pre>
13365 "Could not locate your "root" property in your server response.
13366 Please review your JsonReader config to ensure the config-property
13367 "root" matches the property your server-response.  See the JsonReader
13368 docs for additional assistance."
13369  * </pre>
13370  * <p>An example of the code used for generating error messages:</p><pre><code>
13371 try {
13372     generateError({
13373         foo: 'bar'
13374     });
13375 }
13376 catch (e) {
13377     console.error(e);
13378 }
13379 function generateError(data) {
13380     throw new Ext.Error('foo-error', data);
13381 }
13382  * </code></pre>
13383  * @param {String} message
13384  */
13385 Ext.Error = function(message) {
13386     // Try to read the message from Ext.Error.lang
13387     this.message = (this.lang[message]) ? this.lang[message] : message;
13388 }
13389 Ext.Error.prototype = new Error();
13390 Ext.apply(Ext.Error.prototype, {
13391     // protected.  Extensions place their error-strings here.
13392     lang: {},
13393
13394     name: 'Ext.Error',
13395     /**
13396      * getName
13397      * @return {String}
13398      */
13399     getName : function() {
13400         return this.name;
13401     },
13402     /**
13403      * getMessage
13404      * @return {String}
13405      */
13406     getMessage : function() {
13407         return this.message;
13408     },
13409     /**
13410      * toJson
13411      * @return {String}
13412      */
13413     toJson : function() {
13414         return Ext.encode(this);
13415     }
13416 });
13417
13418 /**
13419  * @class Ext.ComponentMgr
13420  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
13421  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
13422  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
13423  * <p>This object also provides a registry of available Component <i>classes</i>
13424  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
13425  * The <tt>{@link Ext.Component#xtype xtype}</tt> provides a way to avoid instantiating child Components
13426  * when creating a full, nested config object for a complete Ext page.</p>
13427  * <p>A child Component may be specified simply as a <i>config object</i>
13428  * as long as the correct <tt>{@link Ext.Component#xtype xtype}</tt> is specified so that if and when the Component
13429  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
13430  * <p>For a list of all available <tt>{@link Ext.Component#xtype xtypes}</tt>, see {@link Ext.Component}.</p>
13431  * @singleton
13432  */
13433 Ext.ComponentMgr = function(){
13434     var all = new Ext.util.MixedCollection();
13435     var types = {};
13436     var ptypes = {};
13437
13438     return {
13439         /**
13440          * Registers a component.
13441          * @param {Ext.Component} c The component
13442          */
13443         register : function(c){
13444             all.add(c);
13445         },
13446
13447         /**
13448          * Unregisters a component.
13449          * @param {Ext.Component} c The component
13450          */
13451         unregister : function(c){
13452             all.remove(c);
13453         },
13454
13455         /**
13456          * Returns a component by {@link Ext.Component#id id}.
13457          * For additional details see {@link Ext.util.MixedCollection#get}.
13458          * @param {String} id The component {@link Ext.Component#id id}
13459          * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
13460          * Class was found.
13461          */
13462         get : function(id){
13463             return all.get(id);
13464         },
13465
13466         /**
13467          * Registers a function that will be called when a specified component is added to ComponentMgr
13468          * @param {String} id The component {@link Ext.Component#id id}
13469          * @param {Function} fn The callback function
13470          * @param {Object} scope The scope of the callback
13471          */
13472         onAvailable : function(id, fn, scope){
13473             all.on("add", function(index, o){
13474                 if(o.id == id){
13475                     fn.call(scope || o, o);
13476                     all.un("add", fn, scope);
13477                 }
13478             });
13479         },
13480
13481         /**
13482          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
13483          * events on the MixedCollection to monitor addition or removal.  Read-only.
13484          * @type {MixedCollection}
13485          */
13486         all : all,
13487         
13488         /**
13489          * Checks if a Component type is registered.
13490          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
13491          * @return {Boolean} Whether the type is registered.
13492          */
13493         isRegistered : function(xtype){
13494             return types[xtype] !== undefined;    
13495         },
13496
13497         /**
13498          * <p>Registers a new Component constructor, keyed by a new
13499          * {@link Ext.Component#xtype}.</p>
13500          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
13501          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
13502          * child Components.
13503          * see {@link Ext.Container#items}</p>
13504          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
13505          * @param {Constructor} cls The new Component class.
13506          */
13507         registerType : function(xtype, cls){
13508             types[xtype] = cls;
13509             cls.xtype = xtype;
13510         },
13511
13512         /**
13513          * Creates a new Component from the specified config object using the
13514          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
13515          * @param {Object} config A configuration object for the Component you wish to create.
13516          * @param {Constructor} defaultType The constructor to provide the default Component type if
13517          * the config object does not contain a <tt>xtype</tt>. (Optional if the config contains a <tt>xtype</tt>).
13518          * @return {Ext.Component} The newly instantiated Component.
13519          */
13520         create : function(config, defaultType){
13521             return config.render ? config : new types[config.xtype || defaultType](config);
13522         },
13523
13524         /**
13525          * <p>Registers a new Plugin constructor, keyed by a new
13526          * {@link Ext.Component#ptype}.</p>
13527          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
13528          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
13529          * Plugins.</p>
13530          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
13531          * @param {Constructor} cls The new Plugin class.
13532          */
13533         registerPlugin : function(ptype, cls){
13534             ptypes[ptype] = cls;
13535             cls.ptype = ptype;
13536         },
13537
13538         /**
13539          * Creates a new Plugin from the specified config object using the
13540          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
13541          * @param {Object} config A configuration object for the Plugin you wish to create.
13542          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
13543          * the config object does not contain a <tt>ptype</tt>. (Optional if the config contains a <tt>ptype</tt>).
13544          * @return {Ext.Component} The newly instantiated Plugin.
13545          */
13546         createPlugin : function(config, defaultType){
13547             return new ptypes[config.ptype || defaultType](config);
13548         }
13549     };
13550 }();
13551
13552 /**
13553  * Shorthand for {@link Ext.ComponentMgr#registerType}
13554  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
13555  * may be looked up.
13556  * @param {Constructor} cls The new Component class.
13557  * @member Ext
13558  * @method reg
13559  */
13560 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
13561 /**
13562  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
13563  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
13564  * may be looked up.
13565  * @param {Constructor} cls The new Plugin class.
13566  * @member Ext
13567  * @method preg
13568  */
13569 Ext.preg = Ext.ComponentMgr.registerPlugin;
13570 Ext.create = Ext.ComponentMgr.create;
13571 /**
13572  * @class Ext.Component
13573  * @extends Ext.util.Observable
13574  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
13575  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
13576  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
13577  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
13578  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
13579  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
13580  * {@link Ext#getCmp}, passing the {@link #id}.</p>
13581  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
13582  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
13583  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
13584  * and to either extend or augment ExtJs base classes to create custom Components.</p>
13585  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
13586  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
13587  * <pre>
13588 xtype            Class
13589 -------------    ------------------
13590 box              {@link Ext.BoxComponent}
13591 button           {@link Ext.Button}
13592 buttongroup      {@link Ext.ButtonGroup}
13593 colorpalette     {@link Ext.ColorPalette}
13594 component        {@link Ext.Component}
13595 container        {@link Ext.Container}
13596 cycle            {@link Ext.CycleButton}
13597 dataview         {@link Ext.DataView}
13598 datepicker       {@link Ext.DatePicker}
13599 editor           {@link Ext.Editor}
13600 editorgrid       {@link Ext.grid.EditorGridPanel}
13601 flash            {@link Ext.FlashComponent}
13602 grid             {@link Ext.grid.GridPanel}
13603 listview         {@link Ext.ListView}
13604 panel            {@link Ext.Panel}
13605 progress         {@link Ext.ProgressBar}
13606 propertygrid     {@link Ext.grid.PropertyGrid}
13607 slider           {@link Ext.Slider}
13608 spacer           {@link Ext.Spacer}
13609 splitbutton      {@link Ext.SplitButton}
13610 tabpanel         {@link Ext.TabPanel}
13611 treepanel        {@link Ext.tree.TreePanel}
13612 viewport         {@link Ext.ViewPort}
13613 window           {@link Ext.Window}
13614
13615 Toolbar components
13616 ---------------------------------------
13617 paging           {@link Ext.PagingToolbar}
13618 toolbar          {@link Ext.Toolbar}
13619 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
13620 tbfill           {@link Ext.Toolbar.Fill}
13621 tbitem           {@link Ext.Toolbar.Item}
13622 tbseparator      {@link Ext.Toolbar.Separator}
13623 tbspacer         {@link Ext.Toolbar.Spacer}
13624 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
13625 tbtext           {@link Ext.Toolbar.TextItem}
13626
13627 Menu components
13628 ---------------------------------------
13629 menu             {@link Ext.menu.Menu}
13630 colormenu        {@link Ext.menu.ColorMenu}
13631 datemenu         {@link Ext.menu.DateMenu}
13632 menubaseitem     {@link Ext.menu.BaseItem}
13633 menucheckitem    {@link Ext.menu.CheckItem}
13634 menuitem         {@link Ext.menu.Item}
13635 menuseparator    {@link Ext.menu.Separator}
13636 menutextitem     {@link Ext.menu.TextItem}
13637
13638 Form components
13639 ---------------------------------------
13640 form             {@link Ext.FormPanel}
13641 checkbox         {@link Ext.form.Checkbox}
13642 checkboxgroup    {@link Ext.form.CheckboxGroup}
13643 combo            {@link Ext.form.ComboBox}
13644 datefield        {@link Ext.form.DateField}
13645 displayfield     {@link Ext.form.DisplayField}
13646 field            {@link Ext.form.Field}
13647 fieldset         {@link Ext.form.FieldSet}
13648 hidden           {@link Ext.form.Hidden}
13649 htmleditor       {@link Ext.form.HtmlEditor}
13650 label            {@link Ext.form.Label}
13651 numberfield      {@link Ext.form.NumberField}
13652 radio            {@link Ext.form.Radio}
13653 radiogroup       {@link Ext.form.RadioGroup}
13654 textarea         {@link Ext.form.TextArea}
13655 textfield        {@link Ext.form.TextField}
13656 timefield        {@link Ext.form.TimeField}
13657 trigger          {@link Ext.form.TriggerField}
13658
13659 Chart components
13660 ---------------------------------------
13661 chart            {@link Ext.chart.Chart}
13662 barchart         {@link Ext.chart.BarChart}
13663 cartesianchart   {@link Ext.chart.CartesianChart}
13664 columnchart      {@link Ext.chart.ColumnChart}
13665 linechart        {@link Ext.chart.LineChart}
13666 piechart         {@link Ext.chart.PieChart}
13667
13668 Store xtypes
13669 ---------------------------------------
13670 arraystore       {@link Ext.data.ArrayStore}
13671 directstore      {@link Ext.data.DirectStore}
13672 groupingstore    {@link Ext.data.GroupingStore}
13673 jsonstore        {@link Ext.data.JsonStore}
13674 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
13675 store            {@link Ext.data.Store}
13676 xmlstore         {@link Ext.data.XmlStore}
13677 </pre>
13678  * @constructor
13679  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
13680  * <div class="mdetail-params"><ul>
13681  * <li><b>an element</b> :
13682  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
13683  * <li><b>a string</b> :
13684  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
13685  * <li><b>anything else</b> :
13686  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
13687  * </ul></div>
13688  */
13689 Ext.Component = function(config){
13690     config = config || {};
13691     if(config.initialConfig){
13692         if(config.isAction){           // actions
13693             this.baseAction = config;
13694         }
13695         config = config.initialConfig; // component cloning / action set up
13696     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
13697         config = {applyTo: config, id: config.id || config};
13698     }
13699
13700     /**
13701      * This Component's initial configuration specification. Read-only.
13702      * @type Object
13703      * @property initialConfig
13704      */
13705     this.initialConfig = config;
13706
13707     Ext.apply(this, config);
13708     this.addEvents(
13709         /**
13710          * @event disable
13711          * Fires after the component is disabled.
13712          * @param {Ext.Component} this
13713          */
13714         'disable',
13715         /**
13716          * @event enable
13717          * Fires after the component is enabled.
13718          * @param {Ext.Component} this
13719          */
13720         'enable',
13721         /**
13722          * @event beforeshow
13723          * Fires before the component is shown by calling the {@link #show} method.
13724          * Return false from an event handler to stop the show.
13725          * @param {Ext.Component} this
13726          */
13727         'beforeshow',
13728         /**
13729          * @event show
13730          * Fires after the component is shown when calling the {@link #show} method.
13731          * @param {Ext.Component} this
13732          */
13733         'show',
13734         /**
13735          * @event beforehide
13736          * Fires before the component is hidden by calling the {@link #hide} method.
13737          * Return false from an event handler to stop the hide.
13738          * @param {Ext.Component} this
13739          */
13740         'beforehide',
13741         /**
13742          * @event hide
13743          * Fires after the component is hidden.
13744          * Fires after the component is hidden when calling the {@link #hide} method.
13745          * @param {Ext.Component} this
13746          */
13747         'hide',
13748         /**
13749          * @event beforerender
13750          * Fires before the component is {@link #rendered}. Return false from an
13751          * event handler to stop the {@link #render}.
13752          * @param {Ext.Component} this
13753          */
13754         'beforerender',
13755         /**
13756          * @event render
13757          * Fires after the component markup is {@link #rendered}.
13758          * @param {Ext.Component} this
13759          */
13760         'render',
13761         /**
13762          * @event afterrender
13763          * <p>Fires after the component rendering is finished.</p>
13764          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
13765          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
13766          * has been restored.</p>
13767          * @param {Ext.Component} this
13768          */
13769         'afterrender',
13770         /**
13771          * @event beforedestroy
13772          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
13773          * @param {Ext.Component} this
13774          */
13775         'beforedestroy',
13776         /**
13777          * @event destroy
13778          * Fires after the component is {@link #destroy}ed.
13779          * @param {Ext.Component} this
13780          */
13781         'destroy',
13782         /**
13783          * @event beforestaterestore
13784          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
13785          * @param {Ext.Component} this
13786          * @param {Object} state The hash of state values returned from the StateProvider. If this
13787          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
13788          * that simply copies property values into this Component. The method maybe overriden to
13789          * provide custom state restoration.
13790          */
13791         'beforestaterestore',
13792         /**
13793          * @event staterestore
13794          * Fires after the state of the component is restored.
13795          * @param {Ext.Component} this
13796          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
13797          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
13798          * Component. The method maybe overriden to provide custom state restoration.
13799          */
13800         'staterestore',
13801         /**
13802          * @event beforestatesave
13803          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
13804          * @param {Ext.Component} this
13805          * @param {Object} state The hash of state values. This is determined by calling
13806          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
13807          * developer to return whetever representation of state is required, by default, Ext.Component
13808          * has a null implementation.
13809          */
13810         'beforestatesave',
13811         /**
13812          * @event statesave
13813          * Fires after the state of the component is saved to the configured state provider.
13814          * @param {Ext.Component} this
13815          * @param {Object} state The hash of state values. This is determined by calling
13816          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
13817          * developer to return whetever representation of state is required, by default, Ext.Component
13818          * has a null implementation.
13819          */
13820         'statesave'
13821     );
13822     this.getId();
13823     Ext.ComponentMgr.register(this);
13824     Ext.Component.superclass.constructor.call(this);
13825
13826     if(this.baseAction){
13827         this.baseAction.addComponent(this);
13828     }
13829
13830     this.initComponent();
13831
13832     if(this.plugins){
13833         if(Ext.isArray(this.plugins)){
13834             for(var i = 0, len = this.plugins.length; i < len; i++){
13835                 this.plugins[i] = this.initPlugin(this.plugins[i]);
13836             }
13837         }else{
13838             this.plugins = this.initPlugin(this.plugins);
13839         }
13840     }
13841
13842     if(this.stateful !== false){
13843         this.initState(config);
13844     }
13845
13846     if(this.applyTo){
13847         this.applyToMarkup(this.applyTo);
13848         delete this.applyTo;
13849     }else if(this.renderTo){
13850         this.render(this.renderTo);
13851         delete this.renderTo;
13852     }
13853 };
13854
13855 // private
13856 Ext.Component.AUTO_ID = 1000;
13857
13858 Ext.extend(Ext.Component, Ext.util.Observable, {
13859         // Configs below are used for all Components when rendered by FormLayout.
13860     /**
13861      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
13862      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
13863      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
13864      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
13865      * <p>Also see <tt>{@link #hideLabel}</tt> and
13866      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
13867      * Example use:<pre><code>
13868 new Ext.FormPanel({
13869     height: 100,
13870     renderTo: Ext.getBody(),
13871     items: [{
13872         xtype: 'textfield',
13873         fieldLabel: 'Name'
13874     }]
13875 });
13876 </code></pre>
13877      */
13878     /**
13879      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
13880      * label.  Defaults to the container's labelStyle value if set (e.g.,
13881      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
13882      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
13883      * <p>Also see <code>{@link #hideLabel}</code> and
13884      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
13885      * Example use:<pre><code>
13886 new Ext.FormPanel({
13887     height: 100,
13888     renderTo: Ext.getBody(),
13889     items: [{
13890         xtype: 'textfield',
13891         fieldLabel: 'Name',
13892         labelStyle: 'font-weight:bold;'
13893     }]
13894 });
13895 </code></pre>
13896      */
13897     /**
13898      * @cfg {String} labelSeparator <p>The separator to display after the text of each
13899      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
13900      * The order of precedence is:
13901      * <div class="mdetail-params"><ul>
13902      * <li>field / component level</li>
13903      * <li>container level</li>
13904      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
13905      * </ul></div>
13906      * To display no separator for this field's label specify empty string ''.</p>
13907      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
13908      * <p>Also see <tt>{@link #hideLabel}</tt> and
13909      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
13910      * Example use:<pre><code>
13911 new Ext.FormPanel({
13912     height: 100,
13913     renderTo: Ext.getBody(),
13914     layoutConfig: {
13915         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
13916     },
13917     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
13918     items: [{
13919         xtype: 'textfield',
13920         fieldLabel: 'Field 1',
13921         labelSeparator: '...' // field/component level config supersedes others
13922     },{
13923         xtype: 'textfield',
13924         fieldLabel: 'Field 2' // labelSeparator will be '='
13925     }]
13926 });
13927 </code></pre>
13928      */
13929     /**
13930      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
13931      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
13932      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
13933      * reserved so that the field will line up with other fields that do have labels.
13934      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
13935      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
13936      * Example use:<pre><code>
13937 new Ext.FormPanel({
13938     height: 100,
13939     renderTo: Ext.getBody(),
13940     items: [{
13941         xtype: 'textfield'
13942         hideLabel: true
13943     }]
13944 });
13945 </code></pre>
13946      */
13947     /**
13948      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
13949      * directly after each form field wrapper to provide field clearing (defaults to
13950      * <tt>'x-form-clear-left'</tt>).</p>
13951      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
13952      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
13953      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
13954      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
13955      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
13956      */
13957     /**
13958      * @cfg {String} itemCls <p>An additional CSS class to apply to the div wrapping the form item
13959      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
13960      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
13961      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
13962      * <p>Since it is applied to the item wrapper (see
13963      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
13964      * you to write standard CSS rules that can apply to the field, the label (if specified), or
13965      * any other element within the markup for the field.</p>
13966      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
13967      * Example use:<pre><code>
13968 // Apply a style to the field's label:
13969 &lt;style>
13970     .required .x-form-item-label {font-weight:bold;color:red;}
13971 &lt;/style>
13972
13973 new Ext.FormPanel({
13974         height: 100,
13975         renderTo: Ext.getBody(),
13976         items: [{
13977                 xtype: 'textfield',
13978                 fieldLabel: 'Name',
13979                 itemCls: 'required' //this label will be styled
13980         },{
13981                 xtype: 'textfield',
13982                 fieldLabel: 'Favorite Color'
13983         }]
13984 });
13985 </code></pre>
13986      */
13987
13988         // Configs below are used for all Components when rendered by AnchorLayout.
13989     /**
13990      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
13991      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout}</b>
13992      * based layout manager, for example:<div class="mdetail-params"><ul>
13993      * <li>{@link Ext.form.FormPanel}</li>
13994      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
13995      * </ul></div></p>
13996      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
13997      */
13998
13999     /**
14000      * @cfg {String} id
14001      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
14002      * You should assign an id if you need to be able to access the component later and you do
14003      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
14004      * <p>Note that this id will also be used as the element id for the containing HTML element
14005      * that is rendered to the page for this component. This allows you to write id-based CSS
14006      * rules to style the specific instance of this component uniquely, and also to select
14007      * sub-elements using this component's id as the parent.</p>
14008      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
14009      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
14010      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
14011      */
14012     /**
14013      * @cfg {String} itemId
14014      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
14015      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
14016      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
14017      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
14018      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
14019      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
14020      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
14021      * <code>{@link #id}</code>.</p>
14022      * <pre><code>
14023 var c = new Ext.Panel({ //
14024     {@link Ext.BoxComponent#height height}: 300,
14025     {@link #renderTo}: document.body,
14026     {@link Ext.Container#layout layout}: 'auto',
14027     {@link Ext.Container#items items}: [
14028         {
14029             itemId: 'p1',
14030             {@link Ext.Panel#title title}: 'Panel 1',
14031             {@link Ext.BoxComponent#height height}: 150
14032         },
14033         {
14034             itemId: 'p2',
14035             {@link Ext.Panel#title title}: 'Panel 2',
14036             {@link Ext.BoxComponent#height height}: 150
14037         }
14038     ]
14039 })
14040 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
14041 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
14042      * </code></pre>
14043      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
14044      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
14045      */
14046     /**
14047      * @cfg {String} xtype
14048      * The registered <tt>xtype</tt> to create. This config option is not used when passing
14049      * a config object into a constructor. This config option is used only when
14050      * lazy instantiation is being used, and a child item of a Container is being
14051      * specified not as a fully instantiated Component, but as a <i>Component config
14052      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
14053      * type of child Component to create.<br><br>
14054      * The predefined xtypes are listed {@link Ext.Component here}.
14055      * <br><br>
14056      * If you subclass Components to create your own Components, you may register
14057      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
14058      * take advantage of lazy instantiation and rendering.
14059      */
14060     /**
14061      * @cfg {String} ptype
14062      * The registered <tt>ptype</tt> to create. This config option is not used when passing
14063      * a config object into a constructor. This config option is used only when
14064      * lazy instantiation is being used, and a Plugin is being
14065      * specified not as a fully instantiated Component, but as a <i>Component config
14066      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
14067      * type of Plugin to create.<br><br>
14068      * If you create your own Plugins, you may register them using
14069      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
14070      * take advantage of lazy instantiation and rendering.
14071      */
14072     /**
14073      * @cfg {String} cls
14074      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
14075      * useful for adding customized styles to the component or any of its children using standard CSS rules.
14076      */
14077     /**
14078      * @cfg {String} overCls
14079      * An optional extra CSS class that will be added to this component's Element when the mouse moves
14080      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
14081      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
14082      */
14083     /**
14084      * @cfg {String} style
14085      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
14086      * {@link Ext.Element#applyStyles}.
14087      * <pre><code>
14088 new Ext.Panel({
14089     title: 'Some Title',
14090     renderTo: Ext.getBody(),
14091     width: 400, height: 300,
14092     layout: 'form',
14093     items: [{
14094         xtype: 'textarea',
14095         style: {
14096             width: '95%',
14097             marginBottom: '10px'
14098         }
14099     },
14100         new Ext.Button({
14101             text: 'Send',
14102             minWidth: '100',
14103             style: {
14104                 marginBottom: '10px'
14105             }
14106         })
14107     ]
14108 });
14109      * </code></pre>
14110      */
14111     /**
14112      * @cfg {String} ctCls
14113      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
14114      * adding customized styles to the container or any of its children using standard CSS rules.  See
14115      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
14116      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
14117      * which assigns a value by default:
14118      * <div class="mdetail-params"><ul>
14119      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
14120      * </ul></div>
14121      * To configure the above Class with an extra CSS class append to the default.  For example,
14122      * for BoxLayout (Hbox and Vbox):<pre><code>
14123      * ctCls: 'x-box-layout-ct custom-class'
14124      * </code></pre>
14125      * </p>
14126      */
14127     /**
14128      * @cfg {Boolean} disabled
14129      * Render this component disabled (default is false).
14130      */
14131     disabled : false,
14132     /**
14133      * @cfg {Boolean} hidden
14134      * Render this component hidden (default is false). If <tt>true</tt>, the
14135      * {@link #hide} method will be called internally.
14136      */
14137     hidden : false,
14138     /**
14139      * @cfg {Object/Array} plugins
14140      * An object or array of objects that will provide custom functionality for this component.  The only
14141      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
14142      * When a component is created, if any plugins are available, the component will call the init method on each
14143      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
14144      * component as needed to provide its functionality.
14145      */
14146     /**
14147      * @cfg {Mixed} applyTo
14148      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
14149      * that is already present in the document that specifies some structural markup for this
14150      * component.</p><div><ul>
14151      * <li><b>Description</b> : <ul>
14152      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
14153      * by id or CSS class name within the main element, and the component being created may attempt
14154      * to create its subcomponents from that markup if applicable.</div>
14155      * </ul></li>
14156      * <li><b>Notes</b> : <ul>
14157      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
14158      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
14159      * element's parent node will automatically be used as the component's container.</div>
14160      * </ul></li>
14161      * </ul></div>
14162      */
14163     /**
14164      * @cfg {Mixed} renderTo
14165      * <p>Specify the id of the element, a DOM element or an existing Element that this component
14166      * will be rendered into.</p><div><ul>
14167      * <li><b>Notes</b> : <ul>
14168      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
14169      * a {@link Ext.Container Container}. It is the responsibility of the
14170      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
14171      * to render and manage its child items.</div>
14172      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
14173      * </ul></li>
14174      * </ul></div>
14175      * <p>See <tt>{@link #render}</tt> also.</p>
14176      */
14177     /**
14178      * @cfg {Boolean} stateful
14179      * <p>A flag which causes the Component to attempt to restore the state of
14180      * internal properties from a saved state on startup. The component must have
14181      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
14182      * for state to be managed. Auto-generated ids are not guaranteed to be stable
14183      * across page loads and cannot be relied upon to save and restore the same
14184      * state for a component.<p>
14185      * <p>For state saving to work, the state manager's provider must have been
14186      * set to an implementation of {@link Ext.state.Provider} which overrides the
14187      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
14188      * methods to save and recall name/value pairs. A built-in implementation,
14189      * {@link Ext.state.CookieProvider} is available.</p>
14190      * <p>To set the state provider for the current page:</p>
14191      * <pre><code>
14192 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
14193     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
14194 }));
14195      * </code></pre>
14196      * <p>A stateful Component attempts to save state when one of the events
14197      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
14198      * <p>To save state, a stateful Component first serializes its state by
14199      * calling <b><code>getState</code></b>. By default, this function does
14200      * nothing. The developer must provide an implementation which returns an
14201      * object hash which represents the Component's restorable state.</p>
14202      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
14203      * which uses the configured {@link Ext.state.Provider} to save the object
14204      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
14205      * specified, its <code>{@link #id}</code>.</p>
14206      * <p>During construction, a stateful Component attempts to <i>restore</i>
14207      * its state by calling {@link Ext.state.Manager#get} passing the
14208      * <code>{@link #stateId}</code>, or, if that is not specified, the
14209      * <code>{@link #id}</code>.</p>
14210      * <p>The resulting object is passed to <b><code>applyState</code></b>.
14211      * The default implementation of <code>applyState</code> simply copies
14212      * properties into the object, but a developer may override this to support
14213      * more behaviour.</p>
14214      * <p>You can perform extra processing on state save and restore by attaching
14215      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
14216      * {@link #beforestatesave} and {@link #statesave} events.</p>
14217      */
14218     /**
14219      * @cfg {String} stateId
14220      * The unique id for this component to use for state management purposes
14221      * (defaults to the component id if one was set, otherwise null if the
14222      * component is using a generated id).
14223      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
14224      * restoring Component state.</p>
14225      */
14226     /**
14227      * @cfg {Array} stateEvents
14228      * <p>An array of events that, when fired, should trigger this component to
14229      * save its state (defaults to none). <code>stateEvents</code> may be any type
14230      * of event supported by this component, including browser or custom events
14231      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
14232      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
14233      * restoring Component state.</p>
14234      */
14235     /**
14236      * @cfg {Mixed} autoEl
14237      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
14238      * encapsulate this Component.</p>
14239      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
14240      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
14241      * DOM structure created by their own onRender methods.</p>
14242      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
14243      * different DOM elements. Example usage:</p><pre><code>
14244 {
14245     xtype: 'box',
14246     autoEl: {
14247         tag: 'img',
14248         src: 'http://www.example.com/example.jpg'
14249     }
14250 }, {
14251     xtype: 'box',
14252     autoEl: {
14253         tag: 'blockquote',
14254         html: 'autoEl is cool!'
14255     }
14256 }, {
14257     xtype: 'container',
14258     autoEl: 'ul',
14259     cls: 'ux-unordered-list',
14260     items: {
14261         xtype: 'box',
14262         autoEl: 'li',
14263         html: 'First list item'
14264     }
14265 }
14266 </code></pre>
14267      */
14268     autoEl : 'div',
14269
14270     /**
14271      * @cfg {String} disabledClass
14272      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
14273      */
14274     disabledClass : 'x-item-disabled',
14275     /**
14276      * @cfg {Boolean} allowDomMove
14277      * Whether the component can move the Dom node when rendering (defaults to true).
14278      */
14279     allowDomMove : true,
14280     /**
14281      * @cfg {Boolean} autoShow
14282      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
14283      * them on render (defaults to false).
14284      */
14285     autoShow : false,
14286     /**
14287      * @cfg {String} hideMode
14288      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
14289      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
14290      * (css display).</p>
14291      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
14292      * since items are automatically laid out when they are first shown (no sizing
14293      * is done while hidden).</p>
14294      */
14295     hideMode : 'display',
14296     /**
14297      * @cfg {Boolean} hideParent
14298      * True to hide and show the component's container when hide/show is called on the component, false to hide
14299      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
14300      * button on a window by setting hide:true on the button when adding it to its parent container.
14301      */
14302     hideParent : false,
14303     /**
14304      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
14305      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
14306      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
14307      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
14308      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
14309      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
14310      * config for a suggestion, or use a render listener directly:</p><pre><code>
14311 new Ext.Panel({
14312     title: 'The Clickable Panel',
14313     listeners: {
14314         render: function(p) {
14315             // Append the Panel to the click handler&#39;s argument list.
14316             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
14317         },
14318         single: true  // Remove the listener after first invocation
14319     }
14320 });
14321 </code></pre>
14322      * <p>See also <tt>{@link #getEl getEl}</p>
14323      * @type Ext.Element
14324      * @property el
14325      */
14326     /**
14327      * The component's owner {@link Ext.Container} (defaults to undefined, and is set automatically when
14328      * the component is added to a container).  Read-only.
14329      * <p><b>Note</b>: to access items within the container see <tt>{@link #itemId}</tt>.</p>
14330      * @type Ext.Container
14331      * @property ownerCt
14332      */
14333     /**
14334      * True if this component is hidden. Read-only.
14335      * @type Boolean
14336      * @property
14337      */
14338     /**
14339      * True if this component is disabled. Read-only.
14340      * @type Boolean
14341      * @property
14342      */
14343     /**
14344      * True if this component has been rendered. Read-only.
14345      * @type Boolean
14346      * @property
14347      */
14348     rendered : false,
14349
14350     // private
14351     ctype : 'Ext.Component',
14352
14353     // private
14354     actionMode : 'el',
14355
14356     // private
14357     getActionEl : function(){
14358         return this[this.actionMode];
14359     },
14360
14361     initPlugin : function(p){
14362         if(p.ptype && !Ext.isFunction(p.init)){
14363             p = Ext.ComponentMgr.createPlugin(p);
14364         }else if(Ext.isString(p)){
14365             p = Ext.ComponentMgr.createPlugin({
14366                 ptype: p
14367             });
14368         }
14369         p.init(this);
14370         return p;
14371     },
14372
14373     /* // protected
14374      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
14375      * <pre><code>
14376 // Traditional constructor:
14377 Ext.Foo = function(config){
14378     // call superclass constructor:
14379     Ext.Foo.superclass.constructor.call(this, config);
14380
14381     this.addEvents({
14382         // add events
14383     });
14384 };
14385 Ext.extend(Ext.Foo, Ext.Bar, {
14386    // class body
14387 }
14388
14389 // initComponent replaces the constructor:
14390 Ext.Foo = Ext.extend(Ext.Bar, {
14391     initComponent : function(){
14392         // call superclass initComponent
14393         Ext.Container.superclass.initComponent.call(this);
14394
14395         this.addEvents({
14396             // add events
14397         });
14398     }
14399 }
14400 </code></pre>
14401      */
14402     initComponent : Ext.emptyFn,
14403
14404     /**
14405      * <p>Render this Component into the passed HTML element.</p>
14406      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
14407      * do not use the render method.</b></p>
14408      * <p>A Container's child Components are rendered by that Container's
14409      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
14410      * <p>Certain layout managers allow dynamic addition of child components. Those that do
14411      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
14412      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
14413      * <p>If the Container is already rendered when a new child Component is added, you may need to call
14414      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
14415      * unrendered child Components to be rendered. This is required so that you can add multiple
14416      * child components if needed while only refreshing the layout once.</p>
14417      * <p>When creating complex UIs, it is important to remember that sizing and positioning
14418      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
14419      * If you expect child items to be sized in response to user interactions, you must
14420      * configure the Container with a layout manager which creates and manages the type of layout you
14421      * have in mind.</p>
14422      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
14423      * layout manager is used which does nothing but render child components sequentially into the
14424      * Container. No sizing or positioning will be performed in this situation.</b></p>
14425      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
14426      * rendered into. If it is being created from existing markup, this should be omitted.
14427      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
14428      * which this component will be inserted (defaults to appending to the end of the container)
14429      */
14430     render : function(container, position){
14431         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
14432             if(!container && this.el){
14433                 this.el = Ext.get(this.el);
14434                 container = this.el.dom.parentNode;
14435                 this.allowDomMove = false;
14436             }
14437             this.container = Ext.get(container);
14438             if(this.ctCls){
14439                 this.container.addClass(this.ctCls);
14440             }
14441             this.rendered = true;
14442             if(position !== undefined){
14443                 if(Ext.isNumber(position)){
14444                     position = this.container.dom.childNodes[position];
14445                 }else{
14446                     position = Ext.getDom(position);
14447                 }
14448             }
14449             this.onRender(this.container, position || null);
14450             if(this.autoShow){
14451                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
14452             }
14453             if(this.cls){
14454                 this.el.addClass(this.cls);
14455                 delete this.cls;
14456             }
14457             if(this.style){
14458                 this.el.applyStyles(this.style);
14459                 delete this.style;
14460             }
14461             if(this.overCls){
14462                 this.el.addClassOnOver(this.overCls);
14463             }
14464             this.fireEvent('render', this);
14465             this.afterRender(this.container);
14466             if(this.hidden){
14467                 // call this so we don't fire initial hide events.
14468                 this.doHide();
14469             }
14470             if(this.disabled){
14471                 // pass silent so the event doesn't fire the first time.
14472                 this.disable(true);
14473             }
14474
14475             if(this.stateful !== false){
14476                 this.initStateEvents();
14477             }
14478             this.initRef();
14479             this.fireEvent('afterrender', this);
14480         }
14481         return this;
14482     },
14483
14484     initRef : function(){
14485         /**
14486          * @cfg {String} ref
14487          * <p>A path specification, relative to the Component's {@link #ownerCt} specifying into which
14488          * ancestor Container to place a named reference to this Component.</p>
14489          * <p>The ancestor axis can be traversed by using '/' characters in the path.
14490          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
14491 var myGrid = new Ext.grid.EditorGridPanel({
14492     title: 'My EditorGridPanel',
14493     store: myStore,
14494     colModel: myColModel,
14495     tbar: [{
14496         text: 'Save',
14497         handler: saveChanges,
14498         disabled: true,
14499         ref: '../saveButton'
14500     }],
14501     listeners: {
14502         afteredit: function() {
14503 //          The button reference is in the GridPanel
14504             myGrid.saveButton.enable();
14505         }
14506     }
14507 });
14508 </code></pre>
14509          * <p>In the code above, if the ref had been <code>'saveButton'</code> the reference would
14510          * have been placed into the Toolbar. Each '/' in the ref moves up one level from the
14511          * Component's {@link #ownerCt}.</p>
14512          */
14513         if(this.ref){
14514             var levels = this.ref.split('/');
14515             var last = levels.length, i = 0;
14516             var t = this;
14517             while(i < last){
14518                 if(t.ownerCt){
14519                     t = t.ownerCt;
14520                 }
14521                 i++;
14522             }
14523             t[levels[--i]] = this;
14524         }
14525     },
14526
14527     // private
14528     initState : function(config){
14529         if(Ext.state.Manager){
14530             var id = this.getStateId();
14531             if(id){
14532                 var state = Ext.state.Manager.get(id);
14533                 if(state){
14534                     if(this.fireEvent('beforestaterestore', this, state) !== false){
14535                         this.applyState(state);
14536                         this.fireEvent('staterestore', this, state);
14537                     }
14538                 }
14539             }
14540         }
14541     },
14542
14543     // private
14544     getStateId : function(){
14545         return this.stateId || ((this.id.indexOf('ext-comp-') == 0 || this.id.indexOf('ext-gen') == 0) ? null : this.id);
14546     },
14547
14548     // private
14549     initStateEvents : function(){
14550         if(this.stateEvents){
14551             for(var i = 0, e; e = this.stateEvents[i]; i++){
14552                 this.on(e, this.saveState, this, {delay:100});
14553             }
14554         }
14555     },
14556
14557     // private
14558     applyState : function(state, config){
14559         if(state){
14560             Ext.apply(this, state);
14561         }
14562     },
14563
14564     // private
14565     getState : function(){
14566         return null;
14567     },
14568
14569     // private
14570     saveState : function(){
14571         if(Ext.state.Manager && this.stateful !== false){
14572             var id = this.getStateId();
14573             if(id){
14574                 var state = this.getState();
14575                 if(this.fireEvent('beforestatesave', this, state) !== false){
14576                     Ext.state.Manager.set(id, state);
14577                     this.fireEvent('statesave', this, state);
14578                 }
14579             }
14580         }
14581     },
14582
14583     /**
14584      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
14585      * @param {String/HTMLElement} el
14586      */
14587     applyToMarkup : function(el){
14588         this.allowDomMove = false;
14589         this.el = Ext.get(el);
14590         this.render(this.el.dom.parentNode);
14591     },
14592
14593     /**
14594      * Adds a CSS class to the component's underlying element.
14595      * @param {string} cls The CSS class name to add
14596      * @return {Ext.Component} this
14597      */
14598     addClass : function(cls){
14599         if(this.el){
14600             this.el.addClass(cls);
14601         }else{
14602             this.cls = this.cls ? this.cls + ' ' + cls : cls;
14603         }
14604         return this;
14605     },
14606
14607     /**
14608      * Removes a CSS class from the component's underlying element.
14609      * @param {string} cls The CSS class name to remove
14610      * @return {Ext.Component} this
14611      */
14612     removeClass : function(cls){
14613         if(this.el){
14614             this.el.removeClass(cls);
14615         }else if(this.cls){
14616             this.cls = this.cls.split(' ').remove(cls).join(' ');
14617         }
14618         return this;
14619     },
14620
14621     // private
14622     // default function is not really useful
14623     onRender : function(ct, position){
14624         if(!this.el && this.autoEl){
14625             if(Ext.isString(this.autoEl)){
14626                 this.el = document.createElement(this.autoEl);
14627             }else{
14628                 var div = document.createElement('div');
14629                 Ext.DomHelper.overwrite(div, this.autoEl);
14630                 this.el = div.firstChild;
14631             }
14632             if (!this.el.id) {
14633                 this.el.id = this.getId();
14634             }
14635         }
14636         if(this.el){
14637             this.el = Ext.get(this.el);
14638             if(this.allowDomMove !== false){
14639                 ct.dom.insertBefore(this.el.dom, position);
14640             }
14641         }
14642     },
14643
14644     // private
14645     getAutoCreate : function(){
14646         var cfg = Ext.isObject(this.autoCreate) ?
14647                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
14648         if(this.id && !cfg.id){
14649             cfg.id = this.id;
14650         }
14651         return cfg;
14652     },
14653
14654     // private
14655     afterRender : Ext.emptyFn,
14656
14657     /**
14658      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
14659      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
14660      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
14661      * should usually not need to be called directly.
14662      *
14663      */
14664     destroy : function(){
14665         if(this.fireEvent('beforedestroy', this) !== false){
14666             this.beforeDestroy();
14667             if(this.rendered){
14668                 this.el.removeAllListeners();
14669                 this.el.remove();
14670                 if(this.actionMode == 'container' || this.removeMode == 'container'){
14671                     this.container.remove();
14672                 }
14673             }
14674             this.onDestroy();
14675             Ext.ComponentMgr.unregister(this);
14676             this.fireEvent('destroy', this);
14677             this.purgeListeners();
14678         }
14679     },
14680
14681     // private
14682     beforeDestroy : Ext.emptyFn,
14683
14684     // private
14685     onDestroy  : Ext.emptyFn,
14686
14687     /**
14688      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
14689      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
14690      * that may be overridden using the {@link #autoEl} config.</p>
14691      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
14692      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
14693      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
14694      * or use a render listener directly:</p><pre><code>
14695 new Ext.Panel({
14696     title: 'The Clickable Panel',
14697     listeners: {
14698         render: function(p) {
14699             // Append the Panel to the click handler&#39;s argument list.
14700             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
14701         },
14702         single: true  // Remove the listener after first invocation
14703     }
14704 });
14705 </code></pre>
14706      * @return {Ext.Element} The Element which encapsulates this Component.
14707      */
14708     getEl : function(){
14709         return this.el;
14710     },
14711
14712     /**
14713      * Returns the <code>id</code> of this component or automatically generates and
14714      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
14715      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
14716      * </code></pre>
14717      * @return {String} id
14718      */
14719     getId : function(){
14720         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
14721     },
14722
14723     /**
14724      * Returns the <code>{@link #itemId}</code> of this component.  If an
14725      * <code>{@link #itemId}</code> was not assigned through configuration the
14726      * <code>id</code> is returned using <code>{@link #getId}</code>.
14727      * @return {String}
14728      */
14729     getItemId : function(){
14730         return this.itemId || this.getId();
14731     },
14732
14733     /**
14734      * Try to focus this component.
14735      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
14736      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
14737      * @return {Ext.Component} this
14738      */
14739     focus : function(selectText, delay){
14740         if(delay){
14741             this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]);
14742             return;
14743         }
14744         if(this.rendered){
14745             this.el.focus();
14746             if(selectText === true){
14747                 this.el.dom.select();
14748             }
14749         }
14750         return this;
14751     },
14752
14753     // private
14754     blur : function(){
14755         if(this.rendered){
14756             this.el.blur();
14757         }
14758         return this;
14759     },
14760
14761     /**
14762      * Disable this component and fire the 'disable' event.
14763      * @return {Ext.Component} this
14764      */
14765     disable : function(/* private */ silent){
14766         if(this.rendered){
14767             this.onDisable();
14768         }
14769         this.disabled = true;
14770         if(silent !== true){
14771             this.fireEvent('disable', this);
14772         }
14773         return this;
14774     },
14775
14776     // private
14777     onDisable : function(){
14778         this.getActionEl().addClass(this.disabledClass);
14779         this.el.dom.disabled = true;
14780     },
14781
14782     /**
14783      * Enable this component and fire the 'enable' event.
14784      * @return {Ext.Component} this
14785      */
14786     enable : function(){
14787         if(this.rendered){
14788             this.onEnable();
14789         }
14790         this.disabled = false;
14791         this.fireEvent('enable', this);
14792         return this;
14793     },
14794
14795     // private
14796     onEnable : function(){
14797         this.getActionEl().removeClass(this.disabledClass);
14798         this.el.dom.disabled = false;
14799     },
14800
14801     /**
14802      * Convenience function for setting disabled/enabled by boolean.
14803      * @param {Boolean} disabled
14804      * @return {Ext.Component} this
14805      */
14806     setDisabled : function(disabled){
14807         return this[disabled ? 'disable' : 'enable']();
14808     },
14809
14810     /**
14811      * Show this component.  Listen to the '{@link #beforeshow}' event and return
14812      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
14813      * event after showing the component.
14814      * @return {Ext.Component} this
14815      */
14816     show : function(){
14817         if(this.fireEvent('beforeshow', this) !== false){
14818             this.hidden = false;
14819             if(this.autoRender){
14820                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
14821             }
14822             if(this.rendered){
14823                 this.onShow();
14824             }
14825             this.fireEvent('show', this);
14826         }
14827         return this;
14828     },
14829
14830     // private
14831     onShow : function(){
14832         this.getVisibiltyEl().removeClass('x-hide-' + this.hideMode);
14833     },
14834
14835     /**
14836      * Hide this component.  Listen to the '{@link #beforehide}' event and return
14837      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
14838      * event after hiding the component. Note this method is called internally if
14839      * the component is configured to be <code>{@link #hidden}</code>.
14840      * @return {Ext.Component} this
14841      */
14842     hide : function(){
14843         if(this.fireEvent('beforehide', this) !== false){
14844             this.doHide();
14845             this.fireEvent('hide', this);
14846         }
14847         return this;
14848     },
14849
14850     // private
14851     doHide: function(){
14852         this.hidden = true;
14853         if(this.rendered){
14854             this.onHide();
14855         }
14856     },
14857
14858     // private
14859     onHide : function(){
14860         this.getVisibiltyEl().addClass('x-hide-' + this.hideMode);
14861     },
14862
14863     // private
14864     getVisibiltyEl : function(){
14865         return this.hideParent ? this.container : this.getActionEl();
14866     },
14867
14868     /**
14869      * Convenience function to hide or show this component by boolean.
14870      * @param {Boolean} visible True to show, false to hide
14871      * @return {Ext.Component} this
14872      */
14873     setVisible : function(visible){
14874         return this[visible ? 'show' : 'hide']();
14875     },
14876
14877     /**
14878      * Returns true if this component is visible.
14879      * @return {Boolean} True if this component is visible, false otherwise.
14880      */
14881     isVisible : function(){
14882         return this.rendered && this.getVisibiltyEl().isVisible();
14883     },
14884
14885     /**
14886      * Clone the current component using the original config values passed into this instance by default.
14887      * @param {Object} overrides A new config containing any properties to override in the cloned version.
14888      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
14889      * @return {Ext.Component} clone The cloned copy of this component
14890      */
14891     cloneConfig : function(overrides){
14892         overrides = overrides || {};
14893         var id = overrides.id || Ext.id();
14894         var cfg = Ext.applyIf(overrides, this.initialConfig);
14895         cfg.id = id; // prevent dup id
14896         return new this.constructor(cfg);
14897     },
14898
14899     /**
14900      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
14901      * available xtypes, see the {@link Ext.Component} header. Example usage:
14902      * <pre><code>
14903 var t = new Ext.form.TextField();
14904 alert(t.getXType());  // alerts 'textfield'
14905 </code></pre>
14906      * @return {String} The xtype
14907      */
14908     getXType : function(){
14909         return this.constructor.xtype;
14910     },
14911
14912     /**
14913      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
14914      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
14915      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
14916      * to participate in determination of inherited xtypes.</b></p>
14917      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
14918      * <p>Example usage:</p>
14919      * <pre><code>
14920 var t = new Ext.form.TextField();
14921 var isText = t.isXType('textfield');        // true
14922 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
14923 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
14924 </code></pre>
14925      * @param {String} xtype The xtype to check for this Component
14926      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
14927      * the default), or true to check whether this Component is directly of the specified xtype.
14928      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
14929      */
14930     isXType : function(xtype, shallow){
14931         //assume a string by default
14932         if (Ext.isFunction(xtype)){
14933             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
14934         }else if (Ext.isObject(xtype)){
14935             xtype = xtype.constructor.xtype; //handle being passed an instance
14936         }
14937
14938         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
14939     },
14940
14941     /**
14942      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
14943      * available xtypes, see the {@link Ext.Component} header.</p>
14944      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
14945      * to participate in determination of inherited xtypes.</b></p>
14946      * <p>Example usage:</p>
14947      * <pre><code>
14948 var t = new Ext.form.TextField();
14949 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
14950 </code></pre>
14951      * @return {String} The xtype hierarchy string
14952      */
14953     getXTypes : function(){
14954         var tc = this.constructor;
14955         if(!tc.xtypes){
14956             var c = [], sc = this;
14957             while(sc && sc.constructor.xtype){
14958                 c.unshift(sc.constructor.xtype);
14959                 sc = sc.constructor.superclass;
14960             }
14961             tc.xtypeChain = c;
14962             tc.xtypes = c.join('/');
14963         }
14964         return tc.xtypes;
14965     },
14966
14967     /**
14968      * Find a container above this component at any level by a custom function. If the passed function returns
14969      * true, the container will be returned.
14970      * @param {Function} fn The custom function to call with the arguments (container, this component).
14971      * @return {Ext.Container} The first Container for which the custom function returns true
14972      */
14973     findParentBy : function(fn) {
14974         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
14975         return p || null;
14976     },
14977
14978     /**
14979      * Find a container above this component at any level by xtype or class
14980      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
14981      * @return {Ext.Container} The first Container which matches the given xtype or class
14982      */
14983     findParentByType : function(xtype) {
14984         return Ext.isFunction(xtype) ?
14985             this.findParentBy(function(p){
14986                 return p.constructor === xtype;
14987             }) :
14988             this.findParentBy(function(p){
14989                 return p.constructor.xtype === xtype;
14990             });
14991     },
14992
14993     getDomPositionEl : function(){
14994         return this.getPositionEl ? this.getPositionEl() : this.getEl();
14995     },
14996
14997     // private
14998     purgeListeners : function(){
14999         Ext.Component.superclass.purgeListeners.call(this);
15000         if(this.mons){
15001             this.on('beforedestroy', this.clearMons, this, {single: true});
15002         }
15003     },
15004
15005     // private
15006     clearMons : function(){
15007         Ext.each(this.mons, function(m){
15008             m.item.un(m.ename, m.fn, m.scope);
15009         }, this);
15010         this.mons = [];
15011     },
15012
15013     // internal function for auto removal of assigned event handlers on destruction
15014     mon : function(item, ename, fn, scope, opt){
15015         if(!this.mons){
15016             this.mons = [];
15017             this.on('beforedestroy', this.clearMons, this, {single: true});
15018         }
15019
15020         if(Ext.isObject(ename)){
15021                 var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
15022
15023             var o = ename;
15024             for(var e in o){
15025                 if(propRe.test(e)){
15026                     continue;
15027                 }
15028                 if(Ext.isFunction(o[e])){
15029                     // shared options
15030                                 this.mons.push({
15031                                     item: item, ename: e, fn: o[e], scope: o.scope
15032                                 });
15033                                 item.on(e, o[e], o.scope, o);
15034                 }else{
15035                     // individual options
15036                                 this.mons.push({
15037                                     item: item, ename: e, fn: o[e], scope: o.scope
15038                                 });
15039                                 item.on(e, o[e]);
15040                 }
15041             }
15042             return;
15043         }
15044
15045
15046         this.mons.push({
15047             item: item, ename: ename, fn: fn, scope: scope
15048         });
15049         item.on(ename, fn, scope, opt);
15050     },
15051
15052     // protected, opposite of mon
15053     mun : function(item, ename, fn, scope){
15054         var found, mon;
15055         for(var i = 0, len = this.mons.length; i < len; ++i){
15056             mon = this.mons[i];
15057             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
15058                 this.mons.splice(i, 1);
15059                 item.un(ename, fn, scope);
15060                 found = true;
15061                 break;
15062             }
15063         }
15064         return found;
15065     },
15066
15067     /**
15068      * Returns the next component in the owning container
15069      * @return Ext.Component
15070      */
15071     nextSibling : function(){
15072         if(this.ownerCt){
15073             var index = this.ownerCt.items.indexOf(this);
15074             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
15075                 return this.ownerCt.items.itemAt(index+1);
15076             }
15077         }
15078         return null;
15079     },
15080
15081     /**
15082      * Returns the previous component in the owning container
15083      * @return Ext.Component
15084      */
15085     previousSibling : function(){
15086         if(this.ownerCt){
15087             var index = this.ownerCt.items.indexOf(this);
15088             if(index > 0){
15089                 return this.ownerCt.items.itemAt(index-1);
15090             }
15091         }
15092         return null;
15093     },
15094
15095     /**
15096      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
15097      * @return {Ext.Container} the Container which owns this Component.
15098      */
15099     getBubbleTarget : function(){
15100         return this.ownerCt;
15101     }
15102 });
15103
15104 Ext.reg('component', Ext.Component);
15105 /**\r
15106  * @class Ext.Action\r
15107  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it\r
15108  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI\r
15109  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}\r
15110  * and {@link Ext.menu.Menu} components).</p>\r
15111  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support\r
15112  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),\r
15113  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>\r
15114  * Example usage:<br>\r
15115  * <pre><code>\r
15116 // Define the shared action.  Each component below will have the same\r
15117 // display text and icon, and will display the same message on click.\r
15118 var action = new Ext.Action({\r
15119     {@link #text}: 'Do something',\r
15120     {@link #handler}: function(){\r
15121         Ext.Msg.alert('Click', 'You did something.');\r
15122     },\r
15123     {@link #iconCls}: 'do-something',\r
15124     {@link #itemId}: 'myAction'\r
15125 });\r
15126 \r
15127 var panel = new Ext.Panel({\r
15128     title: 'Actions',\r
15129     width: 500,\r
15130     height: 300,\r
15131     tbar: [\r
15132         // Add the action directly to a toolbar as a menu button\r
15133         action,\r
15134         {\r
15135             text: 'Action Menu',\r
15136             // Add the action to a menu as a text item\r
15137             menu: [action]\r
15138         }\r
15139     ],\r
15140     items: [\r
15141         // Add the action to the panel body as a standard button\r
15142         new Ext.Button(action)\r
15143     ],\r
15144     renderTo: Ext.getBody()\r
15145 });\r
15146 \r
15147 // Change the text for all components using the action\r
15148 action.setText('Something else');\r
15149 \r
15150 // Reference an action through a container using the itemId\r
15151 var btn = panel.getComponent('myAction');\r
15152 var aRef = btn.baseAction;\r
15153 aRef.setText('New text');\r
15154 </code></pre>\r
15155  * @constructor\r
15156  * @param {Object} config The configuration options\r
15157  */\r
15158 Ext.Action = function(config){\r
15159     this.initialConfig = config;\r
15160     this.itemId = config.itemId = (config.itemId || config.id || Ext.id());\r
15161     this.items = [];\r
15162 }\r
15163 \r
15164 Ext.Action.prototype = {\r
15165     /**\r
15166      * @cfg {String} text The text to set for all components using this action (defaults to '').\r
15167      */\r
15168     /**\r
15169      * @cfg {String} iconCls\r
15170      * The CSS class selector that specifies a background image to be used as the header icon for\r
15171      * all components using this action (defaults to '').\r
15172      * <p>An example of specifying a custom icon class would be something like:\r
15173      * </p><pre><code>\r
15174 // specify the property in the config for the class:\r
15175      ...\r
15176      iconCls: 'do-something'\r
15177 \r
15178 // css class that specifies background image to be used as the icon image:\r
15179 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }\r
15180 </code></pre>\r
15181      */\r
15182     /**\r
15183      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).\r
15184      */\r
15185     /**\r
15186      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).\r
15187      */\r
15188     /**\r
15189      * @cfg {Function} handler The function that will be invoked by each component tied to this action\r
15190      * when the component's primary event is triggered (defaults to undefined).\r
15191      */\r
15192     /**\r
15193      * @cfg {String} itemId\r
15194      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.\r
15195      */\r
15196     /**\r
15197      * @cfg {Object} scope The scope in which the {@link #handler} function will execute.\r
15198      */\r
15199 \r
15200     // private\r
15201     isAction : true,\r
15202 \r
15203     /**\r
15204      * Sets the text to be displayed by all components using this action.\r
15205      * @param {String} text The text to display\r
15206      */\r
15207     setText : function(text){\r
15208         this.initialConfig.text = text;\r
15209         this.callEach('setText', [text]);\r
15210     },\r
15211 \r
15212     /**\r
15213      * Gets the text currently displayed by all components using this action.\r
15214      */\r
15215     getText : function(){\r
15216         return this.initialConfig.text;\r
15217     },\r
15218 \r
15219     /**\r
15220      * Sets the icon CSS class for all components using this action.  The class should supply\r
15221      * a background image that will be used as the icon image.\r
15222      * @param {String} cls The CSS class supplying the icon image\r
15223      */\r
15224     setIconClass : function(cls){\r
15225         this.initialConfig.iconCls = cls;\r
15226         this.callEach('setIconClass', [cls]);\r
15227     },\r
15228 \r
15229     /**\r
15230      * Gets the icon CSS class currently used by all components using this action.\r
15231      */\r
15232     getIconClass : function(){\r
15233         return this.initialConfig.iconCls;\r
15234     },\r
15235 \r
15236     /**\r
15237      * Sets the disabled state of all components using this action.  Shortcut method\r
15238      * for {@link #enable} and {@link #disable}.\r
15239      * @param {Boolean} disabled True to disable the component, false to enable it\r
15240      */\r
15241     setDisabled : function(v){\r
15242         this.initialConfig.disabled = v;\r
15243         this.callEach('setDisabled', [v]);\r
15244     },\r
15245 \r
15246     /**\r
15247      * Enables all components using this action.\r
15248      */\r
15249     enable : function(){\r
15250         this.setDisabled(false);\r
15251     },\r
15252 \r
15253     /**\r
15254      * Disables all components using this action.\r
15255      */\r
15256     disable : function(){\r
15257         this.setDisabled(true);\r
15258     },\r
15259 \r
15260     /**\r
15261      * Returns true if the components using this action are currently disabled, else returns false.  \r
15262      */\r
15263     isDisabled : function(){\r
15264         return this.initialConfig.disabled;\r
15265     },\r
15266 \r
15267     /**\r
15268      * Sets the hidden state of all components using this action.  Shortcut method\r
15269      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.\r
15270      * @param {Boolean} hidden True to hide the component, false to show it\r
15271      */\r
15272     setHidden : function(v){\r
15273         this.initialConfig.hidden = v;\r
15274         this.callEach('setVisible', [!v]);\r
15275     },\r
15276 \r
15277     /**\r
15278      * Shows all components using this action.\r
15279      */\r
15280     show : function(){\r
15281         this.setHidden(false);\r
15282     },\r
15283 \r
15284     /**\r
15285      * Hides all components using this action.\r
15286      */\r
15287     hide : function(){\r
15288         this.setHidden(true);\r
15289     },\r
15290 \r
15291     /**\r
15292      * Returns true if the components using this action are currently hidden, else returns false.  \r
15293      */\r
15294     isHidden : function(){\r
15295         return this.initialConfig.hidden;\r
15296     },\r
15297 \r
15298     /**\r
15299      * Sets the function that will be called by each component using this action when its primary event is triggered.\r
15300      * @param {Function} fn The function that will be invoked by the action's components.  The function\r
15301      * will be called with no arguments.\r
15302      * @param {Object} scope The scope in which the function will execute\r
15303      */\r
15304     setHandler : function(fn, scope){\r
15305         this.initialConfig.handler = fn;\r
15306         this.initialConfig.scope = scope;\r
15307         this.callEach('setHandler', [fn, scope]);\r
15308     },\r
15309 \r
15310     /**\r
15311      * Executes the specified function once for each component currently tied to this action.  The function passed\r
15312      * in should accept a single argument that will be an object that supports the basic Action config/method interface.\r
15313      * @param {Function} fn The function to execute for each component\r
15314      * @param {Object} scope The scope in which the function will execute\r
15315      */\r
15316     each : function(fn, scope){\r
15317         Ext.each(this.items, fn, scope);\r
15318     },\r
15319 \r
15320     // private\r
15321     callEach : function(fnName, args){\r
15322         var cs = this.items;\r
15323         for(var i = 0, len = cs.length; i < len; i++){\r
15324             cs[i][fnName].apply(cs[i], args);\r
15325         }\r
15326     },\r
15327 \r
15328     // private\r
15329     addComponent : function(comp){\r
15330         this.items.push(comp);\r
15331         comp.on('destroy', this.removeComponent, this);\r
15332     },\r
15333 \r
15334     // private\r
15335     removeComponent : function(comp){\r
15336         this.items.remove(comp);\r
15337     },\r
15338 \r
15339     /**\r
15340      * Executes this action manually using the handler function specified in the original config object\r
15341      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this\r
15342      * function will be passed on to the handler function.\r
15343      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function\r
15344      * @param {Mixed} arg2 (optional)\r
15345      * @param {Mixed} etc... (optional)\r
15346      */\r
15347     execute : function(){\r
15348         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);\r
15349     }\r
15350 };
15351 /**
15352  * @class Ext.Layer
15353  * @extends Ext.Element
15354  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
15355  * automatic maintaining of shadow/shim positions.
15356  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
15357  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
15358  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
15359  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
15360  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
15361  * @cfg {String} cls CSS class to add to the element
15362  * @cfg {Number} zindex Starting z-index (defaults to 11000)
15363  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
15364  * @cfg {Boolean} useDisplay
15365  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
15366  * to use css style <tt>'display:none;'</tt> to hide the Layer.
15367  * @constructor
15368  * @param {Object} config An object with config options.
15369  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
15370  */
15371 (function(){
15372 Ext.Layer = function(config, existingEl){
15373     config = config || {};
15374     var dh = Ext.DomHelper;
15375     var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
15376     if(existingEl){
15377         this.dom = Ext.getDom(existingEl);
15378     }
15379     if(!this.dom){
15380         var o = config.dh || {tag: 'div', cls: 'x-layer'};
15381         this.dom = dh.append(pel, o);
15382     }
15383     if(config.cls){
15384         this.addClass(config.cls);
15385     }
15386     this.constrain = config.constrain !== false;
15387     this.setVisibilityMode(Ext.Element.VISIBILITY);
15388     if(config.id){
15389         this.id = this.dom.id = config.id;
15390     }else{
15391         this.id = Ext.id(this.dom);
15392     }
15393     this.zindex = config.zindex || this.getZIndex();
15394     this.position('absolute', this.zindex);
15395     if(config.shadow){
15396         this.shadowOffset = config.shadowOffset || 4;
15397         this.shadow = new Ext.Shadow({
15398             offset : this.shadowOffset,
15399             mode : config.shadow
15400         });
15401     }else{
15402         this.shadowOffset = 0;
15403     }
15404     this.useShim = config.shim !== false && Ext.useShims;
15405     this.useDisplay = config.useDisplay;
15406     this.hide();
15407 };
15408
15409 var supr = Ext.Element.prototype;
15410
15411 // shims are shared among layer to keep from having 100 iframes
15412 var shims = [];
15413
15414 Ext.extend(Ext.Layer, Ext.Element, {
15415
15416     getZIndex : function(){
15417         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
15418     },
15419
15420     getShim : function(){
15421         if(!this.useShim){
15422             return null;
15423         }
15424         if(this.shim){
15425             return this.shim;
15426         }
15427         var shim = shims.shift();
15428         if(!shim){
15429             shim = this.createShim();
15430             shim.enableDisplayMode('block');
15431             shim.dom.style.display = 'none';
15432             shim.dom.style.visibility = 'visible';
15433         }
15434         var pn = this.dom.parentNode;
15435         if(shim.dom.parentNode != pn){
15436             pn.insertBefore(shim.dom, this.dom);
15437         }
15438         shim.setStyle('z-index', this.getZIndex()-2);
15439         this.shim = shim;
15440         return shim;
15441     },
15442
15443     hideShim : function(){
15444         if(this.shim){
15445             this.shim.setDisplayed(false);
15446             shims.push(this.shim);
15447             delete this.shim;
15448         }
15449     },
15450
15451     disableShadow : function(){
15452         if(this.shadow){
15453             this.shadowDisabled = true;
15454             this.shadow.hide();
15455             this.lastShadowOffset = this.shadowOffset;
15456             this.shadowOffset = 0;
15457         }
15458     },
15459
15460     enableShadow : function(show){
15461         if(this.shadow){
15462             this.shadowDisabled = false;
15463             this.shadowOffset = this.lastShadowOffset;
15464             delete this.lastShadowOffset;
15465             if(show){
15466                 this.sync(true);
15467             }
15468         }
15469     },
15470
15471     // private
15472     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
15473     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
15474     sync : function(doShow){
15475         var sw = this.shadow;
15476         if(!this.updating && this.isVisible() && (sw || this.useShim)){
15477             var sh = this.getShim();
15478
15479             var w = this.getWidth(),
15480                 h = this.getHeight();
15481
15482             var l = this.getLeft(true),
15483                 t = this.getTop(true);
15484
15485             if(sw && !this.shadowDisabled){
15486                 if(doShow && !sw.isVisible()){
15487                     sw.show(this);
15488                 }else{
15489                     sw.realign(l, t, w, h);
15490                 }
15491                 if(sh){
15492                     if(doShow){
15493                        sh.show();
15494                     }
15495                     // fit the shim behind the shadow, so it is shimmed too
15496                     var a = sw.adjusts, s = sh.dom.style;
15497                     s.left = (Math.min(l, l+a.l))+'px';
15498                     s.top = (Math.min(t, t+a.t))+'px';
15499                     s.width = (w+a.w)+'px';
15500                     s.height = (h+a.h)+'px';
15501                 }
15502             }else if(sh){
15503                 if(doShow){
15504                    sh.show();
15505                 }
15506                 sh.setSize(w, h);
15507                 sh.setLeftTop(l, t);
15508             }
15509
15510         }
15511     },
15512
15513     // private
15514     destroy : function(){
15515         this.hideShim();
15516         if(this.shadow){
15517             this.shadow.hide();
15518         }
15519         this.removeAllListeners();
15520         Ext.removeNode(this.dom);
15521         Ext.Element.uncache(this.id);
15522     },
15523
15524     remove : function(){
15525         this.destroy();
15526     },
15527
15528     // private
15529     beginUpdate : function(){
15530         this.updating = true;
15531     },
15532
15533     // private
15534     endUpdate : function(){
15535         this.updating = false;
15536         this.sync(true);
15537     },
15538
15539     // private
15540     hideUnders : function(negOffset){
15541         if(this.shadow){
15542             this.shadow.hide();
15543         }
15544         this.hideShim();
15545     },
15546
15547     // private
15548     constrainXY : function(){
15549         if(this.constrain){
15550             var vw = Ext.lib.Dom.getViewWidth(),
15551                 vh = Ext.lib.Dom.getViewHeight();
15552             var s = Ext.getDoc().getScroll();
15553
15554             var xy = this.getXY();
15555             var x = xy[0], y = xy[1];
15556             var so = this.shadowOffset;
15557             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
15558             // only move it if it needs it
15559             var moved = false;
15560             // first validate right/bottom
15561             if((x + w) > vw+s.left){
15562                 x = vw - w - so;
15563                 moved = true;
15564             }
15565             if((y + h) > vh+s.top){
15566                 y = vh - h - so;
15567                 moved = true;
15568             }
15569             // then make sure top/left isn't negative
15570             if(x < s.left){
15571                 x = s.left;
15572                 moved = true;
15573             }
15574             if(y < s.top){
15575                 y = s.top;
15576                 moved = true;
15577             }
15578             if(moved){
15579                 if(this.avoidY){
15580                     var ay = this.avoidY;
15581                     if(y <= ay && (y+h) >= ay){
15582                         y = ay-h-5;
15583                     }
15584                 }
15585                 xy = [x, y];
15586                 this.storeXY(xy);
15587                 supr.setXY.call(this, xy);
15588                 this.sync();
15589             }
15590         }
15591         return this;
15592     },
15593
15594     isVisible : function(){
15595         return this.visible;
15596     },
15597
15598     // private
15599     showAction : function(){
15600         this.visible = true; // track visibility to prevent getStyle calls
15601         if(this.useDisplay === true){
15602             this.setDisplayed('');
15603         }else if(this.lastXY){
15604             supr.setXY.call(this, this.lastXY);
15605         }else if(this.lastLT){
15606             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
15607         }
15608     },
15609
15610     // private
15611     hideAction : function(){
15612         this.visible = false;
15613         if(this.useDisplay === true){
15614             this.setDisplayed(false);
15615         }else{
15616             this.setLeftTop(-10000,-10000);
15617         }
15618     },
15619
15620     // overridden Element method
15621     setVisible : function(v, a, d, c, e){
15622         if(v){
15623             this.showAction();
15624         }
15625         if(a && v){
15626             var cb = function(){
15627                 this.sync(true);
15628                 if(c){
15629                     c();
15630                 }
15631             }.createDelegate(this);
15632             supr.setVisible.call(this, true, true, d, cb, e);
15633         }else{
15634             if(!v){
15635                 this.hideUnders(true);
15636             }
15637             var cb = c;
15638             if(a){
15639                 cb = function(){
15640                     this.hideAction();
15641                     if(c){
15642                         c();
15643                     }
15644                 }.createDelegate(this);
15645             }
15646             supr.setVisible.call(this, v, a, d, cb, e);
15647             if(v){
15648                 this.sync(true);
15649             }else if(!a){
15650                 this.hideAction();
15651             }
15652         }
15653         return this;
15654     },
15655
15656     storeXY : function(xy){
15657         delete this.lastLT;
15658         this.lastXY = xy;
15659     },
15660
15661     storeLeftTop : function(left, top){
15662         delete this.lastXY;
15663         this.lastLT = [left, top];
15664     },
15665
15666     // private
15667     beforeFx : function(){
15668         this.beforeAction();
15669         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
15670     },
15671
15672     // private
15673     afterFx : function(){
15674         Ext.Layer.superclass.afterFx.apply(this, arguments);
15675         this.sync(this.isVisible());
15676     },
15677
15678     // private
15679     beforeAction : function(){
15680         if(!this.updating && this.shadow){
15681             this.shadow.hide();
15682         }
15683     },
15684
15685     // overridden Element method
15686     setLeft : function(left){
15687         this.storeLeftTop(left, this.getTop(true));
15688         supr.setLeft.apply(this, arguments);
15689         this.sync();
15690         return this;
15691     },
15692
15693     setTop : function(top){
15694         this.storeLeftTop(this.getLeft(true), top);
15695         supr.setTop.apply(this, arguments);
15696         this.sync();
15697         return this;
15698     },
15699
15700     setLeftTop : function(left, top){
15701         this.storeLeftTop(left, top);
15702         supr.setLeftTop.apply(this, arguments);
15703         this.sync();
15704         return this;
15705     },
15706
15707     setXY : function(xy, a, d, c, e){
15708         this.fixDisplay();
15709         this.beforeAction();
15710         this.storeXY(xy);
15711         var cb = this.createCB(c);
15712         supr.setXY.call(this, xy, a, d, cb, e);
15713         if(!a){
15714             cb();
15715         }
15716         return this;
15717     },
15718
15719     // private
15720     createCB : function(c){
15721         var el = this;
15722         return function(){
15723             el.constrainXY();
15724             el.sync(true);
15725             if(c){
15726                 c();
15727             }
15728         };
15729     },
15730
15731     // overridden Element method
15732     setX : function(x, a, d, c, e){
15733         this.setXY([x, this.getY()], a, d, c, e);
15734         return this;
15735     },
15736
15737     // overridden Element method
15738     setY : function(y, a, d, c, e){
15739         this.setXY([this.getX(), y], a, d, c, e);
15740         return this;
15741     },
15742
15743     // overridden Element method
15744     setSize : function(w, h, a, d, c, e){
15745         this.beforeAction();
15746         var cb = this.createCB(c);
15747         supr.setSize.call(this, w, h, a, d, cb, e);
15748         if(!a){
15749             cb();
15750         }
15751         return this;
15752     },
15753
15754     // overridden Element method
15755     setWidth : function(w, a, d, c, e){
15756         this.beforeAction();
15757         var cb = this.createCB(c);
15758         supr.setWidth.call(this, w, a, d, cb, e);
15759         if(!a){
15760             cb();
15761         }
15762         return this;
15763     },
15764
15765     // overridden Element method
15766     setHeight : function(h, a, d, c, e){
15767         this.beforeAction();
15768         var cb = this.createCB(c);
15769         supr.setHeight.call(this, h, a, d, cb, e);
15770         if(!a){
15771             cb();
15772         }
15773         return this;
15774     },
15775
15776     // overridden Element method
15777     setBounds : function(x, y, w, h, a, d, c, e){
15778         this.beforeAction();
15779         var cb = this.createCB(c);
15780         if(!a){
15781             this.storeXY([x, y]);
15782             supr.setXY.call(this, [x, y]);
15783             supr.setSize.call(this, w, h, a, d, cb, e);
15784             cb();
15785         }else{
15786             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
15787         }
15788         return this;
15789     },
15790
15791     /**
15792      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
15793      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
15794      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
15795      * @param {Number} zindex The new z-index to set
15796      * @return {this} The Layer
15797      */
15798     setZIndex : function(zindex){
15799         this.zindex = zindex;
15800         this.setStyle('z-index', zindex + 2);
15801         if(this.shadow){
15802             this.shadow.setZIndex(zindex + 1);
15803         }
15804         if(this.shim){
15805             this.shim.setStyle('z-index', zindex);
15806         }
15807         return this;
15808     }
15809 });
15810 })();/**
15811  * @class Ext.Shadow
15812  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
15813  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
15814  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
15815  * @constructor
15816  * Create a new Shadow
15817  * @param {Object} config The config object
15818  */
15819 Ext.Shadow = function(config){
15820     Ext.apply(this, config);
15821     if(typeof this.mode != "string"){
15822         this.mode = this.defaultMode;
15823     }
15824     var o = this.offset, a = {h: 0};
15825     var rad = Math.floor(this.offset/2);
15826     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
15827         case "drop":
15828             a.w = 0;
15829             a.l = a.t = o;
15830             a.t -= 1;
15831             if(Ext.isIE){
15832                 a.l -= this.offset + rad;
15833                 a.t -= this.offset + rad;
15834                 a.w -= rad;
15835                 a.h -= rad;
15836                 a.t += 1;
15837             }
15838         break;
15839         case "sides":
15840             a.w = (o*2);
15841             a.l = -o;
15842             a.t = o-1;
15843             if(Ext.isIE){
15844                 a.l -= (this.offset - rad);
15845                 a.t -= this.offset + rad;
15846                 a.l += 1;
15847                 a.w -= (this.offset - rad)*2;
15848                 a.w -= rad + 1;
15849                 a.h -= 1;
15850             }
15851         break;
15852         case "frame":
15853             a.w = a.h = (o*2);
15854             a.l = a.t = -o;
15855             a.t += 1;
15856             a.h -= 2;
15857             if(Ext.isIE){
15858                 a.l -= (this.offset - rad);
15859                 a.t -= (this.offset - rad);
15860                 a.l += 1;
15861                 a.w -= (this.offset + rad + 1);
15862                 a.h -= (this.offset + rad);
15863                 a.h += 1;
15864             }
15865         break;
15866     };
15867
15868     this.adjusts = a;
15869 };
15870
15871 Ext.Shadow.prototype = {
15872     /**
15873      * @cfg {String} mode
15874      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
15875      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
15876      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
15877      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
15878      * </ul></div>
15879      */
15880     /**
15881      * @cfg {String} offset
15882      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
15883      */
15884     offset: 4,
15885
15886     // private
15887     defaultMode: "drop",
15888
15889     /**
15890      * Displays the shadow under the target element
15891      * @param {Mixed} targetEl The id or element under which the shadow should display
15892      */
15893     show : function(target){
15894         target = Ext.get(target);
15895         if(!this.el){
15896             this.el = Ext.Shadow.Pool.pull();
15897             if(this.el.dom.nextSibling != target.dom){
15898                 this.el.insertBefore(target);
15899             }
15900         }
15901         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
15902         if(Ext.isIE){
15903             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
15904         }
15905         this.realign(
15906             target.getLeft(true),
15907             target.getTop(true),
15908             target.getWidth(),
15909             target.getHeight()
15910         );
15911         this.el.dom.style.display = "block";
15912     },
15913
15914     /**
15915      * Returns true if the shadow is visible, else false
15916      */
15917     isVisible : function(){
15918         return this.el ? true : false;  
15919     },
15920
15921     /**
15922      * Direct alignment when values are already available. Show must be called at least once before
15923      * calling this method to ensure it is initialized.
15924      * @param {Number} left The target element left position
15925      * @param {Number} top The target element top position
15926      * @param {Number} width The target element width
15927      * @param {Number} height The target element height
15928      */
15929     realign : function(l, t, w, h){
15930         if(!this.el){
15931             return;
15932         }
15933         var a = this.adjusts, d = this.el.dom, s = d.style;
15934         var iea = 0;
15935         s.left = (l+a.l)+"px";
15936         s.top = (t+a.t)+"px";
15937         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
15938         if(s.width != sws || s.height != shs){
15939             s.width = sws;
15940             s.height = shs;
15941             if(!Ext.isIE){
15942                 var cn = d.childNodes;
15943                 var sww = Math.max(0, (sw-12))+"px";
15944                 cn[0].childNodes[1].style.width = sww;
15945                 cn[1].childNodes[1].style.width = sww;
15946                 cn[2].childNodes[1].style.width = sww;
15947                 cn[1].style.height = Math.max(0, (sh-12))+"px";
15948             }
15949         }
15950     },
15951
15952     /**
15953      * Hides this shadow
15954      */
15955     hide : function(){
15956         if(this.el){
15957             this.el.dom.style.display = "none";
15958             Ext.Shadow.Pool.push(this.el);
15959             delete this.el;
15960         }
15961     },
15962
15963     /**
15964      * Adjust the z-index of this shadow
15965      * @param {Number} zindex The new z-index
15966      */
15967     setZIndex : function(z){
15968         this.zIndex = z;
15969         if(this.el){
15970             this.el.setStyle("z-index", z);
15971         }
15972     }
15973 };
15974
15975 // Private utility class that manages the internal Shadow cache
15976 Ext.Shadow.Pool = function(){
15977     var p = [];
15978     var markup = Ext.isIE ?
15979                  '<div class="x-ie-shadow"></div>' :
15980                  '<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>';
15981     return {
15982         pull : function(){
15983             var sh = p.shift();
15984             if(!sh){
15985                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
15986                 sh.autoBoxAdjust = false;
15987             }
15988             return sh;
15989         },
15990
15991         push : function(sh){
15992             p.push(sh);
15993         }
15994     };
15995 }();/**
15996  * @class Ext.BoxComponent
15997  * @extends Ext.Component
15998  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
15999  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
16000  * within the Component rendering model.</p>
16001  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
16002  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
16003  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
16004  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
16005  * element to reference:<pre><code>
16006 var pageHeader = new Ext.BoxComponent({
16007     el: 'my-header-div'
16008 });</code></pre>
16009  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
16010  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
16011  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
16012  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
16013 var myImage = new Ext.BoxComponent({
16014     autoEl: {
16015         tag: 'img',
16016         src: '/images/my-image.jpg'
16017     }
16018 });</code></pre></p>
16019  * @constructor
16020  * @param {Ext.Element/String/Object} config The configuration options.
16021  * @xtype box
16022  */
16023 Ext.BoxComponent = Ext.extend(Ext.Component, {
16024
16025     // Configs below are used for all Components when rendered by BorderLayout.
16026     /**
16027      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
16028      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
16029      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
16030      * <p>See {@link Ext.layout.BorderLayout} also.</p>
16031      */
16032     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
16033     /**
16034      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
16035      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
16036      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
16037      * <p>An object containing margins to apply to this BoxComponent in the
16038      * format:</p><pre><code>
16039 {
16040     top: (top margin),
16041     right: (right margin),
16042     bottom: (bottom margin),
16043     left: (left margin)
16044 }</code></pre>
16045      * <p>May also be a string containing space-separated, numeric margin values. The order of the
16046      * sides associated with each value matches the way CSS processes margin values:</p>
16047      * <p><div class="mdetail-params"><ul>
16048      * <li>If there is only one value, it applies to all sides.</li>
16049      * <li>If there are two values, the top and bottom borders are set to the first value and the
16050      * right and left are set to the second.</li>
16051      * <li>If there are three values, the top is set to the first value, the left and right are set
16052      * to the second, and the bottom is set to the third.</li>
16053      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
16054      * </ul></div></p>
16055      * <p>Defaults to:</p><pre><code>
16056      * {top:0, right:0, bottom:0, left:0}
16057      * </code></pre>
16058      */
16059     /**
16060      * @cfg {Number} x
16061      * The local x (left) coordinate for this component if contained within a positioning container.
16062      */
16063     /**
16064      * @cfg {Number} y
16065      * The local y (top) coordinate for this component if contained within a positioning container.
16066      */
16067     /**
16068      * @cfg {Number} pageX
16069      * The page level x coordinate for this component if contained within a positioning container.
16070      */
16071     /**
16072      * @cfg {Number} pageY
16073      * The page level y coordinate for this component if contained within a positioning container.
16074      */
16075     /**
16076      * @cfg {Number} height
16077      * The height of this component in pixels (defaults to auto).
16078      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
16079      */
16080     /**
16081      * @cfg {Number} width
16082      * The width of this component in pixels (defaults to auto).
16083      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
16084      */
16085     /**
16086      * @cfg {Boolean} autoHeight
16087      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
16088      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
16089      * <p><b>Note</b>: Although many components inherit this config option, not all will
16090      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
16091      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
16092      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
16093      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
16094      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
16095 var w = new Ext.Window({
16096     title: 'Window',
16097     width: 600,
16098     autoHeight: true,
16099     items: {
16100         title: 'Collapse Me',
16101         height: 400,
16102         collapsible: true,
16103         border: false,
16104         listeners: {
16105             beforecollapse: function() {
16106                 w.el.shadow.hide();
16107             },
16108             beforeexpand: function() {
16109                 w.el.shadow.hide();
16110             },
16111             collapse: function() {
16112                 w.syncShadow();
16113             },
16114             expand: function() {
16115                 w.syncShadow();
16116             }
16117         }
16118     }
16119 }).show();
16120 </code></pre>
16121      */
16122     /**
16123      * @cfg {Boolean} autoWidth
16124      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
16125      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
16126      * <p><b>Note</b>: Although many components  inherit this config option, not all will
16127      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
16128      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
16129      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
16130      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
16131      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
16132 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
16133 </code></pre>
16134      * A Panel rendered into that target element must listen for browser window resize in order to relay its
16135       * child items when the browser changes its width:<pre><code>
16136 var myPanel = new Ext.Panel({
16137     renderTo: 'grid-container',
16138     monitorResize: true, // relay on browser resize
16139     title: 'Panel',
16140     height: 400,
16141     autoWidth: true,
16142     layout: 'hbox',
16143     layoutConfig: {
16144         align: 'stretch'
16145     },
16146     defaults: {
16147         flex: 1
16148     },
16149     items: [{
16150         title: 'Box 1',
16151     }, {
16152         title: 'Box 2'
16153     }, {
16154         title: 'Box 3'
16155     }],
16156 });
16157 </code></pre>
16158      */
16159
16160     /* // private internal config
16161      * {Boolean} deferHeight
16162      * True to defer height calculations to an external component, false to allow this component to set its own
16163      * height (defaults to false).
16164      */
16165
16166     // private
16167     initComponent : function(){
16168         Ext.BoxComponent.superclass.initComponent.call(this);
16169         this.addEvents(
16170             /**
16171              * @event resize
16172              * Fires after the component is resized.
16173              * @param {Ext.Component} this
16174              * @param {Number} adjWidth The box-adjusted width that was set
16175              * @param {Number} adjHeight The box-adjusted height that was set
16176              * @param {Number} rawWidth The width that was originally specified
16177              * @param {Number} rawHeight The height that was originally specified
16178              */
16179             'resize',
16180             /**
16181              * @event move
16182              * Fires after the component is moved.
16183              * @param {Ext.Component} this
16184              * @param {Number} x The new x position
16185              * @param {Number} y The new y position
16186              */
16187             'move'
16188         );
16189     },
16190
16191     // private, set in afterRender to signify that the component has been rendered
16192     boxReady : false,
16193     // private, used to defer height settings to subclasses
16194     deferHeight: false,
16195
16196     /**
16197      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
16198      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
16199      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
16200      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16201      * <li>A String used to set the CSS width style.</li>
16202      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
16203      * <li><code>undefined</code> to leave the width unchanged.</li>
16204      * </ul></div>
16205      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
16206      * This may be one of:<div class="mdetail-params"><ul>
16207      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16208      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
16209      * <li><code>undefined</code> to leave the height unchanged.</li>
16210      * </ul></div>
16211      * @return {Ext.BoxComponent} this
16212      */
16213     setSize : function(w, h){
16214         // support for standard size objects
16215         if(typeof w == 'object'){
16216             h = w.height;
16217             w = w.width;
16218         }
16219         // not rendered
16220         if(!this.boxReady){
16221             this.width = w;
16222             this.height = h;
16223             return this;
16224         }
16225
16226         // prevent recalcs when not needed
16227         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16228             return this;
16229         }
16230         this.lastSize = {width: w, height: h};
16231         var adj = this.adjustSize(w, h);
16232         var aw = adj.width, ah = adj.height;
16233         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16234             var rz = this.getResizeEl();
16235             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16236                 rz.setSize(aw, ah);
16237             }else if(!this.deferHeight && ah !== undefined){
16238                 rz.setHeight(ah);
16239             }else if(aw !== undefined){
16240                 rz.setWidth(aw);
16241             }
16242             this.onResize(aw, ah, w, h);
16243             this.fireEvent('resize', this, aw, ah, w, h);
16244         }
16245         return this;
16246     },
16247
16248     /**
16249      * Sets the width of the component.  This method fires the {@link #resize} event.
16250      * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
16251      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16252      * <li>A String used to set the CSS width style.</li>
16253      * </ul></div>
16254      * @return {Ext.BoxComponent} this
16255      */
16256     setWidth : function(width){
16257         return this.setSize(width);
16258     },
16259
16260     /**
16261      * Sets the height of the component.  This method fires the {@link #resize} event.
16262      * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
16263      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16264      * <li>A String used to set the CSS height style.</li>
16265      * <li><i>undefined</i> to leave the height unchanged.</li>
16266      * </ul></div>
16267      * @return {Ext.BoxComponent} this
16268      */
16269     setHeight : function(height){
16270         return this.setSize(undefined, height);
16271     },
16272
16273     /**
16274      * Gets the current size of the component's underlying element.
16275      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16276      */
16277     getSize : function(){
16278         return this.getResizeEl().getSize();
16279     },
16280
16281     /**
16282      * Gets the current width of the component's underlying element.
16283      * @return {Number}
16284      */
16285     getWidth : function(){
16286         return this.getResizeEl().getWidth();
16287     },
16288
16289     /**
16290      * Gets the current height of the component's underlying element.
16291      * @return {Number}
16292      */
16293     getHeight : function(){
16294         return this.getResizeEl().getHeight();
16295     },
16296
16297     /**
16298      * Gets the current size of the component's underlying element, including space taken by its margins.
16299      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
16300      */
16301     getOuterSize : function(){
16302         var el = this.getResizeEl();
16303         return {width: el.getWidth() + el.getMargins('lr'),
16304                 height: el.getHeight() + el.getMargins('tb')};
16305     },
16306
16307     /**
16308      * Gets the current XY position of the component's underlying element.
16309      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16310      * @return {Array} The XY position of the element (e.g., [100, 200])
16311      */
16312     getPosition : function(local){
16313         var el = this.getPositionEl();
16314         if(local === true){
16315             return [el.getLeft(true), el.getTop(true)];
16316         }
16317         return this.xy || el.getXY();
16318     },
16319
16320     /**
16321      * Gets the current box measurements of the component's underlying element.
16322      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16323      * @return {Object} box An object in the format {x, y, width, height}
16324      */
16325     getBox : function(local){
16326         var pos = this.getPosition(local);
16327         var s = this.getSize();
16328         s.x = pos[0];
16329         s.y = pos[1];
16330         return s;
16331     },
16332
16333     /**
16334      * Sets the current box measurements of the component's underlying element.
16335      * @param {Object} box An object in the format {x, y, width, height}
16336      * @return {Ext.BoxComponent} this
16337      */
16338     updateBox : function(box){
16339         this.setSize(box.width, box.height);
16340         this.setPagePosition(box.x, box.y);
16341         return this;
16342     },
16343
16344     /**
16345      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
16346      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
16347      * but in some cases, a Component may have some more wrapping Elements around its main
16348      * active Element.</p>
16349      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
16350      * contains both the <code>&lt;input></code> Element (which is what would be returned
16351      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
16352      * This Element is returned as the <code>resizeEl</code>.
16353      */
16354     getResizeEl : function(){
16355         return this.resizeEl || this.el;
16356     },
16357
16358     // protected
16359     getPositionEl : function(){
16360         return this.positionEl || this.el;
16361     },
16362
16363     /**
16364      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16365      * This method fires the {@link #move} event.
16366      * @param {Number} left The new left
16367      * @param {Number} top The new top
16368      * @return {Ext.BoxComponent} this
16369      */
16370     setPosition : function(x, y){
16371         if(x && typeof x[1] == 'number'){
16372             y = x[1];
16373             x = x[0];
16374         }
16375         this.x = x;
16376         this.y = y;
16377         if(!this.boxReady){
16378             return this;
16379         }
16380         var adj = this.adjustPosition(x, y);
16381         var ax = adj.x, ay = adj.y;
16382
16383         var el = this.getPositionEl();
16384         if(ax !== undefined || ay !== undefined){
16385             if(ax !== undefined && ay !== undefined){
16386                 el.setLeftTop(ax, ay);
16387             }else if(ax !== undefined){
16388                 el.setLeft(ax);
16389             }else if(ay !== undefined){
16390                 el.setTop(ay);
16391             }
16392             this.onPosition(ax, ay);
16393             this.fireEvent('move', this, ax, ay);
16394         }
16395         return this;
16396     },
16397
16398     /**
16399      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16400      * This method fires the {@link #move} event.
16401      * @param {Number} x The new x position
16402      * @param {Number} y The new y position
16403      * @return {Ext.BoxComponent} this
16404      */
16405     setPagePosition : function(x, y){
16406         if(x && typeof x[1] == 'number'){
16407             y = x[1];
16408             x = x[0];
16409         }
16410         this.pageX = x;
16411         this.pageY = y;
16412         if(!this.boxReady){
16413             return;
16414         }
16415         if(x === undefined || y === undefined){ // cannot translate undefined points
16416             return;
16417         }
16418         var p = this.getPositionEl().translatePoints(x, y);
16419         this.setPosition(p.left, p.top);
16420         return this;
16421     },
16422
16423     // private
16424     onRender : function(ct, position){
16425         Ext.BoxComponent.superclass.onRender.call(this, ct, position);
16426         if(this.resizeEl){
16427             this.resizeEl = Ext.get(this.resizeEl);
16428         }
16429         if(this.positionEl){
16430             this.positionEl = Ext.get(this.positionEl);
16431         }
16432     },
16433
16434     // private
16435     afterRender : function(){
16436         Ext.BoxComponent.superclass.afterRender.call(this);
16437         this.boxReady = true;
16438         this.setSize(this.width, this.height);
16439         if(this.x || this.y){
16440             this.setPosition(this.x, this.y);
16441         }else if(this.pageX || this.pageY){
16442             this.setPagePosition(this.pageX, this.pageY);
16443         }
16444     },
16445
16446     /**
16447      * Force the component's size to recalculate based on the underlying element's current height and width.
16448      * @return {Ext.BoxComponent} this
16449      */
16450     syncSize : function(){
16451         delete this.lastSize;
16452         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
16453         return this;
16454     },
16455
16456     /* // protected
16457      * Called after the component is resized, this method is empty by default but can be implemented by any
16458      * subclass that needs to perform custom logic after a resize occurs.
16459      * @param {Number} adjWidth The box-adjusted width that was set
16460      * @param {Number} adjHeight The box-adjusted height that was set
16461      * @param {Number} rawWidth The width that was originally specified
16462      * @param {Number} rawHeight The height that was originally specified
16463      */
16464     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16465
16466     },
16467
16468     /* // protected
16469      * Called after the component is moved, this method is empty by default but can be implemented by any
16470      * subclass that needs to perform custom logic after a move occurs.
16471      * @param {Number} x The new x position
16472      * @param {Number} y The new y position
16473      */
16474     onPosition : function(x, y){
16475
16476     },
16477
16478     // private
16479     adjustSize : function(w, h){
16480         if(this.autoWidth){
16481             w = 'auto';
16482         }
16483         if(this.autoHeight){
16484             h = 'auto';
16485         }
16486         return {width : w, height: h};
16487     },
16488
16489     // private
16490     adjustPosition : function(x, y){
16491         return {x : x, y: y};
16492     }
16493 });
16494 Ext.reg('box', Ext.BoxComponent);
16495
16496
16497 /**
16498  * @class Ext.Spacer
16499  * @extends Ext.BoxComponent
16500  * <p>Used to provide a sizable space in a layout.</p>
16501  * @constructor
16502  * @param {Object} config
16503  */
16504 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
16505     autoEl:'div'
16506 });
16507 Ext.reg('spacer', Ext.Spacer);/**\r
16508  * @class Ext.SplitBar\r
16509  * @extends Ext.util.Observable\r
16510  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).\r
16511  * <br><br>\r
16512  * Usage:\r
16513  * <pre><code>\r
16514 var split = new Ext.SplitBar("elementToDrag", "elementToSize",\r
16515                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);\r
16516 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));\r
16517 split.minSize = 100;\r
16518 split.maxSize = 600;\r
16519 split.animate = true;\r
16520 split.on('moved', splitterMoved);\r
16521 </code></pre>\r
16522  * @constructor\r
16523  * Create a new SplitBar\r
16524  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.\r
16525  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged\r
16526  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
16527  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or  \r
16528                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial\r
16529                         position of the SplitBar).\r
16530  */\r
16531 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){\r
16532     \r
16533     /** @private */\r
16534     this.el = Ext.get(dragElement, true);\r
16535     this.el.dom.unselectable = "on";\r
16536     /** @private */\r
16537     this.resizingEl = Ext.get(resizingElement, true);\r
16538 \r
16539     /**\r
16540      * @private\r
16541      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
16542      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated\r
16543      * @type Number\r
16544      */\r
16545     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;\r
16546     \r
16547     /**\r
16548      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.\r
16549      * @type Number\r
16550      * @property tickSize\r
16551      */\r
16552     /**\r
16553      * The minimum size of the resizing element. (Defaults to 0)\r
16554      * @type Number\r
16555      */\r
16556     this.minSize = 0;\r
16557     \r
16558     /**\r
16559      * The maximum size of the resizing element. (Defaults to 2000)\r
16560      * @type Number\r
16561      */\r
16562     this.maxSize = 2000;\r
16563     \r
16564     /**\r
16565      * Whether to animate the transition to the new size\r
16566      * @type Boolean\r
16567      */\r
16568     this.animate = false;\r
16569     \r
16570     /**\r
16571      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.\r
16572      * @type Boolean\r
16573      */\r
16574     this.useShim = false;\r
16575     \r
16576     /** @private */\r
16577     this.shim = null;\r
16578     \r
16579     if(!existingProxy){\r
16580         /** @private */\r
16581         this.proxy = Ext.SplitBar.createProxy(this.orientation);\r
16582     }else{\r
16583         this.proxy = Ext.get(existingProxy).dom;\r
16584     }\r
16585     /** @private */\r
16586     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});\r
16587     \r
16588     /** @private */\r
16589     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);\r
16590     \r
16591     /** @private */\r
16592     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);\r
16593     \r
16594     /** @private */\r
16595     this.dragSpecs = {};\r
16596     \r
16597     /**\r
16598      * @private The adapter to use to positon and resize elements\r
16599      */\r
16600     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();\r
16601     this.adapter.init(this);\r
16602     \r
16603     if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
16604         /** @private */\r
16605         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);\r
16606         this.el.addClass("x-splitbar-h");\r
16607     }else{\r
16608         /** @private */\r
16609         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);\r
16610         this.el.addClass("x-splitbar-v");\r
16611     }\r
16612     \r
16613     this.addEvents(\r
16614         /**\r
16615          * @event resize\r
16616          * Fires when the splitter is moved (alias for {@link #moved})\r
16617          * @param {Ext.SplitBar} this\r
16618          * @param {Number} newSize the new width or height\r
16619          */\r
16620         "resize",\r
16621         /**\r
16622          * @event moved\r
16623          * Fires when the splitter is moved\r
16624          * @param {Ext.SplitBar} this\r
16625          * @param {Number} newSize the new width or height\r
16626          */\r
16627         "moved",\r
16628         /**\r
16629          * @event beforeresize\r
16630          * Fires before the splitter is dragged\r
16631          * @param {Ext.SplitBar} this\r
16632          */\r
16633         "beforeresize",\r
16634 \r
16635         "beforeapply"\r
16636     );\r
16637 \r
16638     Ext.SplitBar.superclass.constructor.call(this);\r
16639 };\r
16640 \r
16641 Ext.extend(Ext.SplitBar, Ext.util.Observable, {\r
16642     onStartProxyDrag : function(x, y){\r
16643         this.fireEvent("beforeresize", this);\r
16644         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);\r
16645         this.overlay.unselectable();\r
16646         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
16647         this.overlay.show();\r
16648         Ext.get(this.proxy).setDisplayed("block");\r
16649         var size = this.adapter.getElementSize(this);\r
16650         this.activeMinSize = this.getMinimumSize();\r
16651         this.activeMaxSize = this.getMaximumSize();\r
16652         var c1 = size - this.activeMinSize;\r
16653         var c2 = Math.max(this.activeMaxSize - size, 0);\r
16654         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
16655             this.dd.resetConstraints();\r
16656             this.dd.setXConstraint(\r
16657                 this.placement == Ext.SplitBar.LEFT ? c1 : c2, \r
16658                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,\r
16659                 this.tickSize\r
16660             );\r
16661             this.dd.setYConstraint(0, 0);\r
16662         }else{\r
16663             this.dd.resetConstraints();\r
16664             this.dd.setXConstraint(0, 0);\r
16665             this.dd.setYConstraint(\r
16666                 this.placement == Ext.SplitBar.TOP ? c1 : c2, \r
16667                 this.placement == Ext.SplitBar.TOP ? c2 : c1,\r
16668                 this.tickSize\r
16669             );\r
16670          }\r
16671         this.dragSpecs.startSize = size;\r
16672         this.dragSpecs.startPoint = [x, y];\r
16673         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);\r
16674     },\r
16675     \r
16676     /** \r
16677      * @private Called after the drag operation by the DDProxy\r
16678      */\r
16679     onEndProxyDrag : function(e){\r
16680         Ext.get(this.proxy).setDisplayed(false);\r
16681         var endPoint = Ext.lib.Event.getXY(e);\r
16682         if(this.overlay){\r
16683             Ext.destroy(this.overlay);\r
16684             delete this.overlay;\r
16685         }\r
16686         var newSize;\r
16687         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
16688             newSize = this.dragSpecs.startSize + \r
16689                 (this.placement == Ext.SplitBar.LEFT ?\r
16690                     endPoint[0] - this.dragSpecs.startPoint[0] :\r
16691                     this.dragSpecs.startPoint[0] - endPoint[0]\r
16692                 );\r
16693         }else{\r
16694             newSize = this.dragSpecs.startSize + \r
16695                 (this.placement == Ext.SplitBar.TOP ?\r
16696                     endPoint[1] - this.dragSpecs.startPoint[1] :\r
16697                     this.dragSpecs.startPoint[1] - endPoint[1]\r
16698                 );\r
16699         }\r
16700         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);\r
16701         if(newSize != this.dragSpecs.startSize){\r
16702             if(this.fireEvent('beforeapply', this, newSize) !== false){\r
16703                 this.adapter.setElementSize(this, newSize);\r
16704                 this.fireEvent("moved", this, newSize);\r
16705                 this.fireEvent("resize", this, newSize);\r
16706             }\r
16707         }\r
16708     },\r
16709     \r
16710     /**\r
16711      * Get the adapter this SplitBar uses\r
16712      * @return The adapter object\r
16713      */\r
16714     getAdapter : function(){\r
16715         return this.adapter;\r
16716     },\r
16717     \r
16718     /**\r
16719      * Set the adapter this SplitBar uses\r
16720      * @param {Object} adapter A SplitBar adapter object\r
16721      */\r
16722     setAdapter : function(adapter){\r
16723         this.adapter = adapter;\r
16724         this.adapter.init(this);\r
16725     },\r
16726     \r
16727     /**\r
16728      * Gets the minimum size for the resizing element\r
16729      * @return {Number} The minimum size\r
16730      */\r
16731     getMinimumSize : function(){\r
16732         return this.minSize;\r
16733     },\r
16734     \r
16735     /**\r
16736      * Sets the minimum size for the resizing element\r
16737      * @param {Number} minSize The minimum size\r
16738      */\r
16739     setMinimumSize : function(minSize){\r
16740         this.minSize = minSize;\r
16741     },\r
16742     \r
16743     /**\r
16744      * Gets the maximum size for the resizing element\r
16745      * @return {Number} The maximum size\r
16746      */\r
16747     getMaximumSize : function(){\r
16748         return this.maxSize;\r
16749     },\r
16750     \r
16751     /**\r
16752      * Sets the maximum size for the resizing element\r
16753      * @param {Number} maxSize The maximum size\r
16754      */\r
16755     setMaximumSize : function(maxSize){\r
16756         this.maxSize = maxSize;\r
16757     },\r
16758     \r
16759     /**\r
16760      * Sets the initialize size for the resizing element\r
16761      * @param {Number} size The initial size\r
16762      */\r
16763     setCurrentSize : function(size){\r
16764         var oldAnimate = this.animate;\r
16765         this.animate = false;\r
16766         this.adapter.setElementSize(this, size);\r
16767         this.animate = oldAnimate;\r
16768     },\r
16769     \r
16770     /**\r
16771      * Destroy this splitbar. \r
16772      * @param {Boolean} removeEl True to remove the element\r
16773      */\r
16774     destroy : function(removeEl){\r
16775                 Ext.destroy(this.shim, Ext.get(this.proxy));\r
16776         this.dd.unreg();\r
16777         if(removeEl){\r
16778             this.el.remove();\r
16779         }\r
16780                 this.purgeListeners();\r
16781     }\r
16782 });\r
16783 \r
16784 /**\r
16785  * @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.\r
16786  */\r
16787 Ext.SplitBar.createProxy = function(dir){\r
16788     var proxy = new Ext.Element(document.createElement("div"));\r
16789     proxy.unselectable();\r
16790     var cls = 'x-splitbar-proxy';\r
16791     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));\r
16792     document.body.appendChild(proxy.dom);\r
16793     return proxy.dom;\r
16794 };\r
16795 \r
16796 /** \r
16797  * @class Ext.SplitBar.BasicLayoutAdapter\r
16798  * Default Adapter. It assumes the splitter and resizing element are not positioned\r
16799  * elements and only gets/sets the width of the element. Generally used for table based layouts.\r
16800  */\r
16801 Ext.SplitBar.BasicLayoutAdapter = function(){\r
16802 };\r
16803 \r
16804 Ext.SplitBar.BasicLayoutAdapter.prototype = {\r
16805     // do nothing for now\r
16806     init : function(s){\r
16807     \r
16808     },\r
16809     /**\r
16810      * Called before drag operations to get the current size of the resizing element. \r
16811      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
16812      */\r
16813      getElementSize : function(s){\r
16814         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
16815             return s.resizingEl.getWidth();\r
16816         }else{\r
16817             return s.resizingEl.getHeight();\r
16818         }\r
16819     },\r
16820     \r
16821     /**\r
16822      * Called after drag operations to set the size of the resizing element.\r
16823      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
16824      * @param {Number} newSize The new size to set\r
16825      * @param {Function} onComplete A function to be invoked when resizing is complete\r
16826      */\r
16827     setElementSize : function(s, newSize, onComplete){\r
16828         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
16829             if(!s.animate){\r
16830                 s.resizingEl.setWidth(newSize);\r
16831                 if(onComplete){\r
16832                     onComplete(s, newSize);\r
16833                 }\r
16834             }else{\r
16835                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');\r
16836             }\r
16837         }else{\r
16838             \r
16839             if(!s.animate){\r
16840                 s.resizingEl.setHeight(newSize);\r
16841                 if(onComplete){\r
16842                     onComplete(s, newSize);\r
16843                 }\r
16844             }else{\r
16845                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');\r
16846             }\r
16847         }\r
16848     }\r
16849 };\r
16850 \r
16851 /** \r
16852  *@class Ext.SplitBar.AbsoluteLayoutAdapter\r
16853  * @extends Ext.SplitBar.BasicLayoutAdapter\r
16854  * Adapter that  moves the splitter element to align with the resized sizing element. \r
16855  * Used with an absolute positioned SplitBar.\r
16856  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's\r
16857  * document.body, make sure you assign an id to the body element.\r
16858  */\r
16859 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){\r
16860     this.basic = new Ext.SplitBar.BasicLayoutAdapter();\r
16861     this.container = Ext.get(container);\r
16862 };\r
16863 \r
16864 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {\r
16865     init : function(s){\r
16866         this.basic.init(s);\r
16867     },\r
16868     \r
16869     getElementSize : function(s){\r
16870         return this.basic.getElementSize(s);\r
16871     },\r
16872     \r
16873     setElementSize : function(s, newSize, onComplete){\r
16874         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));\r
16875     },\r
16876     \r
16877     moveSplitter : function(s){\r
16878         var yes = Ext.SplitBar;\r
16879         switch(s.placement){\r
16880             case yes.LEFT:\r
16881                 s.el.setX(s.resizingEl.getRight());\r
16882                 break;\r
16883             case yes.RIGHT:\r
16884                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");\r
16885                 break;\r
16886             case yes.TOP:\r
16887                 s.el.setY(s.resizingEl.getBottom());\r
16888                 break;\r
16889             case yes.BOTTOM:\r
16890                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());\r
16891                 break;\r
16892         }\r
16893     }\r
16894 };\r
16895 \r
16896 /**\r
16897  * Orientation constant - Create a vertical SplitBar\r
16898  * @static\r
16899  * @type Number\r
16900  */\r
16901 Ext.SplitBar.VERTICAL = 1;\r
16902 \r
16903 /**\r
16904  * Orientation constant - Create a horizontal SplitBar\r
16905  * @static\r
16906  * @type Number\r
16907  */\r
16908 Ext.SplitBar.HORIZONTAL = 2;\r
16909 \r
16910 /**\r
16911  * Placement constant - The resizing element is to the left of the splitter element\r
16912  * @static\r
16913  * @type Number\r
16914  */\r
16915 Ext.SplitBar.LEFT = 1;\r
16916 \r
16917 /**\r
16918  * Placement constant - The resizing element is to the right of the splitter element\r
16919  * @static\r
16920  * @type Number\r
16921  */\r
16922 Ext.SplitBar.RIGHT = 2;\r
16923 \r
16924 /**\r
16925  * Placement constant - The resizing element is positioned above the splitter element\r
16926  * @static\r
16927  * @type Number\r
16928  */\r
16929 Ext.SplitBar.TOP = 3;\r
16930 \r
16931 /**\r
16932  * Placement constant - The resizing element is positioned under splitter element\r
16933  * @static\r
16934  * @type Number\r
16935  */\r
16936 Ext.SplitBar.BOTTOM = 4;\r
16937 /**
16938  * @class Ext.Container
16939  * @extends Ext.BoxComponent
16940  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
16941  * basic behavior of containing items, namely adding, inserting and removing items.</p>
16942  *
16943  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
16944  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
16945  * Container to be encapsulated by an HTML element to your specifications by using the
16946  * <tt><b>{@link Ext.Component#autoEl autoEl}</b></tt> config option. This is a useful technique when creating
16947  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
16948  * for example.</p>
16949  *
16950  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
16951  * create one using the <b><tt>'container'</tt></b> xtype:<pre><code>
16952 // explicitly create a Container
16953 var embeddedColumns = new Ext.Container({
16954     autoEl: 'div',  // This is the default
16955     layout: 'column',
16956     defaults: {
16957         // implicitly create Container by specifying xtype
16958         xtype: 'container',
16959         autoEl: 'div', // This is the default.
16960         layout: 'form',
16961         columnWidth: 0.5,
16962         style: {
16963             padding: '10px'
16964         }
16965     },
16966 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.
16967     items: [{
16968         items: {
16969             xtype: 'datefield',
16970             name: 'startDate',
16971             fieldLabel: 'Start date'
16972         }
16973     }, {
16974         items: {
16975             xtype: 'datefield',
16976             name: 'endDate',
16977             fieldLabel: 'End date'
16978         }
16979     }]
16980 });</code></pre></p>
16981  *
16982  * <p><u><b>Layout</b></u></p>
16983  * <p>Container classes delegate the rendering of child Components to a layout
16984  * manager class which must be configured into the Container using the
16985  * <code><b>{@link #layout}</b></code> configuration property.</p>
16986  * <p>When either specifying child <code>{@link #items}</code> of a Container,
16987  * or dynamically {@link #add adding} Components to a Container, remember to
16988  * consider how you wish the Container to arrange those child elements, and
16989  * whether those child elements need to be sized using one of Ext's built-in
16990  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
16991  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
16992  * renders child components, appending them one after the other inside the
16993  * Container, and <b>does not apply any sizing</b> at all.</p>
16994  * <p>A common mistake is when a developer neglects to specify a
16995  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
16996  * TreePanels are added to Containers for which no <tt><b>{@link #layout}</b></tt>
16997  * has been specified). If a Container is left to use the default
16998  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its
16999  * child components will be resized, or changed in any way when the Container
17000  * is resized.</p>
17001  * <p>Certain layout managers allow dynamic addition of child components.
17002  * Those that do include {@link Ext.layout.CardLayout},
17003  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and
17004  * {@link Ext.layout.TableLayout}. For example:<pre><code>
17005 //  Create the GridPanel.
17006 var myNewGrid = new Ext.grid.GridPanel({
17007     store: myStore,
17008     columns: myColumnModel,
17009     title: 'Results', // the title becomes the title of the tab
17010 });
17011
17012 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
17013 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
17014  * </code></pre></p>
17015  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
17016  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which
17017  * means all its child items are sized to {@link Ext.layout.FitLayout fit}
17018  * exactly into its client area.
17019  * <p><b><u>Overnesting is a common problem</u></b>.
17020  * An example of overnesting occurs when a GridPanel is added to a TabPanel
17021  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
17022  * <tt><b>{@link #layout}</b></tt> specified) and then add that wrapping Panel
17023  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
17024  * Component which can be added directly to a Container. If the wrapping Panel
17025  * has no <tt><b>{@link #layout}</b></tt> configuration, then the overnested
17026  * GridPanel will not be sized as expected.<p>
17027 </code></pre>
17028  *
17029  * <p><u><b>Adding via remote configuration</b></u></p>
17030  *
17031  * <p>A server side script can be used to add Components which are generated dynamically on the server.
17032  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
17033  * based on certain parameters:
17034  * </p><pre><code>
17035 // execute an Ajax request to invoke server side script:
17036 Ext.Ajax.request({
17037     url: 'gen-invoice-grid.php',
17038     // send additional parameters to instruct server script
17039     params: {
17040         startDate: Ext.getCmp('start-date').getValue(),
17041         endDate: Ext.getCmp('end-date').getValue()
17042     },
17043     // process the response object to add it to the TabPanel:
17044     success: function(xhr) {
17045         var newComponent = eval(xhr.responseText); // see discussion below
17046         myTabPanel.add(newComponent); // add the component to the TabPanel
17047         myTabPanel.setActiveTab(newComponent);
17048     },
17049     failure: function() {
17050         Ext.Msg.alert("Grid create failed", "Server communication failure");
17051     }
17052 });
17053 </code></pre>
17054  * <p>The server script needs to return an executable Javascript statement which, when processed
17055  * using <tt>eval()</tt>, will return either a config object with an {@link Ext.Component#xtype xtype},
17056  * or an instantiated Component. The server might return this for example:</p><pre><code>
17057 (function() {
17058     function formatDate(value){
17059         return value ? value.dateFormat('M d, Y') : '';
17060     };
17061
17062     var store = new Ext.data.Store({
17063         url: 'get-invoice-data.php',
17064         baseParams: {
17065             startDate: '01/01/2008',
17066             endDate: '01/31/2008'
17067         },
17068         reader: new Ext.data.JsonReader({
17069             record: 'transaction',
17070             idProperty: 'id',
17071             totalRecords: 'total'
17072         }, [
17073            'customer',
17074            'invNo',
17075            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
17076            {name: 'value', type: 'float'}
17077         ])
17078     });
17079
17080     var grid = new Ext.grid.GridPanel({
17081         title: 'Invoice Report',
17082         bbar: new Ext.PagingToolbar(store),
17083         store: store,
17084         columns: [
17085             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
17086             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
17087             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
17088             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
17089         ],
17090     });
17091     store.load();
17092     return grid;  // return instantiated component
17093 })();
17094 </code></pre>
17095  * <p>When the above code fragment is passed through the <tt>eval</tt> function in the success handler
17096  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
17097  * runs, and returns the instantiated grid component.</p>
17098  * <p>Note: since the code above is <i>generated</i> by a server script, the <tt>baseParams</tt> for
17099  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel
17100  * can all be generated into the code since these are all known on the server.</p>
17101  *
17102  * @xtype container
17103  */
17104 Ext.Container = Ext.extend(Ext.BoxComponent, {
17105     /**
17106      * @cfg {Boolean} monitorResize
17107      * True to automatically monitor window resize events to handle anything that is sensitive to the current size
17108      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need
17109      * to be set manually.
17110      */
17111     /**
17112      * @cfg {String/Object} layout
17113      * When creating complex UIs, it is important to remember that sizing and
17114      * positioning of child items is the responsibility of the Container's
17115      * layout manager. If you expect child items to be sized in response to
17116      * user interactions, <b>you must specify a layout manager</b> which
17117      * creates and manages the type of layout you have in mind.  For example:<pre><code>
17118 new Ext.Window({
17119     width:300, height: 300,
17120     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
17121     items: [{
17122         title: 'Panel inside a Window'
17123     }]
17124 }).show();
17125      * </code></pre>
17126      * <p>Omitting the {@link #layout} config means that the
17127      * {@link Ext.layout.ContainerLayout default layout manager} will be used which does
17128      * nothing but render child components sequentially into the Container (no sizing or
17129      * positioning will be performed in this situation).</p>
17130      * <p>The layout manager class for this container may be specified as either as an
17131      * Object or as a String:</p>
17132      * <div><ul class="mdetail-params">
17133      *
17134      * <li><u>Specify as an Object</u></li>
17135      * <div><ul class="mdetail-params">
17136      * <li>Example usage:</li>
17137 <pre><code>
17138 layout: {
17139     type: 'vbox',
17140     padding: '5',
17141     align: 'left'
17142 }
17143 </code></pre>
17144      *
17145      * <li><tt><b>type</b></tt></li>
17146      * <br/><p>The layout type to be used for this container.  If not specified,
17147      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
17148      * <br/><p>Valid layout <tt>type</tt> values are:</p>
17149      * <div class="sub-desc"><ul class="mdetail-params">
17150      * <li><tt><b>{@link Ext.layout.AbsoluteLayout absolute}</b></tt></li>
17151      * <li><tt><b>{@link Ext.layout.AccordionLayout accordion}</b></tt></li>
17152      * <li><tt><b>{@link Ext.layout.AnchorLayout anchor}</b></tt></li>
17153      * <li><tt><b>{@link Ext.layout.ContainerLayout auto}</b></tt> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
17154      * <li><tt><b>{@link Ext.layout.BorderLayout border}</b></tt></li>
17155      * <li><tt><b>{@link Ext.layout.CardLayout card}</b></tt></li>
17156      * <li><tt><b>{@link Ext.layout.ColumnLayout column}</b></tt></li>
17157      * <li><tt><b>{@link Ext.layout.FitLayout fit}</b></tt></li>
17158      * <li><tt><b>{@link Ext.layout.FormLayout form}</b></tt></li>
17159      * <li><tt><b>{@link Ext.layout.HBoxLayout hbox}</b></tt></li>
17160      * <li><tt><b>{@link Ext.layout.MenuLayout menu}</b></tt></li>
17161      * <li><tt><b>{@link Ext.layout.TableLayout table}</b></tt></li>
17162      * <li><tt><b>{@link Ext.layout.ToolbarLayout toolbar}</b></tt></li>
17163      * <li><tt><b>{@link Ext.layout.VBoxLayout vbox}</b></tt></li>
17164      * </ul></div>
17165      *
17166      * <li>Layout specific configuration properties</li>
17167      * <br/><p>Additional layout specific configuration properties may also be
17168      * specified. For complete details regarding the valid config options for
17169      * each layout type, see the layout class corresponding to the <tt>type</tt>
17170      * specified.</p>
17171      *
17172      * </ul></div>
17173      *
17174      * <li><u>Specify as a String</u></li>
17175      * <div><ul class="mdetail-params">
17176      * <li>Example usage:</li>
17177 <pre><code>
17178 layout: 'vbox',
17179 layoutConfig: {
17180     padding: '5',
17181     align: 'left'
17182 }
17183 </code></pre>
17184      * <li><tt><b>layout</b></tt></li>
17185      * <br/><p>The layout <tt>type</tt> to be used for this container (see list
17186      * of valid layout type values above).</p><br/>
17187      * <li><tt><b>{@link #layoutConfig}</b></tt></li>
17188      * <br/><p>Additional layout specific configuration properties. For complete
17189      * details regarding the valid config options for each layout type, see the
17190      * layout class corresponding to the <tt>layout</tt> specified.</p>
17191      * </ul></div></ul></div>
17192      */
17193     /**
17194      * @cfg {Object} layoutConfig
17195      * This is a config object containing properties specific to the chosen
17196      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>
17197      * has been specified as a <i>string</i>.</p>
17198      */
17199     /**
17200      * @cfg {Boolean/Number} bufferResize
17201      * When set to true (100 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
17202      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
17203      * with a large quantity of sub-components for which frequent layout calls would be expensive.
17204      */
17205     bufferResize: 100,
17206     
17207     /**
17208      * @cfg {String/Number} activeItem
17209      * A string component id or the numeric index of the component that should be initially activated within the
17210      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
17211      * item in the container's collection).  activeItem only applies to layout styles that can display
17212      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and
17213      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.
17214      */
17215     /**
17216      * @cfg {Object/Array} items
17217      * <pre><b>** IMPORTANT</b>: be sure to specify a <b><code>{@link #layout}</code> ! **</b></pre>
17218      * <p>A single item, or an array of child Components to be added to this container,
17219      * for example:</p>
17220      * <pre><code>
17221 // specifying a single item
17222 items: {...},
17223 layout: 'fit',    // specify a layout!
17224
17225 // specifying multiple items
17226 items: [{...}, {...}],
17227 layout: 'anchor', // specify a layout!
17228      * </code></pre>
17229      * <p>Each item may be:</p>
17230      * <div><ul class="mdetail-params">
17231      * <li>any type of object based on {@link Ext.Component}</li>
17232      * <li>a fully instanciated object or</li>
17233      * <li>an object literal that:</li>
17234      * <div><ul class="mdetail-params">
17235      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
17236      * <li>the {@link Ext.Component#xtype} specified is associated with the Component
17237      * desired and should be chosen from one of the available xtypes as listed
17238      * in {@link Ext.Component}.</li>
17239      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
17240      * specified, the {@link #defaultType} for that Container is used.</li>
17241      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
17242      * instanciated Component object</li>
17243      * </ul></div></ul></div>
17244      * <p><b>Notes</b>:</p>
17245      * <div><ul class="mdetail-params">
17246      * <li>Ext uses lazy rendering. Child Components will only be rendered
17247      * should it become necessary. Items are automatically laid out when they are first
17248      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
17249      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
17250      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
17251      * </ul></div>
17252      */
17253     /**
17254      * @cfg {Object} defaults
17255      * <p>A config object that will be applied to all components added to this container either via the {@link #items}
17256      * config or via the {@link #add} or {@link #insert} methods.  The <tt>defaults</tt> config can contain any
17257      * number of name/value property pairs to be added to each item, and should be valid for the types of items
17258      * being added to the container.  For example, to automatically apply padding to the body of each of a set of
17259      * contained {@link Ext.Panel} items, you could pass: <tt>defaults: {bodyStyle:'padding:15px'}</tt>.</p><br/>
17260      * <p><b>Note</b>: <tt>defaults</tt> will not be applied to config objects if the option is already specified.
17261      * For example:</p><pre><code>
17262 defaults: {               // defaults are applied to items, not the container
17263     autoScroll:true
17264 },
17265 items: [
17266     {
17267         xtype: 'panel',   // defaults <b>do not</b> have precedence over
17268         id: 'panel1',     // options in config objects, so the defaults
17269         autoScroll: false // will not be applied here, panel1 will be autoScroll:false
17270     },
17271     new Ext.Panel({       // defaults <b>do</b> have precedence over options
17272         id: 'panel2',     // options in components, so the defaults
17273         autoScroll: false // will be applied here, panel2 will be autoScroll:true.
17274     })
17275 ]
17276      * </code></pre>
17277      */
17278
17279
17280     /** @cfg {Boolean} autoDestroy
17281      * If true the container will automatically destroy any contained component that is removed from it, else
17282      * destruction must be handled manually (defaults to true).
17283      */
17284     autoDestroy : true,
17285
17286     /** @cfg {Boolean} forceLayout
17287      * If true the container will force a layout initially even if hidden or collapsed. This option
17288      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).
17289      */
17290     forceLayout: false,
17291
17292     /** @cfg {Boolean} hideBorders
17293      * True to hide the borders of each contained component, false to defer to the component's existing
17294      * border settings (defaults to false).
17295      */
17296     /** @cfg {String} defaultType
17297      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
17298      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
17299      * <p>Defaults to <tt>'panel'</tt>, except {@link Ext.menu.Menu} which defaults to <tt>'menuitem'</tt>,
17300      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <tt>'button'</tt>.</p>
17301      */
17302     defaultType : 'panel',
17303
17304     // private
17305     initComponent : function(){
17306         Ext.Container.superclass.initComponent.call(this);
17307
17308         this.addEvents(
17309             /**
17310              * @event afterlayout
17311              * Fires when the components in this container are arranged by the associated layout manager.
17312              * @param {Ext.Container} this
17313              * @param {ContainerLayout} layout The ContainerLayout implementation for this container
17314              */
17315             'afterlayout',
17316             /**
17317              * @event beforeadd
17318              * Fires before any {@link Ext.Component} is added or inserted into the container.
17319              * A handler can return false to cancel the add.
17320              * @param {Ext.Container} this
17321              * @param {Ext.Component} component The component being added
17322              * @param {Number} index The index at which the component will be added to the container's items collection
17323              */
17324             'beforeadd',
17325             /**
17326              * @event beforeremove
17327              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
17328              * false to cancel the remove.
17329              * @param {Ext.Container} this
17330              * @param {Ext.Component} component The component being removed
17331              */
17332             'beforeremove',
17333             /**
17334              * @event add
17335              * @bubbles
17336              * Fires after any {@link Ext.Component} is added or inserted into the container.
17337              * @param {Ext.Container} this
17338              * @param {Ext.Component} component The component that was added
17339              * @param {Number} index The index at which the component was added to the container's items collection
17340              */
17341             'add',
17342             /**
17343              * @event remove
17344              * @bubbles
17345              * Fires after any {@link Ext.Component} is removed from the container.
17346              * @param {Ext.Container} this
17347              * @param {Ext.Component} component The component that was removed
17348              */
17349             'remove'
17350         );
17351
17352                 this.enableBubble('add', 'remove');
17353
17354         /**
17355          * The collection of components in this container as a {@link Ext.util.MixedCollection}
17356          * @type MixedCollection
17357          * @property items
17358          */
17359         var items = this.items;
17360         if(items){
17361             delete this.items;
17362             if(Ext.isArray(items) && items.length > 0){
17363                 this.add.apply(this, items);
17364             }else{
17365                 this.add(items);
17366             }
17367         }
17368     },
17369
17370     // private
17371     initItems : function(){
17372         if(!this.items){
17373             this.items = new Ext.util.MixedCollection(false, this.getComponentId);
17374             this.getLayout(); // initialize the layout
17375         }
17376     },
17377
17378     // private
17379     setLayout : function(layout){
17380         if(this.layout && this.layout != layout){
17381             this.layout.setContainer(null);
17382         }
17383         this.initItems();
17384         this.layout = layout;
17385         layout.setContainer(this);
17386     },
17387
17388     // private
17389     render : function(){
17390         Ext.Container.superclass.render.apply(this, arguments);
17391         if(this.layout){
17392             if(Ext.isObject(this.layout) && !this.layout.layout){
17393                 this.layoutConfig = this.layout;
17394                 this.layout = this.layoutConfig.type;
17395             }
17396             if(typeof this.layout == 'string'){
17397                 this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
17398             }
17399             this.setLayout(this.layout);
17400
17401             if(this.activeItem !== undefined){
17402                 var item = this.activeItem;
17403                 delete this.activeItem;
17404                 this.layout.setActiveItem(item);
17405             }
17406         }
17407         if(!this.ownerCt){
17408             // force a layout if no ownerCt is set
17409             this.doLayout(false, true);
17410         }
17411         if(this.monitorResize === true){
17412             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
17413         }
17414     },
17415
17416     /**
17417      * <p>Returns the Element to be used to contain the child Components of this Container.</p>
17418      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
17419      * if there is a more complex structure to a Container, this may be overridden to return
17420      * the element into which the {@link #layout layout} renders child Components.</p>
17421      * @return {Ext.Element} The Element to render child Components into.
17422      */
17423     getLayoutTarget : function(){
17424         return this.el;
17425     },
17426
17427     // private - used as the key lookup function for the items collection
17428     getComponentId : function(comp){
17429         return comp.getItemId();
17430     },
17431
17432     /**
17433      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
17434      * <br><p><b>Description</b></u> :
17435      * <div><ul class="mdetail-params">
17436      * <li>Fires the {@link #beforeadd} event before adding</li>
17437      * <li>The Container's {@link #defaults default config values} will be applied
17438      * accordingly (see <code>{@link #defaults}</code> for details).</li>
17439      * <li>Fires the {@link #add} event after the component has been added.</li>
17440      * </ul></div>
17441      * <br><p><b>Notes</b></u> :
17442      * <div><ul class="mdetail-params">
17443      * <li>If the Container is <i>already rendered</i> when <tt>add</tt>
17444      * is called, you may need to call {@link #doLayout} to refresh the view which causes
17445      * any unrendered child Components to be rendered. This is required so that you can
17446      * <tt>add</tt> multiple child components if needed while only refreshing the layout
17447      * once. For example:<pre><code>
17448 var tb = new {@link Ext.Toolbar}();
17449 tb.render(document.body);  // toolbar is rendered
17450 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
17451 tb.add({text:'Button 2'});
17452 tb.{@link #doLayout}();             // refresh the layout
17453      * </code></pre></li>
17454      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
17455      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
17456      * for more details.</li>
17457      * </ul></div>
17458      * @param {Object/Array} component
17459      * <p>Either a single component or an Array of components to add.  See
17460      * <code>{@link #items}</code> for additional information.</p>
17461      * @param {Object} (Optional) component_2
17462      * @param {Object} (Optional) component_n
17463      * @return {Ext.Component} component The Component (or config object) that was added.
17464      */
17465     add : function(comp){
17466         this.initItems();
17467         var args = arguments.length > 1;
17468         if(args || Ext.isArray(comp)){
17469             Ext.each(args ? arguments : comp, function(c){
17470                 this.add(c);
17471             }, this);
17472             return;
17473         }
17474         var c = this.lookupComponent(this.applyDefaults(comp));
17475         var pos = this.items.length;
17476         if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){
17477             this.items.add(c);
17478             c.ownerCt = this;
17479             this.fireEvent('add', this, c, pos);
17480         }
17481         return c;
17482     },
17483
17484     /**
17485      * Inserts a Component into this Container at a specified index. Fires the
17486      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
17487      * Component has been inserted.
17488      * @param {Number} index The index at which the Component will be inserted
17489      * into the Container's items collection
17490      * @param {Ext.Component} component The child Component to insert.<br><br>
17491      * Ext uses lazy rendering, and will only render the inserted Component should
17492      * it become necessary.<br><br>
17493      * A Component config object may be passed in order to avoid the overhead of
17494      * constructing a real Component object if lazy rendering might mean that the
17495      * inserted Component will not be rendered immediately. To take advantage of
17496      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
17497      * property to the registered type of the Component wanted.<br><br>
17498      * For a list of all available xtypes, see {@link Ext.Component}.
17499      * @return {Ext.Component} component The Component (or config object) that was
17500      * inserted with the Container's default config values applied.
17501      */
17502     insert : function(index, comp){
17503         this.initItems();
17504         var a = arguments, len = a.length;
17505         if(len > 2){
17506             for(var i = len-1; i >= 1; --i) {
17507                 this.insert(index, a[i]);
17508             }
17509             return;
17510         }
17511         var c = this.lookupComponent(this.applyDefaults(comp));
17512
17513         if(c.ownerCt == this && this.items.indexOf(c) < index){
17514             --index;
17515         }
17516
17517         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
17518             this.items.insert(index, c);
17519             c.ownerCt = this;
17520             this.fireEvent('add', this, c, index);
17521         }
17522         return c;
17523     },
17524
17525     // private
17526     applyDefaults : function(c){
17527         if(this.defaults){
17528             if(typeof c == 'string'){
17529                 c = Ext.ComponentMgr.get(c);
17530                 Ext.apply(c, this.defaults);
17531             }else if(!c.events){
17532                 Ext.applyIf(c, this.defaults);
17533             }else{
17534                 Ext.apply(c, this.defaults);
17535             }
17536         }
17537         return c;
17538     },
17539
17540     // private
17541     onBeforeAdd : function(item){
17542         if(item.ownerCt){
17543             item.ownerCt.remove(item, false);
17544         }
17545         if(this.hideBorders === true){
17546             item.border = (item.border === true);
17547         }
17548     },
17549
17550     /**
17551      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
17552      * the {@link #remove} event after the component has been removed.
17553      * @param {Component/String} component The component reference or id to remove.
17554      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
17555      * Defaults to the value of this Container's {@link #autoDestroy} config.
17556      * @return {Ext.Component} component The Component that was removed.
17557      */
17558     remove : function(comp, autoDestroy){
17559         this.initItems();
17560         var c = this.getComponent(comp);
17561         if(c && this.fireEvent('beforeremove', this, c) !== false){
17562             this.items.remove(c);
17563             delete c.ownerCt;
17564             if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
17565                 c.destroy();
17566             }
17567             if(this.layout && this.layout.activeItem == c){
17568                 delete this.layout.activeItem;
17569             }
17570             this.fireEvent('remove', this, c);
17571         }
17572         return c;
17573     },
17574
17575     /**
17576      * Removes all components from this container.
17577      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
17578      * Defaults to the value of this Container's {@link #autoDestroy} config.
17579      * @return {Array} Array of the destroyed components
17580      */
17581     removeAll: function(autoDestroy){
17582         this.initItems();
17583         var item, rem = [], items = [];
17584         this.items.each(function(i){
17585             rem.push(i);
17586         });
17587         for (var i = 0, len = rem.length; i < len; ++i){
17588             item = rem[i];
17589             this.remove(item, autoDestroy);
17590             if(item.ownerCt !== this){
17591                 items.push(item);
17592             }
17593         }
17594         return items;
17595     },
17596
17597     /**
17598      * Examines this container's <code>{@link #items}</code> <b>property</b>
17599      * and gets a direct child component of this container.
17600      * @param {String/Number} comp This parameter may be any of the following:
17601      * <div><ul class="mdetail-params">
17602      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
17603      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
17604      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
17605      * within the <code>{@link #items}</code> <b>property</b></li>
17606      * </ul></div>
17607      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
17608      * @return Ext.Component The component (if found).
17609      */
17610     getComponent : function(comp){
17611         if(Ext.isObject(comp)){
17612             return comp;
17613         }
17614         return this.items.get(comp);
17615     },
17616
17617     // private
17618     lookupComponent : function(comp){
17619         if(typeof comp == 'string'){
17620             return Ext.ComponentMgr.get(comp);
17621         }else if(!comp.events){
17622             return this.createComponent(comp);
17623         }
17624         return comp;
17625     },
17626
17627     // private
17628     createComponent : function(config){
17629         return Ext.create(config, this.defaultType);
17630     },
17631
17632     /**
17633      * Force this container's layout to be recalculated. A call to this function is required after adding a new component
17634      * to an already rendered container, or possibly after changing sizing/position properties of child components.
17635      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
17636      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
17637      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.
17638      * @return {Ext.Container} this
17639      */
17640     doLayout: function(shallow, force){
17641         var rendered = this.rendered,
17642             forceLayout = this.forceLayout;
17643
17644         if(!this.isVisible() || this.collapsed){
17645             this.deferLayout = this.deferLayout || !shallow;
17646             if(!(force || forceLayout)){
17647                 return;
17648             }
17649             shallow = shallow && !this.deferLayout;
17650         } else {
17651             delete this.deferLayout;
17652         }
17653         if(rendered && this.layout){
17654             this.layout.layout();
17655         }
17656         if(shallow !== true && this.items){
17657             var cs = this.items.items;
17658             for(var i = 0, len = cs.length; i < len; i++){
17659                 var c = cs[i];
17660                 if(c.doLayout){
17661                     c.forceLayout = forceLayout;
17662                     c.doLayout();
17663                 }
17664             }
17665         }
17666         if(rendered){
17667             this.onLayout(shallow, force);
17668         }
17669         delete this.forceLayout;
17670     },
17671
17672     //private
17673     onLayout : Ext.emptyFn,
17674
17675     onShow : function(){
17676         Ext.Container.superclass.onShow.call(this);
17677         if(this.deferLayout !== undefined){
17678             this.doLayout(true);
17679         }
17680     },
17681
17682     /**
17683      * Returns the layout currently in use by the container.  If the container does not currently have a layout
17684      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
17685      * @return {ContainerLayout} layout The container's layout
17686      */
17687     getLayout : function(){
17688         if(!this.layout){
17689             var layout = new Ext.layout.ContainerLayout(this.layoutConfig);
17690             this.setLayout(layout);
17691         }
17692         return this.layout;
17693     },
17694
17695     // private
17696     beforeDestroy : function(){
17697         if(this.items){
17698             Ext.destroy.apply(Ext, this.items.items);
17699         }
17700         if(this.monitorResize){
17701             Ext.EventManager.removeResizeListener(this.doLayout, this);
17702         }
17703         Ext.destroy(this.layout);
17704         Ext.Container.superclass.beforeDestroy.call(this);
17705     },
17706
17707     /**
17708      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
17709      * function call will be the scope provided or the current component. The arguments to the function
17710      * will be the args provided or the current component. If the function returns false at any point,
17711      * the bubble is stopped.
17712      * @param {Function} fn The function to call
17713      * @param {Object} scope (optional) The scope of the function (defaults to current node)
17714      * @param {Array} args (optional) The args to call the function with (default to passing the current component)
17715      * @return {Ext.Container} this
17716      */
17717     bubble : function(fn, scope, args){
17718         var p = this;
17719         while(p){
17720             if(fn.apply(scope || p, args || [p]) === false){
17721                 break;
17722             }
17723             p = p.ownerCt;
17724         }
17725         return this;
17726     },
17727
17728     /**
17729      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
17730      * each component. The scope (<i>this</i>) of
17731      * function call will be the scope provided or the current component. The arguments to the function
17732      * will be the args provided or the current component. If the function returns false at any point,
17733      * the cascade is stopped on that branch.
17734      * @param {Function} fn The function to call
17735      * @param {Object} scope (optional) The scope of the function (defaults to current component)
17736      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
17737      * @return {Ext.Container} this
17738      */
17739     cascade : function(fn, scope, args){
17740         if(fn.apply(scope || this, args || [this]) !== false){
17741             if(this.items){
17742                 var cs = this.items.items;
17743                 for(var i = 0, len = cs.length; i < len; i++){
17744                     if(cs[i].cascade){
17745                         cs[i].cascade(fn, scope, args);
17746                     }else{
17747                         fn.apply(scope || cs[i], args || [cs[i]]);
17748                     }
17749                 }
17750             }
17751         }
17752         return this;
17753     },
17754
17755     /**
17756      * Find a component under this container at any level by id
17757      * @param {String} id
17758      * @return Ext.Component
17759      */
17760     findById : function(id){
17761         var m, ct = this;
17762         this.cascade(function(c){
17763             if(ct != c && c.id === id){
17764                 m = c;
17765                 return false;
17766             }
17767         });
17768         return m || null;
17769     },
17770
17771     /**
17772      * Find a component under this container at any level by xtype or class
17773      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
17774      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
17775      * the default), or true to check whether this Component is directly of the specified xtype.
17776      * @return {Array} Array of Ext.Components
17777      */
17778     findByType : function(xtype, shallow){
17779         return this.findBy(function(c){
17780             return c.isXType(xtype, shallow);
17781         });
17782     },
17783
17784     /**
17785      * Find a component under this container at any level by property
17786      * @param {String} prop
17787      * @param {String} value
17788      * @return {Array} Array of Ext.Components
17789      */
17790     find : function(prop, value){
17791         return this.findBy(function(c){
17792             return c[prop] === value;
17793         });
17794     },
17795
17796     /**
17797      * Find a component under this container at any level by a custom function. If the passed function returns
17798      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
17799      * @param {Function} fn The function to call
17800      * @param {Object} scope (optional)
17801      * @return {Array} Array of Ext.Components
17802      */
17803     findBy : function(fn, scope){
17804         var m = [], ct = this;
17805         this.cascade(function(c){
17806             if(ct != c && fn.call(scope || c, c, ct) === true){
17807                 m.push(c);
17808             }
17809         });
17810         return m;
17811     },
17812
17813     /**
17814      * Get a component contained by this container (alias for items.get(key))
17815      * @param {String/Number} key The index or id of the component
17816      * @return {Ext.Component} Ext.Component
17817      */
17818     get : function(key){
17819         return this.items.get(key);
17820     }
17821 });
17822
17823 Ext.Container.LAYOUTS = {};
17824 Ext.reg('container', Ext.Container);
17825 /**
17826  * @class Ext.layout.ContainerLayout
17827  * <p>The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to
17828  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
17829  * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout
17830  * classes in Ext. It simply renders all child Components into the Container, performing no sizing or
17831  * positioning services. To utilize a layout that provides sizing and positioning of child Components,
17832  * specify an appropriate <tt>{@link Ext.Container#layout layout}</tt>.</p>
17833  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
17834  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
17835  */
17836 Ext.layout.ContainerLayout = function(config){
17837     Ext.apply(this, config);
17838 };
17839
17840 Ext.layout.ContainerLayout.prototype = {
17841     /**
17842      * @cfg {String} extraCls
17843      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
17844      * customized styles to the container or any of its children using standard CSS rules. See
17845      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
17846      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
17847      * which assign a value by default:
17848      * <div class="mdetail-params"><ul>
17849      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
17850      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
17851      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
17852      * </ul></div>
17853      * To configure the above Classes with an extra CSS class append to the default.  For example,
17854      * for ColumnLayout:<pre><code>
17855      * extraCls: 'x-column custom-class'
17856      * </code></pre>
17857      * </p>
17858      */
17859     /**
17860      * @cfg {Boolean} renderHidden
17861      * True to hide each contained item on render (defaults to false).
17862      */
17863
17864     /**
17865      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
17866      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
17867      * </code></pre>
17868      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
17869      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
17870      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
17871      * @type {Ext.Component}
17872      * @property activeItem
17873      */
17874
17875     // private
17876     monitorResize:false,
17877     // private
17878     activeItem : null,
17879
17880     // private
17881     layout : function(){
17882         var target = this.container.getLayoutTarget();
17883         this.onLayout(this.container, target);
17884         this.container.fireEvent('afterlayout', this.container, this);
17885     },
17886
17887     // private
17888     onLayout : function(ct, target){
17889         this.renderAll(ct, target);
17890     },
17891
17892     // private
17893     isValidParent : function(c, target){
17894                 return target && c.getDomPositionEl().dom.parentNode == (target.dom || target);
17895     },
17896
17897     // private
17898     renderAll : function(ct, target){
17899         var items = ct.items.items;
17900         for(var i = 0, len = items.length; i < len; i++) {
17901             var c = items[i];
17902             if(c && (!c.rendered || !this.isValidParent(c, target))){
17903                 this.renderItem(c, i, target);
17904             }
17905         }
17906     },
17907
17908     // private
17909     renderItem : function(c, position, target){
17910         if(c && !c.rendered){
17911             c.render(target, position);
17912             this.configureItem(c, position);
17913         }else if(c && !this.isValidParent(c, target)){
17914             if(typeof position == 'number'){
17915                 position = target.dom.childNodes[position];
17916             }
17917             target.dom.insertBefore(c.getDomPositionEl().dom, position || null);
17918             c.container = target;
17919             this.configureItem(c, position);
17920         }
17921     },
17922     
17923     // private
17924     configureItem: function(c, position){
17925         if(this.extraCls){
17926             var t = c.getPositionEl ? c.getPositionEl() : c;
17927             t.addClass(this.extraCls);
17928         }
17929         if (this.renderHidden && c != this.activeItem) {
17930             c.hide();
17931         }
17932         if(c.doLayout){
17933             c.doLayout(false, this.forceLayout);
17934         }
17935     },
17936
17937     // private
17938     onResize: function(){
17939         if(this.container.collapsed){
17940             return;
17941         }
17942         var b = this.container.bufferResize;
17943         if(b){
17944             if(!this.resizeTask){
17945                 this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
17946                 this.resizeBuffer = typeof b == 'number' ? b : 100;
17947             }
17948             this.resizeTask.delay(this.resizeBuffer);
17949         }else{
17950             this.runLayout();
17951         }
17952     },
17953     
17954     // private
17955     runLayout: function(){
17956         this.layout();
17957         this.container.onLayout();
17958     },
17959
17960     // private
17961     setContainer : function(ct){
17962         if(this.monitorResize && ct != this.container){
17963             if(this.container){
17964                 this.container.un('resize', this.onResize, this);
17965                 this.container.un('bodyresize', this.onResize, this);
17966             }
17967             if(ct){
17968                 ct.on({
17969                     scope: this,
17970                     resize: this.onResize,
17971                     bodyresize: this.onResize
17972                 });
17973             }
17974         }
17975         this.container = ct;
17976     },
17977
17978     // private
17979     parseMargins : function(v){
17980         if(typeof v == 'number'){
17981             v = v.toString();
17982         }
17983         var ms = v.split(' ');
17984         var len = ms.length;
17985         if(len == 1){
17986             ms[1] = ms[0];
17987             ms[2] = ms[0];
17988             ms[3] = ms[0];
17989         }
17990         if(len == 2){
17991             ms[2] = ms[0];
17992             ms[3] = ms[1];
17993         }
17994         if(len == 3){
17995             ms[3] = ms[1];
17996         }
17997         return {
17998             top:parseInt(ms[0], 10) || 0,
17999             right:parseInt(ms[1], 10) || 0,
18000             bottom:parseInt(ms[2], 10) || 0,
18001             left:parseInt(ms[3], 10) || 0
18002         };
18003     },
18004
18005     /**
18006      * The {@link Template Ext.Template} used by Field rendering layout classes (such as
18007      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
18008      * labeled and styled form Field. A default Template is supplied, but this may be
18009      * overriden to create custom field structures. The template processes values returned from
18010      * {@link Ext.layout.FormLayout#getTemplateArgs}.
18011      * @property fieldTpl
18012      * @type Ext.Template
18013      */
18014     fieldTpl: (function() {
18015         var t = new Ext.Template(
18016             '<div class="x-form-item {itemCls}" tabIndex="-1">',
18017                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
18018                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
18019                 '</div><div class="{clearCls}"></div>',
18020             '</div>'
18021         );
18022         t.disableFormats = true;
18023         return t.compile();
18024     })(),
18025         
18026     /*
18027      * Destroys this layout. This is a template method that is empty by default, but should be implemented
18028      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
18029      * @protected
18030      */
18031     destroy : Ext.emptyFn
18032 };
18033 Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout;/**\r
18034  * @class Ext.layout.FitLayout\r
18035  * @extends Ext.layout.ContainerLayout\r
18036  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's\r
18037  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}\r
18038  * config, and should generally not need to be created directly via the new keyword.</p>\r
18039  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container\r
18040  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has\r
18041  * multiple panels, only the first one will be displayed.  Example usage:</p>\r
18042  * <pre><code>\r
18043 var p = new Ext.Panel({\r
18044     title: 'Fit Layout',\r
18045     layout:'fit',\r
18046     items: {\r
18047         title: 'Inner Panel',\r
18048         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',\r
18049         border: false\r
18050     }\r
18051 });\r
18052 </code></pre>\r
18053  */\r
18054 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18055     // private\r
18056     monitorResize:true,\r
18057 \r
18058     // private\r
18059     onLayout : function(ct, target){\r
18060         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);\r
18061         if(!this.container.collapsed){\r
18062             var sz = (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getViewSize() : target.getStyleSize();\r
18063             this.setItemSize(this.activeItem || ct.items.itemAt(0), sz);\r
18064         }\r
18065     },\r
18066 \r
18067     // private\r
18068     setItemSize : function(item, size){\r
18069         if(item && size.height > 0){ // display none?\r
18070             item.setSize(size);\r
18071         }\r
18072     }\r
18073 });\r
18074 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**\r
18075  * @class Ext.layout.CardLayout\r
18076  * @extends Ext.layout.FitLayout\r
18077  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be\r
18078  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.\r
18079  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,\r
18080  * and should generally not need to be created directly via the new keyword.</p>\r
18081  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,\r
18082  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of\r
18083  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,\r
18084  * so that functionality must be provided by the developer.</p>\r
18085  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added\r
18086  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a\r
18087  * common navigation routine -- for this example, the implementation of that routine has been ommitted since\r
18088  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a\r
18089  * completely different implementation.  For serious implementations, a better approach would be to extend\r
18090  * CardLayout to provide the custom functionality needed.  Example usage:</p>\r
18091  * <pre><code>\r
18092 var navHandler = function(direction){\r
18093     // This routine could contain business logic required to manage the navigation steps.\r
18094     // It would call setActiveItem as needed, manage navigation button state, handle any\r
18095     // branching logic that might be required, handle alternate actions like cancellation\r
18096     // or finalization, etc.  A complete wizard implementation could get pretty\r
18097     // sophisticated depending on the complexity required, and should probably be\r
18098     // done as a subclass of CardLayout in a real-world implementation.\r
18099 };\r
18100 \r
18101 var card = new Ext.Panel({\r
18102     title: 'Example Wizard',\r
18103     layout:'card',\r
18104     activeItem: 0, // make sure the active item is set on the container config!\r
18105     bodyStyle: 'padding:15px',\r
18106     defaults: {\r
18107         // applied to each contained panel\r
18108         border:false\r
18109     },\r
18110     // just an example of one possible navigation scheme, using buttons\r
18111     bbar: [\r
18112         {\r
18113             id: 'move-prev',\r
18114             text: 'Back',\r
18115             handler: navHandler.createDelegate(this, [-1]),\r
18116             disabled: true\r
18117         },\r
18118         '->', // greedy spacer so that the buttons are aligned to each side\r
18119         {\r
18120             id: 'move-next',\r
18121             text: 'Next',\r
18122             handler: navHandler.createDelegate(this, [1])\r
18123         }\r
18124     ],\r
18125     // the panels (or "cards") within the layout\r
18126     items: [{\r
18127         id: 'card-0',\r
18128         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'\r
18129     },{\r
18130         id: 'card-1',\r
18131         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'\r
18132     },{\r
18133         id: 'card-2',\r
18134         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'\r
18135     }]\r
18136 });\r
18137 </code></pre>\r
18138  */\r
18139 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {\r
18140     /**\r
18141      * @cfg {Boolean} deferredRender\r
18142      * True to render each contained item at the time it becomes active, false to render all contained items\r
18143      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or\r
18144      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to\r
18145      * true might improve performance.\r
18146      */\r
18147     deferredRender : false,\r
18148     \r
18149     /**\r
18150      * @cfg {Boolean} layoutOnCardChange\r
18151      * True to force a layout of the active item when the active card is changed. Defaults to false.\r
18152      */\r
18153     layoutOnCardChange : false,\r
18154 \r
18155     /**\r
18156      * @cfg {Boolean} renderHidden @hide\r
18157      */\r
18158     // private\r
18159     renderHidden : true,\r
18160     \r
18161     constructor: function(config){\r
18162         Ext.layout.CardLayout.superclass.constructor.call(this, config);\r
18163         this.forceLayout = (this.deferredRender === false);\r
18164     },\r
18165 \r
18166     /**\r
18167      * Sets the active (visible) item in the layout.\r
18168      * @param {String/Number} item The string component id or numeric index of the item to activate\r
18169      */\r
18170     setActiveItem : function(item){\r
18171         item = this.container.getComponent(item);\r
18172         if(this.activeItem != item){\r
18173             if(this.activeItem){\r
18174                 this.activeItem.hide();\r
18175             }\r
18176             this.activeItem = item;\r
18177             item.show();\r
18178             this.container.doLayout();\r
18179             if(this.layoutOnCardChange && item.doLayout){\r
18180                 item.doLayout();\r
18181             }\r
18182         }\r
18183     },\r
18184 \r
18185     // private\r
18186     renderAll : function(ct, target){\r
18187         if(this.deferredRender){\r
18188             this.renderItem(this.activeItem, undefined, target);\r
18189         }else{\r
18190             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);\r
18191         }\r
18192     }\r
18193 });\r
18194 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/**\r
18195  * @class Ext.layout.AnchorLayout\r
18196  * @extends Ext.layout.ContainerLayout\r
18197  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.\r
18198  * If the container is resized, all anchored items are automatically rerendered according to their\r
18199  * <b><tt>{@link #anchor}</tt></b> rules.</p>\r
18200  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}\r
18201  * config, and should generally not need to be created directly via the new keyword.</p>\r
18202  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,\r
18203  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the\r
18204  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.\r
18205  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating\r
18206  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring\r
18207  * logic if necessary.  For example:</p>\r
18208  * <pre><code>\r
18209 var viewport = new Ext.Viewport({\r
18210     layout:'anchor',\r
18211     anchorSize: {width:800, height:600},\r
18212     items:[{\r
18213         title:'Item 1',\r
18214         html:'Content 1',\r
18215         width:800,\r
18216         anchor:'right 20%'\r
18217     },{\r
18218         title:'Item 2',\r
18219         html:'Content 2',\r
18220         width:300,\r
18221         anchor:'50% 30%'\r
18222     },{\r
18223         title:'Item 3',\r
18224         html:'Content 3',\r
18225         width:600,\r
18226         anchor:'-100 50%'\r
18227     }]\r
18228 });\r
18229  * </code></pre>\r
18230  */\r
18231 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18232     /**\r
18233      * @cfg {String} anchor\r
18234      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by\r
18235      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>\r
18236      * \r
18237      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>\r
18238      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string\r
18239      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').\r
18240      * The following types of anchor values are supported:<div class="mdetail-params"><ul>\r
18241      * \r
18242      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">\r
18243      * The first anchor is the percentage width that the item should take up within the container, and the\r
18244      * second is the percentage height.  For example:<pre><code>\r
18245 // two values specified\r
18246 anchor: '100% 50%' // render item complete width of the container and\r
18247                    // 1/2 height of the container\r
18248 // one value specified\r
18249 anchor: '100%'     // the width value; the height will default to auto\r
18250      * </code></pre></div></li>\r
18251      * \r
18252      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">\r
18253      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,\r
18254      * and the second is the offset from the bottom edge. For example:<pre><code>\r
18255 // two values specified\r
18256 anchor: '-50 -100' // render item the complete width of the container\r
18257                    // minus 50 pixels and\r
18258                    // the complete height minus 100 pixels.\r
18259 // one value specified\r
18260 anchor: '-50'      // anchor value is assumed to be the right offset value\r
18261                    // bottom offset will default to 0\r
18262      * </code></pre></div></li>\r
18263      * \r
18264      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>\r
18265      * (or <tt>'b'</tt>).<div class="sub-desc">\r
18266      * Either the container must have a fixed size or an anchorSize config value defined at render time in\r
18267      * order for these to have any effect.</div></li>\r
18268      *\r
18269      * <li><b>Mixed</b> : <div class="sub-desc">\r
18270      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container\r
18271      * right edge by 50 pixels and 75% of the container's height use:\r
18272      * <pre><code>\r
18273 anchor: '-50 75%' \r
18274      * </code></pre></div></li>\r
18275      * \r
18276      * \r
18277      * </ul></div>\r
18278      */\r
18279     \r
18280     // private\r
18281     monitorResize:true,\r
18282 \r
18283     // private\r
18284     getAnchorViewSize : function(ct, target){\r
18285         return target.dom == document.body ?\r
18286                    target.getViewSize() : target.getStyleSize();\r
18287     },\r
18288 \r
18289     // private\r
18290     onLayout : function(ct, target){\r
18291         Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target);\r
18292 \r
18293         var size = this.getAnchorViewSize(ct, target);\r
18294 \r
18295         var w = size.width, h = size.height;\r
18296 \r
18297         if(w < 20 && h < 20){\r
18298             return;\r
18299         }\r
18300 \r
18301         // find the container anchoring size\r
18302         var aw, ah;\r
18303         if(ct.anchorSize){\r
18304             if(typeof ct.anchorSize == 'number'){\r
18305                 aw = ct.anchorSize;\r
18306             }else{\r
18307                 aw = ct.anchorSize.width;\r
18308                 ah = ct.anchorSize.height;\r
18309             }\r
18310         }else{\r
18311             aw = ct.initialConfig.width;\r
18312             ah = ct.initialConfig.height;\r
18313         }\r
18314 \r
18315         var cs = ct.items.items, len = cs.length, i, c, a, cw, ch;\r
18316         for(i = 0; i < len; i++){\r
18317             c = cs[i];\r
18318             if(c.anchor){\r
18319                 a = c.anchorSpec;\r
18320                 if(!a){ // cache all anchor values\r
18321                     var vs = c.anchor.split(' ');\r
18322                     c.anchorSpec = a = {\r
18323                         right: this.parseAnchor(vs[0], c.initialConfig.width, aw),\r
18324                         bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah)\r
18325                     };\r
18326                 }\r
18327                 cw = a.right ? this.adjustWidthAnchor(a.right(w), c) : undefined;\r
18328                 ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h), c) : undefined;\r
18329 \r
18330                 if(cw || ch){\r
18331                     c.setSize(cw || undefined, ch || undefined);\r
18332                 }\r
18333             }\r
18334         }\r
18335     },\r
18336 \r
18337     // private\r
18338     parseAnchor : function(a, start, cstart){\r
18339         if(a && a != 'none'){\r
18340             var last;\r
18341             if(/^(r|right|b|bottom)$/i.test(a)){   // standard anchor\r
18342                 var diff = cstart - start;\r
18343                 return function(v){\r
18344                     if(v !== last){\r
18345                         last = v;\r
18346                         return v - diff;\r
18347                     }\r
18348                 }\r
18349             }else if(a.indexOf('%') != -1){\r
18350                 var ratio = parseFloat(a.replace('%', ''))*.01;   // percentage\r
18351                 return function(v){\r
18352                     if(v !== last){\r
18353                         last = v;\r
18354                         return Math.floor(v*ratio);\r
18355                     }\r
18356                 }\r
18357             }else{\r
18358                 a = parseInt(a, 10);\r
18359                 if(!isNaN(a)){                            // simple offset adjustment\r
18360                     return function(v){\r
18361                         if(v !== last){\r
18362                             last = v;\r
18363                             return v + a;\r
18364                         }\r
18365                     }\r
18366                 }\r
18367             }\r
18368         }\r
18369         return false;\r
18370     },\r
18371 \r
18372     // private\r
18373     adjustWidthAnchor : function(value, comp){\r
18374         return value;\r
18375     },\r
18376 \r
18377     // private\r
18378     adjustHeightAnchor : function(value, comp){\r
18379         return value;\r
18380     }\r
18381     \r
18382     /**\r
18383      * @property activeItem\r
18384      * @hide\r
18385      */\r
18386 });\r
18387 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;/**\r
18388  * @class Ext.layout.ColumnLayout\r
18389  * @extends Ext.layout.ContainerLayout\r
18390  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of\r
18391  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.\r
18392  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,\r
18393  * and should generally not need to be created directly via the new keyword.</p>\r
18394  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a\r
18395  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The\r
18396  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.\r
18397  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>\r
18398  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.\r
18399  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and\r
18400  * less than 1 (e.g., .25).</p>\r
18401  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the\r
18402  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none\r
18403  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second\r
18404  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on\r
18405  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space\r
18406  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns\r
18407  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your\r
18408  * layout may not render as expected.  Example usage:</p>\r
18409  * <pre><code>\r
18410 // All columns are percentages -- they must add up to 1\r
18411 var p = new Ext.Panel({\r
18412     title: 'Column Layout - Percentage Only',\r
18413     layout:'column',\r
18414     items: [{\r
18415         title: 'Column 1',\r
18416         columnWidth: .25 \r
18417     },{\r
18418         title: 'Column 2',\r
18419         columnWidth: .6\r
18420     },{\r
18421         title: 'Column 3',\r
18422         columnWidth: .15\r
18423     }]\r
18424 });\r
18425 \r
18426 // Mix of width and columnWidth -- all columnWidth values must add up\r
18427 // to 1. The first column will take up exactly 120px, and the last two\r
18428 // columns will fill the remaining container width.\r
18429 var p = new Ext.Panel({\r
18430     title: 'Column Layout - Mixed',\r
18431     layout:'column',\r
18432     items: [{\r
18433         title: 'Column 1',\r
18434         width: 120\r
18435     },{\r
18436         title: 'Column 2',\r
18437         columnWidth: .8\r
18438     },{\r
18439         title: 'Column 3',\r
18440         columnWidth: .2\r
18441     }]\r
18442 });\r
18443 </code></pre>\r
18444  */\r
18445 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18446     // private\r
18447     monitorResize:true,\r
18448     \r
18449     extraCls: 'x-column',\r
18450 \r
18451     scrollOffset : 0,\r
18452 \r
18453     // private\r
18454     isValidParent : function(c, target){\r
18455         return (c.getPositionEl ? c.getPositionEl() : c.getEl()).dom.parentNode == this.innerCt.dom;\r
18456     },\r
18457 \r
18458     // private\r
18459     onLayout : function(ct, target){\r
18460         var cs = ct.items.items, len = cs.length, c, i;\r
18461 \r
18462         if(!this.innerCt){\r
18463             target.addClass('x-column-layout-ct');\r
18464 \r
18465             // the innerCt prevents wrapping and shuffling while\r
18466             // the container is resizing\r
18467             this.innerCt = target.createChild({cls:'x-column-inner'});\r
18468             this.innerCt.createChild({cls:'x-clear'});\r
18469         }\r
18470         this.renderAll(ct, this.innerCt);\r
18471 \r
18472         var size = Ext.isIE && target.dom != Ext.getBody().dom ? target.getStyleSize() : target.getViewSize();\r
18473 \r
18474         if(size.width < 1 && size.height < 1){ // display none?\r
18475             return;\r
18476         }\r
18477 \r
18478         var w = size.width - target.getPadding('lr') - this.scrollOffset,\r
18479             h = size.height - target.getPadding('tb'),\r
18480             pw = w;\r
18481 \r
18482         this.innerCt.setWidth(w);\r
18483         \r
18484         // some columns can be percentages while others are fixed\r
18485         // so we need to make 2 passes\r
18486 \r
18487         for(i = 0; i < len; i++){\r
18488             c = cs[i];\r
18489             if(!c.columnWidth){\r
18490                 pw -= (c.getSize().width + c.getEl().getMargins('lr'));\r
18491             }\r
18492         }\r
18493 \r
18494         pw = pw < 0 ? 0 : pw;\r
18495 \r
18496         for(i = 0; i < len; i++){\r
18497             c = cs[i];\r
18498             if(c.columnWidth){\r
18499                 c.setSize(Math.floor(c.columnWidth*pw) - c.getEl().getMargins('lr'));\r
18500             }\r
18501         }\r
18502     }\r
18503     \r
18504     /**\r
18505      * @property activeItem\r
18506      * @hide\r
18507      */\r
18508 });\r
18509 \r
18510 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;/**
18511  * @class Ext.layout.BorderLayout
18512  * @extends Ext.layout.ContainerLayout
18513  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
18514  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
18515  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
18516  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
18517  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
18518  * {@link Ext.Container#layout} config, and should generally not need to be created directly
18519  * via the new keyword.</p>
18520  * <p>BorderLayout does not have any direct config options (other than inherited ones).
18521  * All configuration options available for customizing the BorderLayout are at the
18522  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
18523  * levels.</p>
18524  * <p>Example usage:</p>
18525  * <pre><code>
18526 var myBorderPanel = new Ext.Panel({
18527     {@link Ext.Component#renderTo renderTo}: document.body,
18528     {@link Ext.BoxComponent#width width}: 700,
18529     {@link Ext.BoxComponent#height height}: 500,
18530     {@link Ext.Panel#title title}: 'Border Layout',
18531     {@link Ext.Container#layout layout}: 'border',
18532     {@link Ext.Container#items items}: [{
18533         {@link Ext.Panel#title title}: 'South Region is resizable',
18534         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
18535         {@link Ext.BoxComponent#height height}: 100,
18536         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
18537         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} 
18538         {@link Ext.SplitBar#maxSize maxSize}: 150,
18539         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
18540     },{
18541         // xtype: 'panel' implied by default
18542         {@link Ext.Panel#title title}: 'West Region is collapsible',
18543         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
18544         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
18545         {@link Ext.BoxComponent#width width}: 200,
18546         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
18547         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
18548         {@link Ext.Component#id id}: 'west-region-container',
18549         {@link Ext.Container#layout layout}: 'fit',
18550         {@link Ext.Panel#unstyled unstyled}: true
18551     },{
18552         {@link Ext.Panel#title title}: 'Center Region',
18553         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
18554         {@link Ext.Component#xtype xtype}: 'container',
18555         {@link Ext.Container#layout layout}: 'fit',
18556         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
18557     }]
18558 });
18559 </code></pre>
18560  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
18561  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
18562  * The child item in the center region will always be resized to fill the remaining space not used by
18563  * the other regions in the layout.</li>
18564  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
18565  * (an integer representing the number of pixels that the region should take up).</li>
18566  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
18567  * <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
18568  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
18569  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
18570  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
18571  * is added to the west region:
18572  * <div style="margin-left:16px"><pre><code>
18573 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
18574 wrc.{@link Ext.Panel#removeAll removeAll}();
18575 wrc.{@link Ext.Container#add add}({
18576     title: 'Added Panel',
18577     html: 'Some content'
18578 });
18579 wrc.{@link Ext.Container#doLayout doLayout}();
18580  * </code></pre></div>
18581  * </li>
18582  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
18583  * <div style="margin-left:16px"><pre><code>
18584 wr = myBorderPanel.layout.west;
18585  * </code></pre></div>
18586  * </li>
18587  * </ul></div>
18588  */
18589 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
18590     // private
18591     monitorResize:true,
18592     // private
18593     rendered : false,
18594
18595     // private
18596     onLayout : function(ct, target){
18597         var collapsed;
18598         if(!this.rendered){
18599             target.addClass('x-border-layout-ct');
18600             var items = ct.items.items;
18601             collapsed = [];
18602             for(var i = 0, len = items.length; i < len; i++) {
18603                 var c = items[i];
18604                 var pos = c.region;
18605                 if(c.collapsed){
18606                     collapsed.push(c);
18607                 }
18608                 c.collapsed = false;
18609                 if(!c.rendered){
18610                     c.cls = c.cls ? c.cls +' x-border-panel' : 'x-border-panel';
18611                     c.render(target, i);
18612                 }
18613                 this[pos] = pos != 'center' && c.split ?
18614                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
18615                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
18616                 this[pos].render(target, c);
18617             }
18618             this.rendered = true;
18619         }
18620
18621         var size = target.getViewSize();
18622         if(size.width < 20 || size.height < 20){ // display none?
18623             if(collapsed){
18624                 this.restoreCollapsed = collapsed;
18625             }
18626             return;
18627         }else if(this.restoreCollapsed){
18628             collapsed = this.restoreCollapsed;
18629             delete this.restoreCollapsed;
18630         }
18631
18632         var w = size.width, h = size.height;
18633         var centerW = w, centerH = h, centerY = 0, centerX = 0;
18634
18635         var n = this.north, s = this.south, west = this.west, e = this.east, c = this.center;
18636         if(!c && Ext.layout.BorderLayout.WARN !== false){
18637             throw 'No center region defined in BorderLayout ' + ct.id;
18638         }
18639
18640         if(n && n.isVisible()){
18641             var b = n.getSize();
18642             var m = n.getMargins();
18643             b.width = w - (m.left+m.right);
18644             b.x = m.left;
18645             b.y = m.top;
18646             centerY = b.height + b.y + m.bottom;
18647             centerH -= centerY;
18648             n.applyLayout(b);
18649         }
18650         if(s && s.isVisible()){
18651             var b = s.getSize();
18652             var m = s.getMargins();
18653             b.width = w - (m.left+m.right);
18654             b.x = m.left;
18655             var totalHeight = (b.height + m.top + m.bottom);
18656             b.y = h - totalHeight + m.top;
18657             centerH -= totalHeight;
18658             s.applyLayout(b);
18659         }
18660         if(west && west.isVisible()){
18661             var b = west.getSize();
18662             var m = west.getMargins();
18663             b.height = centerH - (m.top+m.bottom);
18664             b.x = m.left;
18665             b.y = centerY + m.top;
18666             var totalWidth = (b.width + m.left + m.right);
18667             centerX += totalWidth;
18668             centerW -= totalWidth;
18669             west.applyLayout(b);
18670         }
18671         if(e && e.isVisible()){
18672             var b = e.getSize();
18673             var m = e.getMargins();
18674             b.height = centerH - (m.top+m.bottom);
18675             var totalWidth = (b.width + m.left + m.right);
18676             b.x = w - totalWidth + m.left;
18677             b.y = centerY + m.top;
18678             centerW -= totalWidth;
18679             e.applyLayout(b);
18680         }
18681         if(c){
18682             var m = c.getMargins();
18683             var centerBox = {
18684                 x: centerX + m.left,
18685                 y: centerY + m.top,
18686                 width: centerW - (m.left+m.right),
18687                 height: centerH - (m.top+m.bottom)
18688             };
18689             c.applyLayout(centerBox);
18690         }
18691         if(collapsed){
18692             for(var i = 0, len = collapsed.length; i < len; i++){
18693                 collapsed[i].collapse(false);
18694             }
18695         }
18696         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
18697             target.repaint();
18698         }
18699     },
18700
18701     destroy: function() {
18702         var r = ['north', 'south', 'east', 'west'];
18703         for (var i = 0; i < r.length; i++) {
18704             var region = this[r[i]];
18705             if(region){
18706                 if(region.destroy){
18707                     region.destroy();
18708                 }else if (region.split){
18709                     region.split.destroy(true);
18710                 }
18711             }
18712         }
18713         Ext.layout.BorderLayout.superclass.destroy.call(this);
18714     }
18715
18716     /**
18717      * @property activeItem
18718      * @hide
18719      */
18720 });
18721
18722 /**
18723  * @class Ext.layout.BorderLayout.Region
18724  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
18725  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
18726  * independent of other regions and the containing BorderLayout, and can be any of the
18727  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
18728  * <p>Region size is managed automatically and cannot be changed by the user -- for
18729  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
18730  * @constructor
18731  * Create a new Region.
18732  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
18733  * @param {Object} config The configuration options
18734  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
18735  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
18736  * <b>must have a center region</b> for the primary content -- all other regions are optional.
18737  */
18738 Ext.layout.BorderLayout.Region = function(layout, config, pos){
18739     Ext.apply(this, config);
18740     this.layout = layout;
18741     this.position = pos;
18742     this.state = {};
18743     if(typeof this.margins == 'string'){
18744         this.margins = this.layout.parseMargins(this.margins);
18745     }
18746     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
18747     if(this.collapsible){
18748         if(typeof this.cmargins == 'string'){
18749             this.cmargins = this.layout.parseMargins(this.cmargins);
18750         }
18751         if(this.collapseMode == 'mini' && !this.cmargins){
18752             this.cmargins = {left:0,top:0,right:0,bottom:0};
18753         }else{
18754             this.cmargins = Ext.applyIf(this.cmargins || {},
18755                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
18756         }
18757     }
18758 };
18759
18760 Ext.layout.BorderLayout.Region.prototype = {
18761     /**
18762      * @cfg {Boolean} animFloat
18763      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
18764      * panel that will close again once the user mouses out of that panel (or clicks out if
18765      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
18766      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
18767      */
18768     /**
18769      * @cfg {Boolean} autoHide
18770      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
18771      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
18772      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
18773      * user clicks outside of the panel (defaults to <tt>true</tt>).
18774      */
18775     /**
18776      * @cfg {String} collapseMode
18777      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
18778      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
18779      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
18780      * title bar.</div></li>
18781      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
18782      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
18783      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
18784      * </div></li>
18785      * </ul></div></p>
18786      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
18787      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
18788      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
18789      * <p>See also <tt>{@link #cmargins}</tt>.</p>
18790      */
18791     /**
18792      * @cfg {Object} margins
18793      * An object containing margins to apply to the region when in the expanded state in the
18794      * format:<pre><code>
18795 {
18796     top: (top margin),
18797     right: (right margin),
18798     bottom: (bottom margin),
18799     left: (left margin)
18800 }</code></pre>
18801      * <p>May also be a string containing space-separated, numeric margin values. The order of the
18802      * sides associated with each value matches the way CSS processes margin values:</p>
18803      * <p><div class="mdetail-params"><ul>
18804      * <li>If there is only one value, it applies to all sides.</li>
18805      * <li>If there are two values, the top and bottom borders are set to the first value and the
18806      * right and left are set to the second.</li>
18807      * <li>If there are three values, the top is set to the first value, the left and right are set
18808      * to the second, and the bottom is set to the third.</li>
18809      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
18810      * </ul></div></p>
18811      * <p>Defaults to:</p><pre><code>
18812      * {top:0, right:0, bottom:0, left:0}
18813      * </code></pre>
18814      */
18815     /**
18816      * @cfg {Object} cmargins
18817      * An object containing margins to apply to the region when in the collapsed state in the
18818      * format:<pre><code>
18819 {
18820     top: (top margin),
18821     right: (right margin),
18822     bottom: (bottom margin),
18823     left: (left margin)
18824 }</code></pre>
18825      * <p>May also be a string containing space-separated, numeric margin values. The order of the
18826      * sides associated with each value matches the way CSS processes margin values.</p>
18827      * <p><ul>
18828      * <li>If there is only one value, it applies to all sides.</li>
18829      * <li>If there are two values, the top and bottom borders are set to the first value and the
18830      * right and left are set to the second.</li>
18831      * <li>If there are three values, the top is set to the first value, the left and right are set
18832      * to the second, and the bottom is set to the third.</li>
18833      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
18834      * </ul></p>
18835      */
18836     /**
18837      * @cfg {Boolean} collapsible
18838      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
18839      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
18840      * bar of the region, otherwise the button will not be shown.</p>
18841      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
18842      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
18843      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
18844      */
18845     collapsible : false,
18846     /**
18847      * @cfg {Boolean} split
18848      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and 
18849      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
18850      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
18851      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
18852      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
18853      * <li>this configuration option is ignored if <tt>region='center'</tt></li> 
18854      * <li>when <tt>split == true</tt>, it is common to specify a
18855      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
18856      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
18857      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
18858      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
18859      * for the collapse tool</tt></li> 
18860      * </ul></div> 
18861      */
18862     split:false,
18863     /**
18864      * @cfg {Boolean} floatable
18865      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
18866      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
18867      * clicking the expand button to see it again (defaults to <tt>true</tt>).
18868      */
18869     floatable: true,
18870     /**
18871      * @cfg {Number} minWidth
18872      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
18873      * <tt>maxWidth</tt> may also be specified.</p><br>
18874      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> / 
18875      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified 
18876      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
18877      */
18878     minWidth:50,
18879     /**
18880      * @cfg {Number} minHeight
18881      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
18882      * <tt>maxHeight</tt> may also be specified.</p><br>
18883      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> / 
18884      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified 
18885      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
18886      */
18887     minHeight:50,
18888
18889     // private
18890     defaultMargins : {left:0,top:0,right:0,bottom:0},
18891     // private
18892     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
18893     // private
18894     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
18895     floatingZIndex: 100,
18896
18897     /**
18898      * True if this region is collapsed. Read-only.
18899      * @type Boolean
18900      * @property
18901      */
18902     isCollapsed : false,
18903
18904     /**
18905      * This region's panel.  Read-only.
18906      * @type Ext.Panel
18907      * @property panel
18908      */
18909     /**
18910      * This region's layout.  Read-only.
18911      * @type Layout
18912      * @property layout
18913      */
18914     /**
18915      * This region's layout position (north, south, east, west or center).  Read-only.
18916      * @type String
18917      * @property position
18918      */
18919
18920     // private
18921     render : function(ct, p){
18922         this.panel = p;
18923         p.el.enableDisplayMode();
18924         this.targetEl = ct;
18925         this.el = p.el;
18926
18927         var gs = p.getState, ps = this.position;
18928         p.getState = function(){
18929             return Ext.apply(gs.call(p) || {}, this.state);
18930         }.createDelegate(this);
18931
18932         if(ps != 'center'){
18933             p.allowQueuedExpand = false;
18934             p.on({
18935                 beforecollapse: this.beforeCollapse,
18936                 collapse: this.onCollapse,
18937                 beforeexpand: this.beforeExpand,
18938                 expand: this.onExpand,
18939                 hide: this.onHide,
18940                 show: this.onShow,
18941                 scope: this
18942             });
18943             if(this.collapsible || this.floatable){
18944                 p.collapseEl = 'el';
18945                 p.slideAnchor = this.getSlideAnchor();
18946             }
18947             if(p.tools && p.tools.toggle){
18948                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
18949                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
18950             }
18951         }
18952     },
18953
18954     // private
18955     getCollapsedEl : function(){
18956         if(!this.collapsedEl){
18957             if(!this.toolTemplate){
18958                 var tt = new Ext.Template(
18959                      '<div class="x-tool x-tool-{id}">&#160;</div>'
18960                 );
18961                 tt.disableFormats = true;
18962                 tt.compile();
18963                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
18964             }
18965             this.collapsedEl = this.targetEl.createChild({
18966                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
18967                 id: this.panel.id + '-xcollapsed'
18968             });
18969             this.collapsedEl.enableDisplayMode('block');
18970
18971             if(this.collapseMode == 'mini'){
18972                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
18973                 this.miniCollapsedEl = this.collapsedEl.createChild({
18974                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
18975                 });
18976                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
18977                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
18978                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
18979             }else {
18980                 if(this.collapsible !== false && !this.hideCollapseTool) {
18981                     var t = this.toolTemplate.append(
18982                             this.collapsedEl.dom,
18983                             {id:'expand-'+this.position}, true);
18984                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
18985                     t.on('click', this.onExpandClick, this, {stopEvent:true});
18986                 }
18987                 if(this.floatable !== false || this.titleCollapse){
18988                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
18989                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
18990                 }
18991             }
18992         }
18993         return this.collapsedEl;
18994     },
18995
18996     // private
18997     onExpandClick : function(e){
18998         if(this.isSlid){
18999             this.afterSlideIn();
19000             this.panel.expand(false);
19001         }else{
19002             this.panel.expand();
19003         }
19004     },
19005
19006     // private
19007     onCollapseClick : function(e){
19008         this.panel.collapse();
19009     },
19010
19011     // private
19012     beforeCollapse : function(p, animate){
19013         this.lastAnim = animate;
19014         if(this.splitEl){
19015             this.splitEl.hide();
19016         }
19017         this.getCollapsedEl().show();
19018         this.panel.el.setStyle('z-index', 100);
19019         this.isCollapsed = true;
19020         this.layout.layout();
19021     },
19022
19023     // private
19024     onCollapse : function(animate){
19025         this.panel.el.setStyle('z-index', 1);
19026         if(this.lastAnim === false || this.panel.animCollapse === false){
19027             this.getCollapsedEl().dom.style.visibility = 'visible';
19028         }else{
19029             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
19030         }
19031         this.state.collapsed = true;
19032         this.panel.saveState();
19033     },
19034
19035     // private
19036     beforeExpand : function(animate){
19037         var c = this.getCollapsedEl();
19038         this.el.show();
19039         if(this.position == 'east' || this.position == 'west'){
19040             this.panel.setSize(undefined, c.getHeight());
19041         }else{
19042             this.panel.setSize(c.getWidth(), undefined);
19043         }
19044         c.hide();
19045         c.dom.style.visibility = 'hidden';
19046         this.panel.el.setStyle('z-index', this.floatingZIndex);
19047     },
19048
19049     // private
19050     onExpand : function(){
19051         this.isCollapsed = false;
19052         if(this.splitEl){
19053             this.splitEl.show();
19054         }
19055         this.layout.layout();
19056         this.panel.el.setStyle('z-index', 1);
19057         this.state.collapsed = false;
19058         this.panel.saveState();
19059     },
19060
19061     // private
19062     collapseClick : function(e){
19063         if(this.isSlid){
19064            e.stopPropagation();
19065            this.slideIn();
19066         }else{
19067            e.stopPropagation();
19068            this.slideOut();
19069         }
19070     },
19071
19072     // private
19073     onHide : function(){
19074         if(this.isCollapsed){
19075             this.getCollapsedEl().hide();
19076         }else if(this.splitEl){
19077             this.splitEl.hide();
19078         }
19079     },
19080
19081     // private
19082     onShow : function(){
19083         if(this.isCollapsed){
19084             this.getCollapsedEl().show();
19085         }else if(this.splitEl){
19086             this.splitEl.show();
19087         }
19088     },
19089
19090     /**
19091      * True if this region is currently visible, else false.
19092      * @return {Boolean}
19093      */
19094     isVisible : function(){
19095         return !this.panel.hidden;
19096     },
19097
19098     /**
19099      * Returns the current margins for this region.  If the region is collapsed, the
19100      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
19101      * {@link #margins} value will be returned.
19102      * @return {Object} An object containing the element's margins: <tt>{left: (left
19103      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
19104      */
19105     getMargins : function(){
19106         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
19107     },
19108
19109     /**
19110      * Returns the current size of this region.  If the region is collapsed, the size of the
19111      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
19112      * @return {Object} An object containing the element's size: <tt>{width: (element width),
19113      * height: (element height)}</tt>
19114      */
19115     getSize : function(){
19116         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
19117     },
19118
19119     /**
19120      * Sets the specified panel as the container element for this region.
19121      * @param {Ext.Panel} panel The new panel
19122      */
19123     setPanel : function(panel){
19124         this.panel = panel;
19125     },
19126
19127     /**
19128      * Returns the minimum allowable width for this region.
19129      * @return {Number} The minimum width
19130      */
19131     getMinWidth: function(){
19132         return this.minWidth;
19133     },
19134
19135     /**
19136      * Returns the minimum allowable height for this region.
19137      * @return {Number} The minimum height
19138      */
19139     getMinHeight: function(){
19140         return this.minHeight;
19141     },
19142
19143     // private
19144     applyLayoutCollapsed : function(box){
19145         var ce = this.getCollapsedEl();
19146         ce.setLeftTop(box.x, box.y);
19147         ce.setSize(box.width, box.height);
19148     },
19149
19150     // private
19151     applyLayout : function(box){
19152         if(this.isCollapsed){
19153             this.applyLayoutCollapsed(box);
19154         }else{
19155             this.panel.setPosition(box.x, box.y);
19156             this.panel.setSize(box.width, box.height);
19157         }
19158     },
19159
19160     // private
19161     beforeSlide: function(){
19162         this.panel.beforeEffect();
19163     },
19164
19165     // private
19166     afterSlide : function(){
19167         this.panel.afterEffect();
19168     },
19169
19170     // private
19171     initAutoHide : function(){
19172         if(this.autoHide !== false){
19173             if(!this.autoHideHd){
19174                 var st = new Ext.util.DelayedTask(this.slideIn, this);
19175                 this.autoHideHd = {
19176                     "mouseout": function(e){
19177                         if(!e.within(this.el, true)){
19178                             st.delay(500);
19179                         }
19180                     },
19181                     "mouseover" : function(e){
19182                         st.cancel();
19183                     },
19184                     scope : this
19185                 };
19186             }
19187             this.el.on(this.autoHideHd);
19188         }
19189     },
19190
19191     // private
19192     clearAutoHide : function(){
19193         if(this.autoHide !== false){
19194             this.el.un("mouseout", this.autoHideHd.mouseout);
19195             this.el.un("mouseover", this.autoHideHd.mouseover);
19196         }
19197     },
19198
19199     // private
19200     clearMonitor : function(){
19201         Ext.getDoc().un("click", this.slideInIf, this);
19202     },
19203
19204     /**
19205      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
19206      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
19207      * are clicked, or the mouse exits the Region.
19208      */
19209     slideOut : function(){
19210         if(this.isSlid || this.el.hasActiveFx()){
19211             return;
19212         }
19213         this.isSlid = true;
19214         var ts = this.panel.tools;
19215         if(ts && ts.toggle){
19216             ts.toggle.hide();
19217         }
19218         this.el.show();
19219         if(this.position == 'east' || this.position == 'west'){
19220             this.panel.setSize(undefined, this.collapsedEl.getHeight());
19221         }else{
19222             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
19223         }
19224         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
19225         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
19226         this.el.setStyle("z-index", this.floatingZIndex+2);
19227         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
19228         if(this.animFloat !== false){
19229             this.beforeSlide();
19230             this.el.slideIn(this.getSlideAnchor(), {
19231                 callback: function(){
19232                     this.afterSlide();
19233                     this.initAutoHide();
19234                     Ext.getDoc().on("click", this.slideInIf, this);
19235                 },
19236                 scope: this,
19237                 block: true
19238             });
19239         }else{
19240             this.initAutoHide();
19241              Ext.getDoc().on("click", this.slideInIf, this);
19242         }
19243     },
19244
19245     // private
19246     afterSlideIn : function(){
19247         this.clearAutoHide();
19248         this.isSlid = false;
19249         this.clearMonitor();
19250         this.el.setStyle("z-index", "");
19251         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
19252         this.el.dom.style.left = this.restoreLT[0];
19253         this.el.dom.style.top = this.restoreLT[1];
19254
19255         var ts = this.panel.tools;
19256         if(ts && ts.toggle){
19257             ts.toggle.show();
19258         }
19259     },
19260
19261     /**
19262      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
19263      * this region back into its collapsed state.
19264      */
19265     slideIn : function(cb){
19266         if(!this.isSlid || this.el.hasActiveFx()){
19267             Ext.callback(cb);
19268             return;
19269         }
19270         this.isSlid = false;
19271         if(this.animFloat !== false){
19272             this.beforeSlide();
19273             this.el.slideOut(this.getSlideAnchor(), {
19274                 callback: function(){
19275                     this.el.hide();
19276                     this.afterSlide();
19277                     this.afterSlideIn();
19278                     Ext.callback(cb);
19279                 },
19280                 scope: this,
19281                 block: true
19282             });
19283         }else{
19284             this.el.hide();
19285             this.afterSlideIn();
19286         }
19287     },
19288
19289     // private
19290     slideInIf : function(e){
19291         if(!e.within(this.el)){
19292             this.slideIn();
19293         }
19294     },
19295
19296     // private
19297     anchors : {
19298         "west" : "left",
19299         "east" : "right",
19300         "north" : "top",
19301         "south" : "bottom"
19302     },
19303
19304     // private
19305     sanchors : {
19306         "west" : "l",
19307         "east" : "r",
19308         "north" : "t",
19309         "south" : "b"
19310     },
19311
19312     // private
19313     canchors : {
19314         "west" : "tl-tr",
19315         "east" : "tr-tl",
19316         "north" : "tl-bl",
19317         "south" : "bl-tl"
19318     },
19319
19320     // private
19321     getAnchor : function(){
19322         return this.anchors[this.position];
19323     },
19324
19325     // private
19326     getCollapseAnchor : function(){
19327         return this.canchors[this.position];
19328     },
19329
19330     // private
19331     getSlideAnchor : function(){
19332         return this.sanchors[this.position];
19333     },
19334
19335     // private
19336     getAlignAdj : function(){
19337         var cm = this.cmargins;
19338         switch(this.position){
19339             case "west":
19340                 return [0, 0];
19341             break;
19342             case "east":
19343                 return [0, 0];
19344             break;
19345             case "north":
19346                 return [0, 0];
19347             break;
19348             case "south":
19349                 return [0, 0];
19350             break;
19351         }
19352     },
19353
19354     // private
19355     getExpandAdj : function(){
19356         var c = this.collapsedEl, cm = this.cmargins;
19357         switch(this.position){
19358             case "west":
19359                 return [-(cm.right+c.getWidth()+cm.left), 0];
19360             break;
19361             case "east":
19362                 return [cm.right+c.getWidth()+cm.left, 0];
19363             break;
19364             case "north":
19365                 return [0, -(cm.top+cm.bottom+c.getHeight())];
19366             break;
19367             case "south":
19368                 return [0, cm.top+cm.bottom+c.getHeight()];
19369             break;
19370         }
19371     }
19372 };
19373
19374 /**
19375  * @class Ext.layout.BorderLayout.SplitRegion
19376  * @extends Ext.layout.BorderLayout.Region
19377  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
19378  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
19379  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
19380  * @constructor
19381  * Create a new SplitRegion.
19382  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
19383  * @param {Object} config The configuration options
19384  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
19385  * BorderLayout must have a center region for the primary content -- all other regions are optional.
19386  */
19387 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
19388     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
19389     // prevent switch
19390     this.applyLayout = this.applyFns[pos];
19391 };
19392
19393 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
19394     /**
19395      * @cfg {Number} tickSize
19396      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
19397      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
19398      */
19399     /**
19400      * @cfg {String} splitTip
19401      * The tooltip to display when the user hovers over a
19402      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
19403      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
19404      * <tt>{@link #useSplitTips} = true</tt>.
19405      */
19406     splitTip : "Drag to resize.",
19407     /**
19408      * @cfg {String} collapsibleSplitTip
19409      * The tooltip to display when the user hovers over a
19410      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
19411      * (defaults to "Drag to resize. Double click to hide."). Only applies if
19412      * <tt>{@link #useSplitTips} = true</tt>.
19413      */
19414     collapsibleSplitTip : "Drag to resize. Double click to hide.",
19415     /**
19416      * @cfg {Boolean} useSplitTips
19417      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
19418      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
19419      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
19420      */
19421     useSplitTips : false,
19422
19423     // private
19424     splitSettings : {
19425         north : {
19426             orientation: Ext.SplitBar.VERTICAL,
19427             placement: Ext.SplitBar.TOP,
19428             maxFn : 'getVMaxSize',
19429             minProp: 'minHeight',
19430             maxProp: 'maxHeight'
19431         },
19432         south : {
19433             orientation: Ext.SplitBar.VERTICAL,
19434             placement: Ext.SplitBar.BOTTOM,
19435             maxFn : 'getVMaxSize',
19436             minProp: 'minHeight',
19437             maxProp: 'maxHeight'
19438         },
19439         east : {
19440             orientation: Ext.SplitBar.HORIZONTAL,
19441             placement: Ext.SplitBar.RIGHT,
19442             maxFn : 'getHMaxSize',
19443             minProp: 'minWidth',
19444             maxProp: 'maxWidth'
19445         },
19446         west : {
19447             orientation: Ext.SplitBar.HORIZONTAL,
19448             placement: Ext.SplitBar.LEFT,
19449             maxFn : 'getHMaxSize',
19450             minProp: 'minWidth',
19451             maxProp: 'maxWidth'
19452         }
19453     },
19454
19455     // private
19456     applyFns : {
19457         west : function(box){
19458             if(this.isCollapsed){
19459                 return this.applyLayoutCollapsed(box);
19460             }
19461             var sd = this.splitEl.dom, s = sd.style;
19462             this.panel.setPosition(box.x, box.y);
19463             var sw = sd.offsetWidth;
19464             s.left = (box.x+box.width-sw)+'px';
19465             s.top = (box.y)+'px';
19466             s.height = Math.max(0, box.height)+'px';
19467             this.panel.setSize(box.width-sw, box.height);
19468         },
19469         east : function(box){
19470             if(this.isCollapsed){
19471                 return this.applyLayoutCollapsed(box);
19472             }
19473             var sd = this.splitEl.dom, s = sd.style;
19474             var sw = sd.offsetWidth;
19475             this.panel.setPosition(box.x+sw, box.y);
19476             s.left = (box.x)+'px';
19477             s.top = (box.y)+'px';
19478             s.height = Math.max(0, box.height)+'px';
19479             this.panel.setSize(box.width-sw, box.height);
19480         },
19481         north : function(box){
19482             if(this.isCollapsed){
19483                 return this.applyLayoutCollapsed(box);
19484             }
19485             var sd = this.splitEl.dom, s = sd.style;
19486             var sh = sd.offsetHeight;
19487             this.panel.setPosition(box.x, box.y);
19488             s.left = (box.x)+'px';
19489             s.top = (box.y+box.height-sh)+'px';
19490             s.width = Math.max(0, box.width)+'px';
19491             this.panel.setSize(box.width, box.height-sh);
19492         },
19493         south : function(box){
19494             if(this.isCollapsed){
19495                 return this.applyLayoutCollapsed(box);
19496             }
19497             var sd = this.splitEl.dom, s = sd.style;
19498             var sh = sd.offsetHeight;
19499             this.panel.setPosition(box.x, box.y+sh);
19500             s.left = (box.x)+'px';
19501             s.top = (box.y)+'px';
19502             s.width = Math.max(0, box.width)+'px';
19503             this.panel.setSize(box.width, box.height-sh);
19504         }
19505     },
19506
19507     // private
19508     render : function(ct, p){
19509         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
19510
19511         var ps = this.position;
19512
19513         this.splitEl = ct.createChild({
19514             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
19515             id: this.panel.id + '-xsplit'
19516         });
19517
19518         if(this.collapseMode == 'mini'){
19519             this.miniSplitEl = this.splitEl.createChild({
19520                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
19521             });
19522             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
19523             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
19524         }
19525
19526         var s = this.splitSettings[ps];
19527
19528         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
19529         this.split.tickSize = this.tickSize;
19530         this.split.placement = s.placement;
19531         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
19532         this.split.minSize = this.minSize || this[s.minProp];
19533         this.split.on("beforeapply", this.onSplitMove, this);
19534         this.split.useShim = this.useShim === true;
19535         this.maxSize = this.maxSize || this[s.maxProp];
19536
19537         if(p.hidden){
19538             this.splitEl.hide();
19539         }
19540
19541         if(this.useSplitTips){
19542             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
19543         }
19544         if(this.collapsible){
19545             this.splitEl.on("dblclick", this.onCollapseClick,  this);
19546         }
19547     },
19548
19549     //docs inherit from superclass
19550     getSize : function(){
19551         if(this.isCollapsed){
19552             return this.collapsedEl.getSize();
19553         }
19554         var s = this.panel.getSize();
19555         if(this.position == 'north' || this.position == 'south'){
19556             s.height += this.splitEl.dom.offsetHeight;
19557         }else{
19558             s.width += this.splitEl.dom.offsetWidth;
19559         }
19560         return s;
19561     },
19562
19563     // private
19564     getHMaxSize : function(){
19565          var cmax = this.maxSize || 10000;
19566          var center = this.layout.center;
19567          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
19568     },
19569
19570     // private
19571     getVMaxSize : function(){
19572         var cmax = this.maxSize || 10000;
19573         var center = this.layout.center;
19574         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
19575     },
19576
19577     // private
19578     onSplitMove : function(split, newSize){
19579         var s = this.panel.getSize();
19580         this.lastSplitSize = newSize;
19581         if(this.position == 'north' || this.position == 'south'){
19582             this.panel.setSize(s.width, newSize);
19583             this.state.height = newSize;
19584         }else{
19585             this.panel.setSize(newSize, s.height);
19586             this.state.width = newSize;
19587         }
19588         this.layout.layout();
19589         this.panel.saveState();
19590         return false;
19591     },
19592
19593     /**
19594      * Returns a reference to the split bar in use by this region.
19595      * @return {Ext.SplitBar} The split bar
19596      */
19597     getSplitBar : function(){
19598         return this.split;
19599     },
19600
19601     // inherit docs
19602     destroy : function() {
19603         Ext.destroy(
19604             this.miniSplitEl,
19605             this.split,
19606             this.splitEl
19607         );
19608     }
19609 });
19610
19611 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
19612  * @class Ext.layout.FormLayout
19613  * @extends Ext.layout.AnchorLayout
19614  * <p>This layout manager is specifically designed for rendering and managing child Components of
19615  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
19616  * {@link Ext.form.Field Field}s.</p>
19617  *
19618  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
19619  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
19620  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
19621  *
19622  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
19623  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
19624  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
19625  *
19626  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
19627  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
19628  * layout-specific config properties:<div class="mdetail-params"><ul>
19629  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
19630  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
19631  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
19632  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
19633  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
19634  * </ul></div></p>
19635  *
19636  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
19637  * <div class="mdetail-params"><ul>
19638  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
19639  * </ul></div></p>
19640  *
19641  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
19642  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
19643  * in this way may be configured with the following options which affect the way the FormLayout renders them:
19644  * <div class="mdetail-params"><ul>
19645  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
19646  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
19647  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
19648  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
19649  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
19650  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
19651  * </ul></div></p>
19652  *
19653  * <p>Example usage:</p>
19654  * <pre><code>
19655 // Required if showing validation messages
19656 Ext.QuickTips.init();
19657
19658 // While you can create a basic Panel with layout:'form', practically
19659 // you should usually use a FormPanel to also get its form functionality
19660 // since it already creates a FormLayout internally.
19661 var form = new Ext.form.FormPanel({
19662     title: 'Form Layout',
19663     bodyStyle: 'padding:15px',
19664     width: 350,
19665     defaultType: 'textfield',
19666     defaults: {
19667         // applied to each contained item
19668         width: 230,
19669         msgTarget: 'side'
19670     },
19671     items: [{
19672             fieldLabel: 'First Name',
19673             name: 'first',
19674             allowBlank: false,
19675             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
19676         },{
19677             fieldLabel: 'Last Name',
19678             name: 'last'
19679         },{
19680             fieldLabel: 'Email',
19681             name: 'email',
19682             vtype:'email'
19683         }, {
19684             xtype: 'textarea',
19685             hideLabel: true,     // override hideLabels layout config
19686             name: 'msg',
19687             anchor: '100% -53'
19688         }
19689     ],
19690     buttons: [
19691         {text: 'Save'},
19692         {text: 'Cancel'}
19693     ],
19694     layoutConfig: {
19695         {@link #labelSeparator}: '~' // superseded by assignment below
19696     },
19697     // config options applicable to container when layout='form':
19698     hideLabels: false,
19699     labelAlign: 'left',   // or 'right' or 'top'
19700     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
19701     labelWidth: 65,       // defaults to 100
19702     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
19703 });
19704 </code></pre>
19705  */
19706 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
19707
19708     /**
19709      * @cfg {String} labelSeparator
19710      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
19711      * of this property at the <b>container</b> level takes precedence.
19712      */
19713     labelSeparator : ':',
19714
19715     /**
19716      * Read only. The CSS style specification string added to field labels in this layout if not
19717      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
19718      * @type String
19719      * @property labelStyle
19720      */
19721
19722     // private
19723     setContainer : function(ct){
19724         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
19725         if(ct.labelAlign){
19726             ct.addClass('x-form-label-'+ct.labelAlign);
19727         }
19728
19729         if(ct.hideLabels){
19730             this.labelStyle = "display:none";
19731             this.elementStyle = "padding-left:0;";
19732             this.labelAdjust = 0;
19733         }else{
19734             this.labelSeparator = ct.labelSeparator || this.labelSeparator;
19735             ct.labelWidth = ct.labelWidth || 100;
19736             if(typeof ct.labelWidth == 'number'){
19737                 var pad = (typeof ct.labelPad == 'number' ? ct.labelPad : 5);
19738                 this.labelAdjust = ct.labelWidth+pad;
19739                 this.labelStyle = "width:"+ct.labelWidth+"px;";
19740                 this.elementStyle = "padding-left:"+(ct.labelWidth+pad)+'px';
19741             }
19742             if(ct.labelAlign == 'top'){
19743                 this.labelStyle = "width:auto;";
19744                 this.labelAdjust = 0;
19745                 this.elementStyle = "padding-left:0;";
19746             }
19747         }
19748     },
19749
19750     //private
19751     getLabelStyle: function(s){
19752         var ls = '', items = [this.labelStyle, s];
19753         for (var i = 0, len = items.length; i < len; ++i){
19754             if (items[i]){
19755                 ls += items[i];
19756                 if (ls.substr(-1, 1) != ';'){
19757                     ls += ';'
19758                 }
19759             }
19760         }
19761         return ls;
19762     },
19763
19764     /**
19765      * @cfg {Ext.Template} fieldTpl
19766      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
19767      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
19768 new Ext.Template(
19769     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
19770         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
19771         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
19772         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
19773     '&lt;/div>'
19774 );
19775 </code></pre>
19776      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
19777      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
19778      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
19779      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
19780      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
19781      * supplied at the container level.</div></li>
19782      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
19783      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
19784      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
19785      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
19786      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
19787      * field (defaults to <tt>''</tt>)</div></li>
19788      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
19789      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
19790      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
19791      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
19792      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
19793      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
19794      * </ul></div>
19795      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
19796      */
19797
19798     // private
19799     renderItem : function(c, position, target){
19800         if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
19801             var args = this.getTemplateArgs(c);
19802             if(typeof position == 'number'){
19803                 position = target.dom.childNodes[position] || null;
19804             }
19805             if(position){
19806                 this.fieldTpl.insertBefore(position, args);
19807             }else{
19808                 this.fieldTpl.append(target, args);
19809             }
19810             c.render('x-form-el-'+c.id);
19811         }else {
19812             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
19813         }
19814     },
19815
19816     /**
19817      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
19818      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
19819      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
19820      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
19821      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
19822      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
19823      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
19824      * supplied at the container level.</div></li>
19825      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
19826      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
19827      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
19828      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
19829      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
19830      * field (defaults to <tt>''</tt>)</div></li>
19831      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
19832      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
19833      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
19834      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
19835      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
19836      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
19837      * </ul></div>
19838      * @param field The {@link Field Ext.form.Field} being rendered.
19839      * @return An object hash containing the properties required to render the Field.
19840      */
19841     getTemplateArgs: function(field) {
19842         var noLabelSep = !field.fieldLabel || field.hideLabel;
19843         return {
19844             id: field.id,
19845             label: field.fieldLabel,
19846             labelStyle: field.labelStyle||this.labelStyle||'',
19847             elementStyle: this.elementStyle||'',
19848             labelSeparator: noLabelSep ? '' : (typeof field.labelSeparator == 'undefined' ? this.labelSeparator : field.labelSeparator),
19849             itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
19850             clearCls: field.clearCls || 'x-form-clear-left'
19851         };
19852     },
19853
19854     // private
19855     adjustWidthAnchor : function(value, comp){
19856         return value - (comp.isFormField || comp.fieldLabel  ? (comp.hideLabel ? 0 : this.labelAdjust) : 0);
19857     },
19858
19859     // private
19860     isValidParent : function(c, target){
19861         return true;
19862     }
19863
19864     /**
19865      * @property activeItem
19866      * @hide
19867      */
19868 });
19869
19870 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;/**\r
19871  * @class Ext.layout.AccordionLayout\r
19872  * @extends Ext.layout.FitLayout\r
19873  * <p>This is a layout that contains multiple panels in an expandable accordion style such that only\r
19874  * <b>one panel can be open at any given time</b>.  Each panel has built-in support for expanding and collapsing.\r
19875  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
19876  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
19877  * <p>Example usage:</p>\r
19878  * <pre><code>\r
19879 var accordion = new Ext.Panel({\r
19880     title: 'Accordion Layout',\r
19881     layout:'accordion',\r
19882     defaults: {\r
19883         // applied to each contained panel\r
19884         bodyStyle: 'padding:15px'\r
19885     },\r
19886     layoutConfig: {\r
19887         // layout-specific configs go here\r
19888         titleCollapse: false,\r
19889         animate: true,\r
19890         activeOnTop: true\r
19891     },\r
19892     items: [{\r
19893         title: 'Panel 1',\r
19894         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
19895     },{\r
19896         title: 'Panel 2',\r
19897         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
19898     },{\r
19899         title: 'Panel 3',\r
19900         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
19901     }]\r
19902 });\r
19903 </code></pre>\r
19904  */\r
19905 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {\r
19906     /**\r
19907      * @cfg {Boolean} fill\r
19908      * True to adjust the active item's height to fill the available space in the container, false to use the\r
19909      * item's current height, or auto height if not explicitly set (defaults to true).\r
19910      */\r
19911     fill : true,\r
19912     /**\r
19913      * @cfg {Boolean} autoWidth\r
19914      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).\r
19915      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within\r
19916      * layouts if they have auto width, so in such cases this config should be set to false.\r
19917      */\r
19918     autoWidth : true,\r
19919     /**\r
19920      * @cfg {Boolean} titleCollapse\r
19921      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow\r
19922      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,\r
19923      * {@link #hideCollapseTool} should be false also.\r
19924      */\r
19925     titleCollapse : true,\r
19926     /**\r
19927      * @cfg {Boolean} hideCollapseTool\r
19928      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).\r
19929      * When set to true, {@link #titleCollapse} should be true also.\r
19930      */\r
19931     hideCollapseTool : false,\r
19932     /**\r
19933      * @cfg {Boolean} collapseFirst\r
19934      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools\r
19935      * in the contained panels' title bars, false to render it last (defaults to false).\r
19936      */\r
19937     collapseFirst : false,\r
19938     /**\r
19939      * @cfg {Boolean} animate\r
19940      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and\r
19941      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each\r
19942      * contained panel for this property, set this to undefined at the layout level.\r
19943      */\r
19944     animate : false,\r
19945     /**\r
19946      * @cfg {Boolean} sequence\r
19947      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.\r
19948      */\r
19949     sequence : false,\r
19950     /**\r
19951      * @cfg {Boolean} activeOnTop\r
19952      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,\r
19953      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).\r
19954      */\r
19955     activeOnTop : false,\r
19956 \r
19957     renderItem : function(c){\r
19958         if(this.animate === false){\r
19959             c.animCollapse = false;\r
19960         }\r
19961         c.collapsible = true;\r
19962         if(this.autoWidth){\r
19963             c.autoWidth = true;\r
19964         }\r
19965         if(this.titleCollapse){\r
19966             c.titleCollapse = true;\r
19967         }\r
19968         if(this.hideCollapseTool){\r
19969             c.hideCollapseTool = true;\r
19970         }\r
19971         if(this.collapseFirst !== undefined){\r
19972             c.collapseFirst = this.collapseFirst;\r
19973         }\r
19974         if(!this.activeItem && !c.collapsed){\r
19975             this.activeItem = c;\r
19976         }else if(this.activeItem && this.activeItem != c){\r
19977             c.collapsed = true;\r
19978         }\r
19979         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);\r
19980         c.header.addClass('x-accordion-hd');\r
19981         c.on('beforeexpand', this.beforeExpand, this);\r
19982     },\r
19983 \r
19984     // private\r
19985     beforeExpand : function(p, anim){\r
19986         var ai = this.activeItem;\r
19987         if(ai){\r
19988             if(this.sequence){\r
19989                 delete this.activeItem;\r
19990                 if (!ai.collapsed){\r
19991                     ai.collapse({callback:function(){\r
19992                         p.expand(anim || true);\r
19993                     }, scope: this});\r
19994                     return false;\r
19995                 }\r
19996             }else{\r
19997                 ai.collapse(this.animate);\r
19998             }\r
19999         }\r
20000         this.activeItem = p;\r
20001         if(this.activeOnTop){\r
20002             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);\r
20003         }\r
20004         this.layout();\r
20005     },\r
20006 \r
20007     // private\r
20008     setItemSize : function(item, size){\r
20009         if(this.fill && item){\r
20010             var hh = 0;\r
20011             this.container.items.each(function(p){\r
20012                 if(p != item){\r
20013                     hh += p.header.getHeight();\r
20014                 }    \r
20015             });\r
20016             size.height -= hh;\r
20017             item.setSize(size);\r
20018         }\r
20019     },\r
20020 \r
20021     /**\r
20022      * Sets the active (expanded) item in the layout.\r
20023      * @param {String/Number} item The string component id or numeric index of the item to activate\r
20024      */\r
20025     setActiveItem : function(item){\r
20026         item = this.container.getComponent(item);\r
20027         if(this.activeItem != item){\r
20028             if(item.rendered && item.collapsed){\r
20029                 item.expand();\r
20030             }else{\r
20031                 this.activeItem = item;\r
20032             }\r
20033         }\r
20034 \r
20035     }\r
20036 });\r
20037 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;\r
20038 \r
20039 //backwards compat\r
20040 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**\r
20041  * @class Ext.layout.TableLayout\r
20042  * @extends Ext.layout.ContainerLayout\r
20043  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be\r
20044  * specified, and rowspan and colspan can be used to create complex layouts within the table.\r
20045  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,\r
20046  * and should generally not need to be created directly via the new keyword.</p>\r
20047  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via\r
20048  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the\r
20049  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a\r
20050  * TableLayout can supply the following table-specific config properties:</p>\r
20051  * <ul>\r
20052  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>\r
20053  * <li><b>colspan</b> Applied to the table cell containing the item.</li>\r
20054  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>\r
20055  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>\r
20056  * </ul>\r
20057  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard\r
20058  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes\r
20059  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.\r
20060  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the\r
20061  * total column count in the layoutConfig and start adding panels in their natural order from left to right,\r
20062  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,\r
20063  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add\r
20064  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>\r
20065  * <pre><code>\r
20066 // This code will generate a layout table that is 3 columns by 2 rows\r
20067 // with some spanning included.  The basic layout will be:\r
20068 // +--------+-----------------+\r
20069 // |   A    |   B             |\r
20070 // |        |--------+--------|\r
20071 // |        |   C    |   D    |\r
20072 // +--------+--------+--------+\r
20073 var table = new Ext.Panel({\r
20074     title: 'Table Layout',\r
20075     layout:'table',\r
20076     defaults: {\r
20077         // applied to each contained panel\r
20078         bodyStyle:'padding:20px'\r
20079     },\r
20080     layoutConfig: {\r
20081         // The total column count must be specified here\r
20082         columns: 3\r
20083     },\r
20084     items: [{\r
20085         html: '&lt;p&gt;Cell A content&lt;/p&gt;',\r
20086         rowspan: 2\r
20087     },{\r
20088         html: '&lt;p&gt;Cell B content&lt;/p&gt;',\r
20089         colspan: 2\r
20090     },{\r
20091         html: '&lt;p&gt;Cell C content&lt;/p&gt;',\r
20092         cellCls: 'highlight'\r
20093     },{\r
20094         html: '&lt;p&gt;Cell D content&lt;/p&gt;'\r
20095     }]\r
20096 });\r
20097 </code></pre>\r
20098  */\r
20099 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
20100     /**\r
20101      * @cfg {Number} columns\r
20102      * The total number of columns to create in the table for this layout.  If not specified, all Components added to\r
20103      * this layout will be rendered into a single row using one column per Component.\r
20104      */\r
20105 \r
20106     // private\r
20107     monitorResize:false,\r
20108 \r
20109     /**\r
20110      * @cfg {Object} tableAttrs\r
20111      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification\r
20112      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>\r
20113 {\r
20114     xtype: 'panel',\r
20115     layout: 'table',\r
20116     layoutConfig: {\r
20117         tableAttrs: {\r
20118                 style: {\r
20119                         width: '100%'\r
20120                 }\r
20121         },\r
20122         columns: 3\r
20123     }\r
20124 }</code></pre>\r
20125      */\r
20126     tableAttrs:null,\r
20127     \r
20128     // private\r
20129     setContainer : function(ct){\r
20130         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);\r
20131 \r
20132         this.currentRow = 0;\r
20133         this.currentColumn = 0;\r
20134         this.cells = [];\r
20135     },\r
20136 \r
20137     // private\r
20138     onLayout : function(ct, target){\r
20139         var cs = ct.items.items, len = cs.length, c, i;\r
20140 \r
20141         if(!this.table){\r
20142             target.addClass('x-table-layout-ct');\r
20143 \r
20144             this.table = target.createChild(\r
20145                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
20146         }\r
20147         this.renderAll(ct, target);\r
20148     },\r
20149 \r
20150     // private\r
20151     getRow : function(index){\r
20152         var row = this.table.tBodies[0].childNodes[index];\r
20153         if(!row){\r
20154             row = document.createElement('tr');\r
20155             this.table.tBodies[0].appendChild(row);\r
20156         }\r
20157         return row;\r
20158     },\r
20159 \r
20160     // private\r
20161     getNextCell : function(c){\r
20162         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);\r
20163         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];\r
20164         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){\r
20165             if(!this.cells[rowIndex]){\r
20166                 this.cells[rowIndex] = [];\r
20167             }\r
20168             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){\r
20169                 this.cells[rowIndex][colIndex] = true;\r
20170             }\r
20171         }\r
20172         var td = document.createElement('td');\r
20173         if(c.cellId){\r
20174             td.id = c.cellId;\r
20175         }\r
20176         var cls = 'x-table-layout-cell';\r
20177         if(c.cellCls){\r
20178             cls += ' ' + c.cellCls;\r
20179         }\r
20180         td.className = cls;\r
20181         if(c.colspan){\r
20182             td.colSpan = c.colspan;\r
20183         }\r
20184         if(c.rowspan){\r
20185             td.rowSpan = c.rowspan;\r
20186         }\r
20187         this.getRow(curRow).appendChild(td);\r
20188         return td;\r
20189     },\r
20190     \r
20191     // private\r
20192     getNextNonSpan: function(colIndex, rowIndex){\r
20193         var cols = this.columns;\r
20194         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {\r
20195             if(cols && colIndex >= cols){\r
20196                 rowIndex++;\r
20197                 colIndex = 0;\r
20198             }else{\r
20199                 colIndex++;\r
20200             }\r
20201         }\r
20202         return [colIndex, rowIndex];\r
20203     },\r
20204 \r
20205     // private\r
20206     renderItem : function(c, position, target){\r
20207         if(c && !c.rendered){\r
20208             c.render(this.getNextCell(c));\r
20209             if(this.extraCls){\r
20210                 var t = c.getPositionEl ? c.getPositionEl() : c;\r
20211                 t.addClass(this.extraCls);\r
20212             }\r
20213         }\r
20214     },\r
20215 \r
20216     // private\r
20217     isValidParent : function(c, target){\r
20218         return true;\r
20219     }\r
20220 \r
20221     /**\r
20222      * @property activeItem\r
20223      * @hide\r
20224      */\r
20225 });\r
20226 \r
20227 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**\r
20228  * @class Ext.layout.AbsoluteLayout\r
20229  * @extends Ext.layout.AnchorLayout\r
20230  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the\r
20231  * ability for x/y positioning using the standard x and y component config options.</p>\r
20232  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
20233  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
20234  * <p>Example usage:</p>\r
20235  * <pre><code>\r
20236 var form = new Ext.form.FormPanel({\r
20237     title: 'Absolute Layout',\r
20238     layout:'absolute',\r
20239     layoutConfig: {\r
20240         // layout-specific configs go here\r
20241         extraCls: 'x-abs-layout-item',\r
20242     },\r
20243     baseCls: 'x-plain',\r
20244     url:'save-form.php',\r
20245     defaultType: 'textfield',\r
20246     items: [{\r
20247         x: 0,\r
20248         y: 5,\r
20249         xtype:'label',\r
20250         text: 'Send To:'\r
20251     },{\r
20252         x: 60,\r
20253         y: 0,\r
20254         name: 'to',\r
20255         anchor:'100%'  // anchor width by percentage\r
20256     },{\r
20257         x: 0,\r
20258         y: 35,\r
20259         xtype:'label',\r
20260         text: 'Subject:'\r
20261     },{\r
20262         x: 60,\r
20263         y: 30,\r
20264         name: 'subject',\r
20265         anchor: '100%'  // anchor width by percentage\r
20266     },{\r
20267         x:0,\r
20268         y: 60,\r
20269         xtype: 'textarea',\r
20270         name: 'msg',\r
20271         anchor: '100% 100%'  // anchor width and height\r
20272     }]\r
20273 });\r
20274 </code></pre>\r
20275  */\r
20276 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {\r
20277 \r
20278     extraCls: 'x-abs-layout-item',\r
20279 \r
20280     onLayout : function(ct, target){\r
20281         target.position();\r
20282         this.paddingLeft = target.getPadding('l');\r
20283         this.paddingTop = target.getPadding('t');\r
20284 \r
20285         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);\r
20286     },\r
20287 \r
20288     // private\r
20289     adjustWidthAnchor : function(value, comp){\r
20290         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;\r
20291     },\r
20292 \r
20293     // private\r
20294     adjustHeightAnchor : function(value, comp){\r
20295         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;\r
20296     }\r
20297     /**\r
20298      * @property activeItem\r
20299      * @hide\r
20300      */\r
20301 });\r
20302 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
20303 /**\r
20304  * @class Ext.layout.BoxLayout\r
20305  * @extends Ext.layout.ContainerLayout\r
20306  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>\r
20307  */\r
20308 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
20309     /**\r
20310      * @cfg {Object} defaultMargins\r
20311      * <p>If the individual contained items do not have a <tt>margins</tt>\r
20312      * property specified, the default margins from this property will be\r
20313      * applied to each item.</p>\r
20314      * <br><p>This property may be specified as an object containing margins\r
20315      * to apply in the format:</p><pre><code>\r
20316 {\r
20317     top: (top margin),\r
20318     right: (right margin),\r
20319     bottom: (bottom margin),\r
20320     left: (left margin)\r
20321 }</code></pre>\r
20322      * <p>This property may also be specified as a string containing\r
20323      * space-separated, numeric margin values. The order of the sides associated\r
20324      * with each value matches the way CSS processes margin values:</p>\r
20325      * <div class="mdetail-params"><ul>\r
20326      * <li>If there is only one value, it applies to all sides.</li>\r
20327      * <li>If there are two values, the top and bottom borders are set to the\r
20328      * first value and the right and left are set to the second.</li>\r
20329      * <li>If there are three values, the top is set to the first value, the left\r
20330      * and right are set to the second, and the bottom is set to the third.</li>\r
20331      * <li>If there are four values, they apply to the top, right, bottom, and\r
20332      * left, respectively.</li>\r
20333      * </ul></div>\r
20334      * <p>Defaults to:</p><pre><code>\r
20335      * {top:0, right:0, bottom:0, left:0}\r
20336      * </code></pre>\r
20337      */\r
20338     defaultMargins : {left:0,top:0,right:0,bottom:0},\r
20339     /**\r
20340      * @cfg {String} padding\r
20341      * Defaults to <tt>'0'</tt>. Sets the padding to be applied to all child items managed by this\r
20342      * container's layout. \r
20343      */\r
20344     padding : '0',\r
20345     // documented in subclasses\r
20346     pack : 'start',\r
20347 \r
20348     // private\r
20349     monitorResize : true,\r
20350     scrollOffset : 0,\r
20351     extraCls : 'x-box-item',\r
20352     ctCls : 'x-box-layout-ct',\r
20353     innerCls : 'x-box-inner',\r
20354 \r
20355     // private\r
20356     isValidParent : function(c, target){\r
20357         return c.getEl().dom.parentNode == this.innerCt.dom;\r
20358     },\r
20359 \r
20360     // private\r
20361     onLayout : function(ct, target){\r
20362         var cs = ct.items.items, len = cs.length, c, i, last = len-1, cm;\r
20363 \r
20364         if(!this.innerCt){\r
20365             target.addClass(this.ctCls);\r
20366 \r
20367             // the innerCt prevents wrapping and shuffling while\r
20368             // the container is resizing\r
20369             this.innerCt = target.createChild({cls:this.innerCls});\r
20370             this.padding = this.parseMargins(this.padding); \r
20371         }\r
20372         this.renderAll(ct, this.innerCt);\r
20373     },\r
20374 \r
20375     // private\r
20376     renderItem : function(c){\r
20377         if(typeof c.margins == 'string'){\r
20378             c.margins = this.parseMargins(c.margins);\r
20379         }else if(!c.margins){\r
20380             c.margins = this.defaultMargins;\r
20381         }\r
20382         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);\r
20383     },\r
20384 \r
20385     getTargetSize : function(target){
20386         return (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getStyleSize() : target.getViewSize();\r
20387     },\r
20388     \r
20389     getItems: function(ct){\r
20390         var items = [];\r
20391         ct.items.each(function(c){\r
20392             if(c.isVisible()){\r
20393                 items.push(c);\r
20394             }\r
20395         });\r
20396         return items;\r
20397     }\r
20398 \r
20399     /**\r
20400      * @property activeItem\r
20401      * @hide\r
20402      */\r
20403 });\r
20404 \r
20405 /**\r
20406  * @class Ext.layout.VBoxLayout\r
20407  * @extends Ext.layout.BoxLayout\r
20408  * A layout that arranges items vertically\r
20409  */\r
20410 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {\r
20411     /**\r
20412      * @cfg {String} align\r
20413      * Controls how the child items of the container are aligned. Acceptable configuration values for this\r
20414      * property are:\r
20415      * <div class="mdetail-params"><ul>\r
20416      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally\r
20417      * at the <b>left</b> side of the container</div></li>\r
20418      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the\r
20419      * <b>mid-width</b> of the container</div></li>\r
20420      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill\r
20421      * the width of the container</div></li>\r
20422      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to\r
20423      * the size of the largest item.</div></li>\r
20424      * </ul></div>\r
20425      */\r
20426     align : 'left', // left, center, stretch, strechmax\r
20427     /**\r
20428      * @cfg {String} pack\r
20429      * Controls how the child items of the container are packed together. Acceptable configuration values\r
20430      * for this property are:\r
20431      * <div class="mdetail-params"><ul>\r
20432      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at\r
20433      * <b>top</b> side of container</div></li>\r
20434      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at\r
20435      * <b>mid-height</b> of container</div></li>\r
20436      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>\r
20437      * side of container</div></li>\r
20438      * </ul></div>\r
20439      */\r
20440     /**\r
20441      * @cfg {Number} flex\r
20442      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed\r
20443      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>\r
20444      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with\r
20445      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or\r
20446      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).\r
20447      */\r
20448 \r
20449     // private\r
20450     onLayout : function(ct, target){\r
20451         Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target);\r
20452                     \r
20453         \r
20454         var cs = this.getItems(ct), cm, ch, margin,\r
20455             size = this.getTargetSize(target),\r
20456             w = size.width - target.getPadding('lr') - this.scrollOffset,\r
20457             h = size.height - target.getPadding('tb'),\r
20458             l = this.padding.left, t = this.padding.top,\r
20459             isStart = this.pack == 'start',\r
20460             isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,\r
20461             stretchWidth = w - (this.padding.left + this.padding.right),\r
20462             extraHeight = 0,\r
20463             maxWidth = 0,\r
20464             totalFlex = 0,\r
20465             flexHeight = 0,\r
20466             usedHeight = 0;\r
20467             \r
20468         Ext.each(cs, function(c){\r
20469             cm = c.margins;\r
20470             totalFlex += c.flex || 0;\r
20471             ch = c.getHeight();\r
20472             margin = cm.top + cm.bottom;\r
20473             extraHeight += ch + margin;\r
20474             flexHeight += margin + (c.flex ? 0 : ch);\r
20475             maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right);\r
20476         });\r
20477         extraHeight = h - extraHeight - this.padding.top - this.padding.bottom;\r
20478         \r
20479         var innerCtWidth = maxWidth + this.padding.left + this.padding.right;\r
20480         switch(this.align){\r
20481             case 'stretch':\r
20482                 this.innerCt.setSize(w, h);\r
20483                 break;\r
20484             case 'stretchmax':\r
20485             case 'left':\r
20486                 this.innerCt.setSize(innerCtWidth, h);\r
20487                 break;\r
20488             case 'center':\r
20489                 this.innerCt.setSize(w = Math.max(w, innerCtWidth), h);\r
20490                 break;\r
20491         }\r
20492 \r
20493         var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight),\r
20494             leftOver = availHeight,\r
20495             heights = [],\r
20496             restore = [],\r
20497             idx = 0,\r
20498             availableWidth = Math.max(0, w - this.padding.left - this.padding.right);\r
20499             \r
20500 \r
20501         Ext.each(cs, function(c){\r
20502             if(isStart && c.flex){\r
20503                 ch = Math.floor(availHeight * (c.flex / totalFlex));\r
20504                 leftOver -= ch;\r
20505                 heights.push(ch);\r
20506             }\r
20507         }); \r
20508         \r
20509         if(this.pack == 'center'){\r
20510             t += extraHeight ? extraHeight / 2 : 0;\r
20511         }else if(this.pack == 'end'){\r
20512             t += extraHeight;\r
20513         }\r
20514         Ext.each(cs, function(c){\r
20515             cm = c.margins;\r
20516             t += cm.top;\r
20517             c.setPosition(l + cm.left, t);\r
20518             if(isStart && c.flex){\r
20519                 ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0));\r
20520                 if(isRestore){\r
20521                     restore.push(c.getWidth());\r
20522                 }\r
20523                 c.setSize(availableWidth, ch);\r
20524             }else{\r
20525                 ch = c.getHeight();\r
20526             }\r
20527             t += ch + cm.bottom;\r
20528         });\r
20529         \r
20530         idx = 0;\r
20531         Ext.each(cs, function(c){\r
20532             cm = c.margins;\r
20533             if(this.align == 'stretch'){\r
20534                 c.setWidth((stretchWidth - (cm.left + cm.right)).constrain(\r
20535                     c.minWidth || 0, c.maxWidth || 1000000));\r
20536             }else if(this.align == 'stretchmax'){\r
20537                 c.setWidth((maxWidth - (cm.left + cm.right)).constrain(\r
20538                     c.minWidth || 0, c.maxWidth || 1000000));\r
20539             }else{\r
20540                 if(this.align == 'center'){\r
20541                     var diff = availableWidth - (c.getWidth() + cm.left + cm.right);\r
20542                     if(diff > 0){\r
20543                         c.setPosition(l + cm.left + (diff/2), c.y);\r
20544                     }\r
20545                 }\r
20546                 if(isStart && c.flex){\r
20547                     c.setWidth(restore[idx++]);\r
20548                 }\r
20549             }\r
20550         }, this);\r
20551     }\r
20552     /**\r
20553      * @property activeItem\r
20554      * @hide\r
20555      */\r
20556 });\r
20557 \r
20558 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;\r
20559 \r
20560 /**\r
20561  * @class Ext.layout.HBoxLayout\r
20562  * @extends Ext.layout.BoxLayout\r
20563  * A layout that arranges items horizontally\r
20564  */\r
20565 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {\r
20566     /**\r
20567      * @cfg {String} align\r
20568      * Controls how the child items of the container are aligned. Acceptable configuration values for this\r
20569      * property are:\r
20570      * <div class="mdetail-params"><ul>\r
20571      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically\r
20572      * at the <b>left</b> side of the container</div></li>\r
20573      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically at the\r
20574      * <b>mid-height</b> of the container</div></li>\r
20575      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill\r
20576      * the height of the container</div></li>\r
20577      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to\r
20578      * the size of the largest item.</div></li>\r
20579      */\r
20580     align : 'top', // top, middle, stretch, strechmax\r
20581     /**\r
20582      * @cfg {String} pack\r
20583      * Controls how the child items of the container are packed together. Acceptable configuration values\r
20584      * for this property are:\r
20585      * <div class="mdetail-params"><ul>\r
20586      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at\r
20587      * <b>left</b> side of container</div></li>\r
20588      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at\r
20589      * <b>mid-width</b> of container</div></li>\r
20590      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>\r
20591      * side of container</div></li>\r
20592      * </ul></div>\r
20593      */\r
20594     /**\r
20595      * @cfg {Number} flex\r
20596      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed\r
20597      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>\r
20598      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with\r
20599      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or\r
20600      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).\r
20601      */\r
20602 \r
20603     // private\r
20604     onLayout : function(ct, target){\r
20605         Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);\r
20606         \r
20607         var cs = this.getItems(ct), cm, cw, margin,\r
20608             size = this.getTargetSize(target),\r
20609             w = size.width - target.getPadding('lr') - this.scrollOffset,\r
20610             h = size.height - target.getPadding('tb'),\r
20611             l = this.padding.left, t = this.padding.top,\r
20612             isStart = this.pack == 'start',\r
20613             isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,\r
20614             stretchHeight = h - (this.padding.top + this.padding.bottom),\r
20615             extraWidth = 0,\r
20616             maxHeight = 0,\r
20617             totalFlex = 0,\r
20618             flexWidth = 0,\r
20619             usedWidth = 0;\r
20620         \r
20621         Ext.each(cs, function(c){\r
20622             cm = c.margins;\r
20623             totalFlex += c.flex || 0;\r
20624             cw = c.getWidth();\r
20625             margin = cm.left + cm.right;\r
20626             extraWidth += cw + margin;\r
20627             flexWidth += margin + (c.flex ? 0 : cw);\r
20628             maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);\r
20629         });\r
20630         extraWidth = w - extraWidth - this.padding.left - this.padding.right;\r
20631         \r
20632         var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;\r
20633         switch(this.align){\r
20634             case 'stretch':\r
20635                 this.innerCt.setSize(w, h);\r
20636                 break;\r
20637             case 'stretchmax':\r
20638             case 'top':\r
20639                 this.innerCt.setSize(w, innerCtHeight);\r
20640                 break;\r
20641             case 'middle':\r
20642                 this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));\r
20643                 break;\r
20644         }\r
20645         \r
20646 \r
20647         var availWidth = Math.max(0, w - this.padding.left - this.padding.right - flexWidth),\r
20648             leftOver = availWidth,\r
20649             widths = [],\r
20650             restore = [],\r
20651             idx = 0,\r
20652             availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);\r
20653             \r
20654 \r
20655         Ext.each(cs, function(c){\r
20656             if(isStart && c.flex){\r
20657                 cw = Math.floor(availWidth * (c.flex / totalFlex));\r
20658                 leftOver -= cw;\r
20659                 widths.push(cw);\r
20660             }\r
20661         }); \r
20662         \r
20663         if(this.pack == 'center'){\r
20664             l += extraWidth ? extraWidth / 2 : 0;\r
20665         }else if(this.pack == 'end'){\r
20666             l += extraWidth;\r
20667         }\r
20668         Ext.each(cs, function(c){\r
20669             cm = c.margins;\r
20670             l += cm.left;\r
20671             c.setPosition(l, t + cm.top);\r
20672             if(isStart && c.flex){\r
20673                 cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));\r
20674                 if(isRestore){\r
20675                     restore.push(c.getHeight());\r
20676                 }\r
20677                 c.setSize(cw, availableHeight);\r
20678             }else{\r
20679                 cw = c.getWidth();\r
20680             }\r
20681             l += cw + cm.right;\r
20682         });\r
20683         \r
20684         idx = 0;\r
20685         Ext.each(cs, function(c){\r
20686             var cm = c.margins;\r
20687             if(this.align == 'stretch'){\r
20688                 c.setHeight((stretchHeight - (cm.top + cm.bottom)).constrain(\r
20689                     c.minHeight || 0, c.maxHeight || 1000000));\r
20690             }else if(this.align == 'stretchmax'){\r
20691                 c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(\r
20692                     c.minHeight || 0, c.maxHeight || 1000000));\r
20693             }else{\r
20694                 if(this.align == 'middle'){\r
20695                     var diff = availableHeight - (c.getHeight() + cm.top + cm.bottom);\r
20696                     if(diff > 0){\r
20697                         c.setPosition(c.x, t + cm.top + (diff/2));\r
20698                     }\r
20699                 }\r
20700                 if(isStart && c.flex){\r
20701                     c.setHeight(restore[idx++]);\r
20702                 }\r
20703             }\r
20704         }, this);\r
20705     }\r
20706 \r
20707     /**\r
20708      * @property activeItem\r
20709      * @hide\r
20710      */\r
20711 });\r
20712 \r
20713 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;
20714 /**\r
20715  * @class Ext.Viewport\r
20716  * @extends Ext.Container\r
20717  * <p>A specialized container representing the viewable application area (the browser viewport).</p>\r
20718  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of\r
20719  * the browser viewport and manages window resizing. There may only be one Viewport created\r
20720  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s\r
20721  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}\r
20722  * method of any of its child Panels may themselves have a layout.</p>\r
20723  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide\r
20724  * for scrolling if needed using the {@link #autoScroll} config.</p>\r
20725  * <p>An example showing a classic application border layout:</p><pre><code>\r
20726 new Ext.Viewport({\r
20727     layout: 'border',\r
20728     items: [{\r
20729         region: 'north',\r
20730         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',\r
20731         autoHeight: true,\r
20732         border: false,\r
20733         margins: '0 0 5 0'\r
20734     }, {\r
20735         region: 'west',\r
20736         collapsible: true,\r
20737         title: 'Navigation',\r
20738         width: 200\r
20739         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout} \r
20740     }, {\r
20741         region: 'south',\r
20742         title: 'Title for Panel',\r
20743         collapsible: true,\r
20744         html: 'Information goes here',\r
20745         split: true,\r
20746         height: 100,\r
20747         minHeight: 100\r
20748     }, {\r
20749         region: 'east',\r
20750         title: 'Title for the Grid Panel',\r
20751         collapsible: true,\r
20752         split: true,\r
20753         width: 200,\r
20754         xtype: 'grid',\r
20755         // remaining grid configuration not shown ...\r
20756         // notice that the GridPanel is added directly as the region\r
20757         // it is not "overnested" inside another Panel\r
20758     }, {\r
20759         region: 'center',\r
20760         xtype: 'tabpanel', // TabPanel itself has no title\r
20761         items: {\r
20762             title: 'Default Tab',\r
20763             html: 'The first tab\'s content. Others may be added dynamically'\r
20764         }\r
20765     }]\r
20766 });\r
20767 </code></pre>\r
20768  * @constructor\r
20769  * Create a new Viewport\r
20770  * @param {Object} config The config object\r
20771  * @xtype viewport\r
20772  */\r
20773 Ext.Viewport = Ext.extend(Ext.Container, {\r
20774         /*\r
20775          * Privatize config options which, if used, would interfere with the\r
20776          * correct operation of the Viewport as the sole manager of the\r
20777          * layout of the document body.\r
20778          */\r
20779     /**\r
20780      * @cfg {Mixed} applyTo @hide\r
20781          */\r
20782     /**\r
20783      * @cfg {Boolean} allowDomMove @hide\r
20784          */\r
20785     /**\r
20786      * @cfg {Boolean} hideParent @hide\r
20787          */\r
20788     /**\r
20789      * @cfg {Mixed} renderTo @hide\r
20790          */\r
20791     /**\r
20792      * @cfg {Boolean} hideParent @hide\r
20793          */\r
20794     /**\r
20795      * @cfg {Number} height @hide\r
20796          */\r
20797     /**\r
20798      * @cfg {Number} width @hide\r
20799          */\r
20800     /**\r
20801      * @cfg {Boolean} autoHeight @hide\r
20802          */\r
20803     /**\r
20804      * @cfg {Boolean} autoWidth @hide\r
20805          */\r
20806     /**\r
20807      * @cfg {Boolean} deferHeight @hide\r
20808          */\r
20809     /**\r
20810      * @cfg {Boolean} monitorResize @hide\r
20811          */\r
20812     initComponent : function() {\r
20813         Ext.Viewport.superclass.initComponent.call(this);\r
20814         document.getElementsByTagName('html')[0].className += ' x-viewport';\r
20815         this.el = Ext.getBody();\r
20816         this.el.setHeight = Ext.emptyFn;\r
20817         this.el.setWidth = Ext.emptyFn;\r
20818         this.el.setSize = Ext.emptyFn;\r
20819         this.el.dom.scroll = 'no';\r
20820         this.allowDomMove = false;\r
20821         this.autoWidth = true;\r
20822         this.autoHeight = true;\r
20823         Ext.EventManager.onWindowResize(this.fireResize, this);\r
20824         this.renderTo = this.el;\r
20825     },\r
20826 \r
20827     fireResize : function(w, h){\r
20828         this.fireEvent('resize', this, w, h, w, h);\r
20829     }\r
20830 });\r
20831 Ext.reg('viewport', Ext.Viewport);/**
20832  * @class Ext.Panel
20833  * @extends Ext.Container
20834  * <p>Panel is a container that has specific functionality and structural components that make
20835  * it the perfect building block for application-oriented user interfaces.</p>
20836  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
20837  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
20838  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
20839  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
20840  * those child elements need to be sized using one of Ext's built-in <tt><b>{@link Ext.Container#layout layout}</b></tt> schemes. By
20841  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
20842  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
20843  * at all.</p>
20844  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
20845  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
20846  * information).</p>
20847  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
20848  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
20849  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
20850  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
20851  * @constructor
20852  * @param {Object} config The config object
20853  * @xtype panel
20854  */
20855 Ext.Panel = Ext.extend(Ext.Container, {
20856     /**
20857      * The Panel's header {@link Ext.Element Element}. Read-only.
20858      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
20859      * <br><p><b>Note</b>: see the Note for <tt>{@link Ext.Component#el el} also.</p>
20860      * @type Ext.Element
20861      * @property header
20862      */
20863     /**
20864      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
20865      * The content may be specified in the {@link #html} config, or it may be loaded using the
20866      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
20867      * <p>If this is used to load visible HTML elements in either way, then
20868      * the Panel may not be used as a Layout for hosting nested Panels.</p>
20869      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
20870      * then the body Element must not be loaded or changed - it is under the control
20871      * of the Panel's Layout.
20872      * <br><p><b>Note</b>: see the Note for <tt>{@link Ext.Component#el el} also.</p>
20873      * @type Ext.Element
20874      * @property body
20875      */
20876     /**
20877      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
20878      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
20879      * @type Ext.Element
20880      * @property bwrap
20881      */
20882     /**
20883      * True if this panel is collapsed. Read-only.
20884      * @type Boolean
20885      * @property collapsed
20886      */
20887     /**
20888      * @cfg {Object} bodyCfg
20889      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
20890      * Panel Element.</p>
20891      * <p>By default, the Default element in the table below will be used for the html markup to
20892      * create a child element with the commensurate Default class name (<tt>baseCls</tt> will be
20893      * replaced by <tt>{@link #baseCls}</tt>):</p>
20894      * <pre>
20895      * Panel      Default  Default             Custom      Additional       Additional
20896      * Element    element  class               element     class            style
20897      * ========   ==========================   =========   ==============   ===========
20898      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
20899      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
20900      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
20901      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
20902      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
20903      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
20904      * </pre>
20905      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
20906      * to use a different form of markup than is created by default. An example of this might be
20907      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
20908      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
20909      * element:</p>
20910      * <pre><code>
20911 new Ext.Panel({
20912     title: 'Message Title',
20913     renderTo: Ext.getBody(),
20914     width: 200, height: 130,
20915     <b>bodyCfg</b>: {
20916         tag: 'center',
20917         cls: 'x-panel-body',  // Default class not applied if Custom element specified
20918         html: 'Message'
20919     },
20920     footerCfg: {
20921         tag: 'h2',
20922         cls: 'x-panel-footer'        // same as the Default class
20923         html: 'footer html'
20924     },
20925     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
20926     footerStyle:    'background-color:red' // see {@link #bodyStyle}
20927 });
20928      * </code></pre>
20929      * <p>The example above also explicitly creates a <tt>{@link #footer}</tt> with custom markup and
20930      * styling applied.</p>
20931      */
20932     /**
20933      * @cfg {Object} headerCfg
20934      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
20935      * of this Panel's {@link #header} Element.  See <tt>{@link #bodyCfg}</tt> also.</p>
20936      */
20937     /**
20938      * @cfg {Object} bwrapCfg
20939      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
20940      * of this Panel's {@link #bwrap} Element.  See <tt>{@link #bodyCfg}</tt> also.</p>
20941      */
20942     /**
20943      * @cfg {Object} tbarCfg
20944      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
20945      * of this Panel's {@link #tbar} Element.  See <tt>{@link #bodyCfg}</tt> also.</p>
20946      */
20947     /**
20948      * @cfg {Object} bbarCfg
20949      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
20950      * of this Panel's {@link #bbar} Element.  See <tt>{@link #bodyCfg}</tt> also.</p>
20951      */
20952     /**
20953      * @cfg {Object} footerCfg
20954      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
20955      * of this Panel's {@link #footer} Element.  See <tt>{@link #bodyCfg}</tt> also.</p>
20956      */
20957     /**
20958      * @cfg {Boolean} closable
20959      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
20960      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <tt>true</tt>
20961      * to enable closing in such situations. Defaults to <tt>false</tt>.
20962      */
20963     /**
20964      * The Panel's footer {@link Ext.Element Element}. Read-only.
20965      * <p>This Element is used to house the Panel's <tt>{@link #buttons}</tt> or <tt>{@link #fbar}</tt>.</p>
20966      * <br><p><b>Note</b>: see the Note for <tt>{@link Ext.Component#el el} also.</p>
20967      * @type Ext.Element
20968      * @property footer
20969      */
20970     /**
20971      * @cfg {Mixed} applyTo
20972      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
20973      * the document that specifies some panel-specific structural markup.  When <tt>applyTo</tt> is used,
20974      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
20975      * will automatically create those components from that markup. Any required components not specified in the
20976      * markup will be autogenerated if necessary.</p>
20977      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
20978      * <ul><li>baseCls + '-header'</li>
20979      * <li>baseCls + '-header-text'</li>
20980      * <li>baseCls + '-bwrap'</li>
20981      * <li>baseCls + '-tbar'</li>
20982      * <li>baseCls + '-body'</li>
20983      * <li>baseCls + '-bbar'</li>
20984      * <li>baseCls + '-footer'</li></ul>
20985      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
20986      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
20987      * panel's container.</p>
20988      */
20989     /**
20990      * @cfg {Object/Array} tbar
20991      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
20992      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
20993      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
20994      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
20995      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
20996      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
20997      * submission parameters are collected from the DOM tree.</p>
20998      */
20999     /**
21000      * @cfg {Object/Array} bbar
21001      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
21002      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
21003      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
21004      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not<b> be updated by a load
21005      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
21006      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
21007      * submission parameters are collected from the DOM tree.</p>
21008      */
21009     /** @cfg {Object/Array} fbar
21010      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
21011      * {@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>
21012      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
21013      * <p>If <tt>{@link #buttons}</tt> are specified, they will supersede the <tt>fbar</tt> configuration property.</p>
21014      * The Panel's <tt>{@link #buttonAlign}</tt> configuration affects the layout of these items, for example:
21015      * <pre><code>
21016 var w = new Ext.Window({
21017     height: 250,
21018     width: 500,
21019     bbar: new Ext.Toolbar({
21020         items: [{
21021             text: 'bbar Left'
21022         },'->',{
21023             text: 'bbar Right'
21024         }]
21025     }),
21026     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use "-", and "->"
21027                                   // to control the alignment of fbar items
21028     fbar: [{
21029         text: 'fbar Left'
21030     },'->',{
21031         text: 'fbar Right'
21032     }]
21033 }).show();
21034      * </code></pre>
21035      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not<b> be updated by a load
21036      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
21037      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
21038      * submission parameters are collected from the DOM tree.</p>
21039      */
21040     /**
21041      * @cfg {Boolean} header
21042      * <tt>true</tt> to create the Panel's header element explicitly, <tt>false</tt> to skip creating
21043      * it.  If a <tt>{@link #title}</tt> is set the header will be created automatically, otherwise it will not.
21044      * If a <tt>{@link #title}</tt> is set but <tt>header</tt> is explicitly set to <tt>false</tt>, the header
21045      * will not be rendered.
21046      */
21047     /**
21048      * @cfg {Boolean} footer
21049      * <tt>true</tt> to create the footer element explicitly, false to skip creating it. The footer
21050      * will be created automatically if <tt>{@link #buttons}</tt> or a <tt>{@link #fbar}</tt> have
21051      * been configured.  See <tt>{@link #bodyCfg}</tt> for an example.
21052      */
21053     /**
21054      * @cfg {String} title
21055      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
21056      * <tt>{@link #header}</tt> (defaults to ''). When a <tt>title</tt> is specified the
21057      * <tt>{@link #header}</tt> element will automatically be created and displayed unless
21058      * {@link #header} is explicitly set to <tt>false</tt>.  If you do not want to specify a
21059      * <tt>title</tt> at config time, but you may want one later, you must either specify a non-empty
21060      * <tt>title</tt> (a blank space ' ' will do) or <tt>header:true</tt> so that the container
21061      * element will get created.
21062      */
21063     /**
21064      * @cfg {Array} buttons
21065      * <tt>buttons</tt> will be used as <tt>{@link Ext.Container#items items}</tt> for the toolbar in
21066      * the footer (<tt>{@link #fbar}</tt>). Typically the value of this configuration property will be
21067      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
21068      * If an item is configured with <tt>minWidth</tt> or the Panel is configured with <tt>minButtonWidth</tt>,
21069      * that width will be applied to the item.
21070      */
21071     /**
21072      * @cfg {Object/String/Function} autoLoad
21073      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
21074      * If autoLoad is not null, the panel will attempt to load its contents
21075      * immediately upon render.<p>
21076      * The URL will become the default URL for this panel's {@link #body} element,
21077      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
21078      */
21079     /**
21080      * @cfg {Boolean} frame
21081      * <tt>false</tt> by default to render with plain 1px square borders. <tt>true</tt> to render with
21082      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
21083      * <p>The template generated for each condition is depicted below:</p><pre><code>
21084      *
21085 // frame = false
21086 &lt;div id="developer-specified-id-goes-here" class="x-panel">
21087
21088     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
21089
21090     &lt;div class="x-panel-bwrap">
21091         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
21092     &lt;/div>
21093 &lt;/div>
21094
21095 // frame = true (create 9 elements)
21096 &lt;div id="developer-specified-id-goes-here" class="x-panel">
21097     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
21098         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
21099     &lt;/div>&lt;/div>&lt;/div>
21100
21101     &lt;div class="x-panel-bwrap">
21102         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
21103             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
21104         &lt;/div>&lt;/div>&lt;/div>
21105
21106         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
21107         &lt;/div>&lt;/div>&lt;/div>
21108 &lt;/div>
21109      * </code></pre>
21110      */
21111     /**
21112      * @cfg {Boolean} border
21113      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
21114      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
21115      */
21116     /**
21117      * @cfg {Boolean} bodyBorder
21118      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
21119      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
21120      * as a 1px wide inset border, giving the entire body element an inset appearance.
21121      */
21122     /**
21123      * @cfg {String/Object/Function} bodyCssClass
21124      * Additional css class selector to be applied to the {@link #body} element in the format expected by
21125      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
21126      */
21127     /**
21128      * @cfg {String/Object/Function} bodyStyle
21129      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
21130      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
21131      */
21132     /**
21133      * @cfg {String} iconCls
21134      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
21135      * <p>An example of specifying a custom icon class would be something like:
21136      * </p><pre><code>
21137 // specify the property in the config for the class:
21138      ...
21139      iconCls: 'my-icon'
21140
21141 // css class that specifies background image to be used as the icon image:
21142 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
21143 </code></pre>
21144      */
21145     /**
21146      * @cfg {Boolean} collapsible
21147      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
21148      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
21149      */
21150     /**
21151      * @cfg {Array} tools
21152      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
21153      * stored as an {@link Ext.Element Element} referenced by a public property called <tt><b></b>tools.<i>&lt;tool-type&gt;</i></tt>
21154      * <p>Each tool config may contain the following properties:
21155      * <div class="mdetail-params"><ul>
21156      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
21157      * of tool to create. By default, this assigns a CSS class of the form <tt>x-tool-<i>&lt;tool-type&gt;</i></tt> to the
21158      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
21159      * The developer may implement custom tools by supplying alternate CSS rules and background images:
21160      * <ul>
21161      * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><tt> toggle</tt> (Created by default when {@link #collapsible} is <tt>true</tt>)</div>
21162      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><tt> close</tt></div>
21163      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><tt> minimize</tt></div>
21164      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><tt> maximize</tt></div>
21165      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><tt> restore</tt></div>
21166      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><tt> gear</tt></div>
21167      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><tt> pin</tt></div>
21168      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><tt> unpin</tt></div>
21169      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><tt> right</tt></div>
21170      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><tt> left</tt></div>
21171      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><tt> up</tt></div>
21172      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><tt> down</tt></div>
21173      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><tt> refresh</tt></div>
21174      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><tt> minus</tt></div>
21175      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><tt> plus</tt></div>
21176      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><tt> help</tt></div>
21177      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><tt> search</tt></div>
21178      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><tt> save</tt></div>
21179      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><tt> print</tt></div>
21180      * </ul></div></li>
21181      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
21182      * call when clicked. Arguments passed are:<ul>
21183      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
21184      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
21185      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
21186      * <li><b>tc</b> : Ext.Panel<div class="sub-desc">The tool configuration object</div></li>
21187      * </ul></div></li>
21188      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
21189      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
21190      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
21191      * a config argument to {@link Ext.QuickTip#register}</div></li>
21192      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
21193      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
21194      * event listeners in the format of an argument to {@link #addListener}</div></li>
21195      * </ul></div>
21196      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
21197      * tools only provide the visual button. Any required functionality must be provided by adding
21198      * handlers that implement the necessary behavior.</p>
21199      * <p>Example usage:</p>
21200      * <pre><code>
21201 tools:[{
21202     id:'refresh',
21203     qtip: 'Refresh form Data',
21204     // hidden:true,
21205     handler: function(event, toolEl, panel){
21206         // refresh logic
21207     }
21208 },
21209 {
21210     id:'help',
21211     qtip: 'Get Help',
21212     handler: function(event, toolEl, panel){
21213         // whatever
21214     }
21215 }]
21216 </code></pre>
21217      * <p>For the custom id of <tt>'help'</tt> define two relevant css classes with a link to
21218      * a 15x15 image:</p>
21219      * <pre><code>
21220 .x-tool-help {background-image: url(images/help.png);}
21221 .x-tool-help-over {background-image: url(images/help_over.png);}
21222 // if using an image sprite:
21223 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
21224 .x-tool-help-over {background-position:-15px 0;}
21225 </code></pre>
21226      */
21227     /**
21228      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
21229      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
21230 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
21231      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
21232      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
21233      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
21234      * visual indication when hovering over the tool:</p><pre><code>
21235 var win = new Ext.Window({
21236     tools: [{
21237         id: 'download',
21238         href: '/MyPdfDoc.pdf'
21239     }],
21240     toolTemplate: new Ext.XTemplate(
21241         '&lt;tpl if="id==\'download\'">',
21242             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
21243         '&lt;/tpl>',
21244         '&lt;tpl if="id!=\'download\'">',
21245             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
21246         '&lt;/tpl>'
21247     ),
21248     width:500,
21249     height:300,
21250     closeAction:'hide'
21251 });</code></pre>
21252      * <p>Note that the CSS class "x-tool-pdf" should have an associated style rule which provides an
21253      * appropriate background image, something like:</p>
21254     <pre><code>
21255     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
21256     </code></pre>
21257      */
21258     /**
21259      * @cfg {Boolean} hideCollapseTool
21260      * <tt>true</tt> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
21261      * <tt>false</tt> to display it (defaults to <tt>false</tt>).
21262      */
21263     /**
21264      * @cfg {Boolean} titleCollapse
21265      * <tt>true</tt> to allow expanding and collapsing the panel (when <tt>{@link #collapsible} = true</tt>)
21266      * by clicking anywhere in the header bar, <tt>false</tt>) to allow it only by clicking to tool button
21267      * (defaults to <tt>false</tt>)). If this panel is a child item of a border layout also see the
21268      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
21269      * <tt>{@link Ext.layout.BorderLayout.Region#floatable floatable}</tt> config option.
21270      */
21271     /**
21272      * @cfg {Boolean} autoScroll
21273      * <tt>true</tt> to use overflow:'auto' on the panel's body element and show scroll bars automatically when
21274      * necessary, <tt>false</tt> to clip any overflowing content (defaults to <tt>false</tt>).
21275      */
21276     /**
21277      * @cfg {Mixed} floating
21278      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
21279      * configuration property are:</p><div class="mdetail-params"><ul>
21280      * <li><b><tt>false</tt></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
21281      * rendered.</div></li>
21282      * <li><b><tt>true</tt></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
21283      * shimming and shadow).<ul>
21284      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
21285      * panel at negative offsets so that it is hidden.</div>
21286      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
21287      * <i>after</i> render (e.g., <tt>myPanel.setPosition(100,100);</tt>).</div>
21288      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
21289      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
21290      * </ul></div></li>
21291      * <li><b><tt>{@link Ext.Layer object}</tt></b> : <div class="sub-desc">The specified object will be used
21292      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
21293      * </ul></div>
21294      */
21295     /**
21296      * @cfg {Boolean/String} shadow
21297      * <tt>true</tt> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
21298      * panel, <tt>false</tt> to display no shadow (defaults to <tt>'sides'</tt>).  Note that this option
21299      * only applies when <tt>{@link #floating} = true</tt>.
21300      */
21301     /**
21302      * @cfg {Number} shadowOffset
21303      * The number of pixels to offset the shadow if displayed (defaults to <tt>4</tt>). Note that this
21304      * option only applies when <tt>{@link #floating} = true</tt>.
21305      */
21306     /**
21307      * @cfg {Boolean} shim
21308      * <tt>false</tt> to disable the iframe shim in browsers which need one (defaults to <tt>true</tt>).
21309      * Note that this option only applies when <tt>{@link #floating} = true</tt>.
21310      */
21311     /**
21312      * @cfg {String/Object} html
21313      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the panel's body
21314      * content (defaults to ''). The HTML content is added by the Panel's {@link #afterRender} method,
21315      * and so the document will not contain this HTML at the time the {@link #render} event is fired.
21316      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
21317      */
21318     /**
21319      * @cfg {String} contentEl
21320      * <p>Specify the <tt>id</tt> of an existing HTML node to use as the panel's body content
21321      * (defaults to '').</p><div><ul>
21322      * <li><b>Description</b> : <ul>
21323      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the body
21324      * of a new panel (it simply moves the specified DOM element into the body element of the Panel
21325      * <i>when the Panel is rendered</i> to use as the content (it is not going to be the
21326      * actual panel itself).</div>
21327      * </ul></li>
21328      * <li><b>Notes</b> : <ul>
21329      * <div class="sub-desc">The specified HTML Element is appended to the Panel's {@link #body} Element by the
21330      * Panel's {@link #afterRender} method <i>after any configured {@link #html HTML} has
21331      * been inserted</i>, and so the document will not contain this HTML at the time the
21332      * {@link #render} event is fired.</div>
21333      * <div class="sub-desc">The specified HTML element used will not participate in any layout scheme that the
21334      * Panel may use. It's just HTML. Layouts operate on child items.</div>
21335      * <div class="sub-desc">Add either the <tt>x-hidden</tt> or the <tt>x-hide-display</tt> CSS class to
21336      * prevent a brief flicker of the content before it is rendered to the panel.</div>
21337      * </ul></li>
21338      * </ul></div>
21339      */
21340     /**
21341      * @cfg {Object/Array} keys
21342      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
21343      * used to assign custom key handling to this panel (defaults to <tt>null</tt>).
21344      */
21345     /**
21346      * @cfg {Boolean/Object} draggable
21347      * <p><tt>true</tt> to enable dragging of this Panel (defaults to <tt>false</tt>).</p>
21348      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
21349      * in this config instead of <tt>true</tt>. Ext.Panel.DD is an internal, undocumented class which
21350      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
21351      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
21352      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
21353      * <pre><code>
21354 new Ext.Panel({
21355     title: 'Drag me',
21356     x: 100,
21357     y: 100,
21358     renderTo: Ext.getBody(),
21359     floating: true,
21360     frame: true,
21361     width: 400,
21362     height: 200,
21363     draggable: {
21364 //      Config option of Ext.Panel.DD class.
21365 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
21366         insertProxy: false,
21367
21368 //      Called for each mousemove event while dragging the DD object.
21369         onDrag : function(e){
21370 //          Record the x,y position of the drag proxy so that we can
21371 //          position the Panel at end of drag.
21372             var pel = this.proxy.getEl();
21373             this.x = pel.getLeft(true);
21374             this.y = pel.getTop(true);
21375
21376 //          Keep the Shadow aligned if there is one.
21377             var s = this.panel.getEl().shadow;
21378             if (s) {
21379                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
21380             }
21381         },
21382
21383 //      Called on the mouseup event.
21384         endDrag : function(e){
21385             this.panel.setPosition(this.x, this.y);
21386         }
21387     }
21388 }).show();
21389 </code></pre>
21390      */
21391     /**
21392      * @cfg {String} tabTip
21393      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
21394      * the tab of a Ext.Panel which is an item of a {@link Ext.TabPanel}. {@link Ext.QuickTips}.init()
21395      * must be called in order for the tips to render.
21396      */
21397     /**
21398      * @cfg {Boolean} disabled
21399      * Render this panel disabled (default is <tt>false</tt>). An important note when using the disabled
21400      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
21401      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
21402      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
21403      * the disabled state:
21404      * <pre><code>
21405 new Ext.Panel({
21406     ...
21407     listeners: {
21408         'afterlayout': {
21409             fn: function(p){
21410                 p.disable();
21411             },
21412             single: true // important, as many layouts can occur
21413         }
21414     }
21415 });
21416 </code></pre>
21417      */
21418     /**
21419      * @cfg {Boolean} autoHeight
21420      * <tt>true</tt> to use height:'auto', <tt>false</tt> to use fixed height (defaults to <tt>false</tt>).
21421      * <b>Note</b>: Setting <tt>autoHeight:true</tt> means that the browser will manage the panel's height
21422      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
21423      * manages dimensions (<tt>fit</tt>, <tt>border</tt>, etc.) then setting <tt>autoHeight:true</tt>
21424      * can cause issues with scrolling and will not generally work as expected since the panel will take
21425      * on the height of its contents rather than the height required by the Ext layout.
21426      */
21427
21428
21429     /**
21430      * @cfg {String} baseCls
21431      * The base CSS class to apply to this panel's element (defaults to <tt>'x-panel'</tt>).
21432      * <p>Another option available by default is to specify <tt>'x-plain'</tt> which strips all styling
21433      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
21434      * See <tt>{@link #unstyled}</tt> also.</p>
21435      */
21436     baseCls : 'x-panel',
21437     /**
21438      * @cfg {String} collapsedCls
21439      * A CSS class to add to the panel's element after it has been collapsed (defaults to
21440      * <tt>'x-panel-collapsed'</tt>).
21441      */
21442     collapsedCls : 'x-panel-collapsed',
21443     /**
21444      * @cfg {Boolean} maskDisabled
21445      * <tt>true</tt> to mask the panel when it is {@link #disabled}, <tt>false</tt> to not mask it (defaults
21446      * to <tt>true</tt>).  Either way, the panel will always tell its contained elements to disable themselves
21447      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
21448      * disabled.
21449      */
21450     maskDisabled : true,
21451     /**
21452      * @cfg {Boolean} animCollapse
21453      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
21454      * animation (defaults to <tt>true</tt> if the {@link Ext.Fx} class is available, otherwise <tt>false</tt>).
21455      */
21456     animCollapse : Ext.enableFx,
21457     /**
21458      * @cfg {Boolean} headerAsText
21459      * <tt>true</tt> to display the panel <tt>{@link #title}</tt> in the <tt>{@link #header}</tt>,
21460      * <tt>false</tt> to hide it (defaults to <tt>true</tt>).
21461      */
21462     headerAsText : true,
21463     /**
21464      * @cfg {String} buttonAlign
21465      * The alignment of any {@link #buttons} added to this panel.  Valid values are <tt>'right'</tt>,
21466      * <tt>'left'</tt> and <tt>'center'</tt> (defaults to <tt>'right'</tt>).
21467      */
21468     buttonAlign : 'right',
21469     /**
21470      * @cfg {Boolean} collapsed
21471      * <tt>true</tt> to render the panel collapsed, <tt>false</tt> to render it expanded (defaults to
21472      * <tt>false</tt>).
21473      */
21474     collapsed : false,
21475     /**
21476      * @cfg {Boolean} collapseFirst
21477      * <tt>true</tt> to make sure the collapse/expand toggle button always renders first (to the left of)
21478      * any other tools in the panel's title bar, <tt>false</tt> to render it last (defaults to <tt>true</tt>).
21479      */
21480     collapseFirst : true,
21481     /**
21482      * @cfg {Number} minButtonWidth
21483      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <tt>75</tt>)
21484      */
21485     minButtonWidth : 75,
21486     /**
21487      * @cfg {Boolean} unstyled
21488      * Overrides the <tt>{@link #baseCls}</tt> setting to <tt>{@link #baseCls} = 'x-plain'</tt> which renders
21489      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
21490      */
21491     /**
21492      * @cfg {String} elements
21493      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
21494      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
21495      * make sure a structural element is rendered even if not specified at config time (for example, you may want
21496      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
21497      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
21498      * <li><tt>header</tt></li>
21499      * <li><tt>tbar</tt> (top bar)</li>
21500      * <li><tt>body</tt></li>
21501      * <li><tt>bbar</tt> (bottom bar)</li>
21502      * <li><tt>footer</tt></li>
21503      * </ul></div>
21504      * Defaults to '<tt>body</tt>'.
21505      */
21506     elements : 'body',
21507     /**
21508      * @cfg {Boolean} preventBodyReset
21509      * Defaults to <tt>false</tt>.  When set to <tt>true</tt>, an extra css class <tt>'x-panel-normal'</tt>
21510      * will be added to the panel's element, effectively applying css styles suggested by the W3C
21511      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
21512      * footer, etc.).
21513      */
21514     preventBodyReset : false,
21515
21516     // protected - these could be used to customize the behavior of the window,
21517     // but changing them would not be useful without further mofifications and
21518     // could lead to unexpected or undesirable results.
21519     toolTarget : 'header',
21520     collapseEl : 'bwrap',
21521     slideAnchor : 't',
21522     disabledClass : '',
21523
21524     // private, notify box this class will handle heights
21525     deferHeight : true,
21526     // private
21527     expandDefaults: {
21528         duration : 0.25
21529     },
21530     // private
21531     collapseDefaults : {
21532         duration : 0.25
21533     },
21534
21535     // private
21536     initComponent : function(){
21537         Ext.Panel.superclass.initComponent.call(this);
21538
21539         this.addEvents(
21540             /**
21541              * @event bodyresize
21542              * Fires after the Panel has been resized.
21543              * @param {Ext.Panel} p the Panel which has been resized.
21544              * @param {Number} width The Panel's new width.
21545              * @param {Number} height The Panel's new height.
21546              */
21547             'bodyresize',
21548             /**
21549              * @event titlechange
21550              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
21551              * @param {Ext.Panel} p the Panel which has had its title changed.
21552              * @param {String} The new title.
21553              */
21554             'titlechange',
21555             /**
21556              * @event iconchange
21557              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
21558              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
21559              * @param {String} The new icon class.
21560              * @param {String} The old icon class.
21561              */
21562             'iconchange',
21563             /**
21564              * @event collapse
21565              * Fires after the Panel has been collapsed.
21566              * @param {Ext.Panel} p the Panel that has been collapsed.
21567              */
21568             'collapse',
21569             /**
21570              * @event expand
21571              * Fires after the Panel has been expanded.
21572              * @param {Ext.Panel} p The Panel that has been expanded.
21573              */
21574             'expand',
21575             /**
21576              * @event beforecollapse
21577              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
21578              * @param {Ext.Panel} p the Panel being collapsed.
21579              * @param {Boolean} animate True if the collapse is animated, else false.
21580              */
21581             'beforecollapse',
21582             /**
21583              * @event beforeexpand
21584              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
21585              * @param {Ext.Panel} p The Panel being expanded.
21586              * @param {Boolean} animate True if the expand is animated, else false.
21587              */
21588             'beforeexpand',
21589             /**
21590              * @event beforeclose
21591              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
21592              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
21593              * applies to such subclasses.
21594              * A handler can return false to cancel the close.
21595              * @param {Ext.Panel} p The Panel being closed.
21596              */
21597             'beforeclose',
21598             /**
21599              * @event close
21600              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
21601              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
21602              * @param {Ext.Panel} p The Panel that has been closed.
21603              */
21604             'close',
21605             /**
21606              * @event activate
21607              * Fires after the Panel has been visually activated.
21608              * Note that Panels do not directly support being activated, but some Panel subclasses
21609              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
21610              * activate and deactivate events under the control of the TabPanel.
21611              * @param {Ext.Panel} p The Panel that has been activated.
21612              */
21613             'activate',
21614             /**
21615              * @event deactivate
21616              * Fires after the Panel has been visually deactivated.
21617              * Note that Panels do not directly support being deactivated, but some Panel subclasses
21618              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
21619              * activate and deactivate events under the control of the TabPanel.
21620              * @param {Ext.Panel} p The Panel that has been deactivated.
21621              */
21622             'deactivate'
21623         );
21624
21625         if(this.unstyled){
21626             this.baseCls = 'x-plain';
21627         }
21628
21629         // shortcuts
21630         if(this.tbar){
21631             this.elements += ',tbar';
21632             if(Ext.isObject(this.tbar)){
21633                 this.topToolbar = this.tbar;
21634             }
21635             delete this.tbar;
21636         }
21637         if(this.bbar){
21638             this.elements += ',bbar';
21639             if(Ext.isObject(this.bbar)){
21640                 this.bottomToolbar = this.bbar;
21641             }
21642             delete this.bbar;
21643         }
21644
21645         if(this.header === true){
21646             this.elements += ',header';
21647             delete this.header;
21648         }else if(this.headerCfg || (this.title && this.header !== false)){
21649             this.elements += ',header';
21650         }
21651
21652         if(this.footerCfg || this.footer === true){
21653             this.elements += ',footer';
21654             delete this.footer;
21655         }
21656
21657         if(this.buttons){
21658             this.elements += ',footer';
21659             var btns = this.buttons;
21660             /**
21661              * This Panel's Array of buttons as created from the <tt>{@link #buttons}</tt>
21662              * config property. Read only.
21663              * @type Array
21664              * @property buttons
21665              */
21666             this.buttons = [];
21667             for(var i = 0, len = btns.length; i < len; i++) {
21668                 if(btns[i].render){ // button instance
21669                     this.buttons.push(btns[i]);
21670                 }else if(btns[i].xtype){
21671                     this.buttons.push(Ext.create(btns[i], 'button'));
21672                 }else{
21673                     this.addButton(btns[i]);
21674                 }
21675             }
21676         }
21677         if(this.fbar){
21678             this.elements += ',footer';
21679         }
21680         if(this.autoLoad){
21681             this.on('render', this.doAutoLoad, this, {delay:10});
21682         }
21683     },
21684
21685     // private
21686     createElement : function(name, pnode){
21687         if(this[name]){
21688             pnode.appendChild(this[name].dom);
21689             return;
21690         }
21691
21692         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
21693             if(this[name+'Cfg']){
21694                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
21695             }else{
21696                 var el = document.createElement('div');
21697                 el.className = this[name+'Cls'];
21698                 this[name] = Ext.get(pnode.appendChild(el));
21699             }
21700             if(this[name+'CssClass']){
21701                 this[name].addClass(this[name+'CssClass']);
21702             }
21703             if(this[name+'Style']){
21704                 this[name].applyStyles(this[name+'Style']);
21705             }
21706         }
21707     },
21708
21709     // private
21710     onRender : function(ct, position){
21711         Ext.Panel.superclass.onRender.call(this, ct, position);
21712         this.createClasses();
21713
21714         var el = this.el,
21715             d = el.dom,
21716             bw;
21717         el.addClass(this.baseCls);
21718         if(d.firstChild){ // existing markup
21719             this.header = el.down('.'+this.headerCls);
21720             this.bwrap = el.down('.'+this.bwrapCls);
21721             var cp = this.bwrap ? this.bwrap : el;
21722             this.tbar = cp.down('.'+this.tbarCls);
21723             this.body = cp.down('.'+this.bodyCls);
21724             this.bbar = cp.down('.'+this.bbarCls);
21725             this.footer = cp.down('.'+this.footerCls);
21726             this.fromMarkup = true;
21727         }
21728         if (this.preventBodyReset === true) {
21729             el.addClass('x-panel-reset');
21730         }
21731         if(this.cls){
21732             el.addClass(this.cls);
21733         }
21734
21735         if(this.buttons){
21736             this.elements += ',footer';
21737         }
21738
21739         // This block allows for maximum flexibility and performance when using existing markup
21740
21741         // framing requires special markup
21742         if(this.frame){
21743             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
21744
21745             this.createElement('header', d.firstChild.firstChild.firstChild);
21746             this.createElement('bwrap', d);
21747
21748             // append the mid and bottom frame to the bwrap
21749             bw = this.bwrap.dom;
21750             var ml = d.childNodes[1], bl = d.childNodes[2];
21751             bw.appendChild(ml);
21752             bw.appendChild(bl);
21753
21754             var mc = bw.firstChild.firstChild.firstChild;
21755             this.createElement('tbar', mc);
21756             this.createElement('body', mc);
21757             this.createElement('bbar', mc);
21758             this.createElement('footer', bw.lastChild.firstChild.firstChild);
21759
21760             if(!this.footer){
21761                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
21762             }
21763         }else{
21764             this.createElement('header', d);
21765             this.createElement('bwrap', d);
21766
21767             // append the mid and bottom frame to the bwrap
21768             bw = this.bwrap.dom;
21769             this.createElement('tbar', bw);
21770             this.createElement('body', bw);
21771             this.createElement('bbar', bw);
21772             this.createElement('footer', bw);
21773
21774             if(!this.header){
21775                 this.body.addClass(this.bodyCls + '-noheader');
21776                 if(this.tbar){
21777                     this.tbar.addClass(this.tbarCls + '-noheader');
21778                 }
21779             }
21780         }
21781
21782         if(this.padding !== undefined) {
21783             this.body.setStyle('padding', this.body.addUnits(this.padding));
21784         }
21785
21786         if(this.border === false){
21787             this.el.addClass(this.baseCls + '-noborder');
21788             this.body.addClass(this.bodyCls + '-noborder');
21789             if(this.header){
21790                 this.header.addClass(this.headerCls + '-noborder');
21791             }
21792             if(this.footer){
21793                 this.footer.addClass(this.footerCls + '-noborder');
21794             }
21795             if(this.tbar){
21796                 this.tbar.addClass(this.tbarCls + '-noborder');
21797             }
21798             if(this.bbar){
21799                 this.bbar.addClass(this.bbarCls + '-noborder');
21800             }
21801         }
21802
21803         if(this.bodyBorder === false){
21804            this.body.addClass(this.bodyCls + '-noborder');
21805         }
21806
21807         this.bwrap.enableDisplayMode('block');
21808
21809         if(this.header){
21810             this.header.unselectable();
21811
21812             // for tools, we need to wrap any existing header markup
21813             if(this.headerAsText){
21814                 this.header.dom.innerHTML =
21815                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
21816
21817                 if(this.iconCls){
21818                     this.setIconClass(this.iconCls);
21819                 }
21820             }
21821         }
21822
21823         if(this.floating){
21824             this.makeFloating(this.floating);
21825         }
21826
21827         if(this.collapsible){
21828             this.tools = this.tools ? this.tools.slice(0) : [];
21829             if(!this.hideCollapseTool){
21830                 this.tools[this.collapseFirst?'unshift':'push']({
21831                     id: 'toggle',
21832                     handler : this.toggleCollapse,
21833                     scope: this
21834                 });
21835             }
21836             if(this.titleCollapse && this.header){
21837                 this.mon(this.header, 'click', this.toggleCollapse, this);
21838                 this.header.setStyle('cursor', 'pointer');
21839             }
21840         }
21841         if(this.tools){
21842             var ts = this.tools;
21843             this.tools = {};
21844             this.addTool.apply(this, ts);
21845         }else{
21846             this.tools = {};
21847         }
21848
21849         if(this.buttons && this.buttons.length > 0){
21850             this.fbar = new Ext.Toolbar({
21851                 items: this.buttons,
21852                 toolbarCls: 'x-panel-fbar'
21853             });
21854         }
21855         this.toolbars = [];
21856         if(this.fbar){
21857             this.fbar = Ext.create(this.fbar, 'toolbar');
21858             this.fbar.enableOverflow = false;
21859             if(this.fbar.items){
21860                 this.fbar.items.each(function(c){
21861                     c.minWidth = c.minWidth || this.minButtonWidth;
21862                 }, this);
21863             }
21864             this.fbar.toolbarCls = 'x-panel-fbar';
21865
21866             var bct = this.footer.createChild({cls: 'x-panel-btns x-panel-btns-'+this.buttonAlign});
21867             this.fbar.ownerCt = this;
21868             this.fbar.render(bct);
21869             bct.createChild({cls:'x-clear'});
21870             this.toolbars.push(this.fbar);
21871         }
21872
21873         if(this.tbar && this.topToolbar){
21874             if(Ext.isArray(this.topToolbar)){
21875                 this.topToolbar = new Ext.Toolbar(this.topToolbar);
21876             }else if(!this.topToolbar.events){
21877                 this.topToolbar = Ext.create(this.topToolbar, 'toolbar');
21878             }
21879             this.topToolbar.ownerCt = this;
21880             this.topToolbar.render(this.tbar);
21881             this.toolbars.push(this.topToolbar);
21882         }
21883         if(this.bbar && this.bottomToolbar){
21884             if(Ext.isArray(this.bottomToolbar)){
21885                 this.bottomToolbar = new Ext.Toolbar(this.bottomToolbar);
21886             }else if(!this.bottomToolbar.events){
21887                 this.bottomToolbar = Ext.create(this.bottomToolbar, 'toolbar');
21888             }
21889             this.bottomToolbar.ownerCt = this;
21890             this.bottomToolbar.render(this.bbar);
21891             this.toolbars.push(this.bottomToolbar);
21892         }
21893         Ext.each(this.toolbars, function(tb){
21894             tb.on({
21895                 scope: this,
21896                 afterlayout: this.syncHeight,
21897                 remove: this.syncHeight
21898             });
21899         }, this);
21900     },
21901
21902     /**
21903      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
21904      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
21905      * @param {String} cls The new CSS class name
21906      */
21907     setIconClass : function(cls){
21908         var old = this.iconCls;
21909         this.iconCls = cls;
21910         if(this.rendered && this.header){
21911             if(this.frame){
21912                 this.header.addClass('x-panel-icon');
21913                 this.header.replaceClass(old, this.iconCls);
21914             }else{
21915                 var hd = this.header.dom;
21916                 var img = hd.firstChild && String(hd.firstChild.tagName).toLowerCase() == 'img' ? hd.firstChild : null;
21917                 if(img){
21918                     Ext.fly(img).replaceClass(old, this.iconCls);
21919                 }else{
21920                     Ext.DomHelper.insertBefore(hd.firstChild, {
21921                         tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
21922                     });
21923                  }
21924             }
21925         }
21926         this.fireEvent('iconchange', this, cls, old);
21927     },
21928
21929     // private
21930     makeFloating : function(cfg){
21931         this.floating = true;
21932         this.el = new Ext.Layer(
21933             Ext.isObject(cfg) ? cfg : {
21934                 shadow: this.shadow !== undefined ? this.shadow : 'sides',
21935                 shadowOffset: this.shadowOffset,
21936                 constrain:false,
21937                 shim: this.shim === false ? false : undefined
21938             }, this.el
21939         );
21940     },
21941
21942     /**
21943      * Returns the {@link Ext.Toolbar toolbar} from the top (<tt>{@link #tbar}</tt>) section of the panel.
21944      * @return {Ext.Toolbar} The toolbar
21945      */
21946     getTopToolbar : function(){
21947         return this.topToolbar;
21948     },
21949
21950     /**
21951      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<tt>{@link #bbar}</tt>) section of the panel.
21952      * @return {Ext.Toolbar} The toolbar
21953      */
21954     getBottomToolbar : function(){
21955         return this.bottomToolbar;
21956     },
21957
21958     /**
21959      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
21960      * approach is to add buttons via the {@link #buttons} config.
21961      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
21962      * button config, an object will be treated as a button config object.
21963      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
21964      * @param {Object} scope The scope to use for the button handler function
21965      * @return {Ext.Button} The button that was added
21966      */
21967     addButton : function(config, handler, scope){
21968         var bc = {
21969             handler: handler,
21970             scope: scope,
21971             minWidth: this.minButtonWidth,
21972             hideParent:true
21973         };
21974         if(typeof config == "string"){
21975             bc.text = config;
21976         }else{
21977             Ext.apply(bc, config);
21978         }
21979         var btn = new Ext.Button(bc);
21980         if(!this.buttons){
21981             this.buttons = [];
21982         }
21983         this.buttons.push(btn);
21984         return btn;
21985     },
21986
21987     // private
21988     addTool : function(){
21989         if(!this[this.toolTarget]) { // no where to render tools!
21990             return;
21991         }
21992         if(!this.toolTemplate){
21993             // initialize the global tool template on first use
21994             var tt = new Ext.Template(
21995                  '<div class="x-tool x-tool-{id}">&#160;</div>'
21996             );
21997             tt.disableFormats = true;
21998             tt.compile();
21999             Ext.Panel.prototype.toolTemplate = tt;
22000         }
22001         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
22002             var tc = a[i];
22003             if(!this.tools[tc.id]){
22004                 var overCls = 'x-tool-'+tc.id+'-over';
22005                 var t = this.toolTemplate.insertFirst((tc.align !== 'left') ? this[this.toolTarget] : this[this.toolTarget].child('span'), tc, true);
22006                 this.tools[tc.id] = t;
22007                 t.enableDisplayMode('block');
22008                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
22009                 if(tc.on){
22010                     this.mon(t, tc.on);
22011                 }
22012                 if(tc.hidden){
22013                     t.hide();
22014                 }
22015                 if(tc.qtip){
22016                     if(Ext.isObject(tc.qtip)){
22017                         Ext.QuickTips.register(Ext.apply({
22018                               target: t.id
22019                         }, tc.qtip));
22020                     } else {
22021                         t.dom.qtip = tc.qtip;
22022                     }
22023                 }
22024                 t.addClassOnOver(overCls);
22025             }
22026         }
22027     },
22028
22029     onLayout : function(){
22030         if(this.toolbars.length > 0){
22031             this.duringLayout = true;
22032             Ext.each(this.toolbars, function(tb){
22033                 tb.doLayout();
22034             });
22035             delete this.duringLayout;
22036             this.syncHeight();
22037         }
22038     },
22039
22040     syncHeight : function(){
22041         if(!(this.autoHeight || this.duringLayout)){
22042             var last = this.lastSize;
22043             if(last && !Ext.isEmpty(last.height)){
22044                 var old = last.height, h = this.el.getHeight();
22045                 if(old != 'auto' && old != h){
22046                     var bd = this.body, bdh = bd.getHeight();
22047                     h = Math.max(bdh + old - h, 0);
22048                     if(bdh > 0 && bdh != h){
22049                         bd.setHeight(h);
22050                         if(Ext.isIE && h <= 0){
22051                             return;
22052                         }
22053                         var sz = bd.getSize();
22054                         this.fireEvent('bodyresize', sz.width, sz.height);
22055                     }
22056                 }
22057             }
22058         }
22059     },
22060
22061     // private
22062     onShow : function(){
22063         if(this.floating){
22064             return this.el.show();
22065         }
22066         Ext.Panel.superclass.onShow.call(this);
22067     },
22068
22069     // private
22070     onHide : function(){
22071         if(this.floating){
22072             return this.el.hide();
22073         }
22074         Ext.Panel.superclass.onHide.call(this);
22075     },
22076
22077     // private
22078     createToolHandler : function(t, tc, overCls, panel){
22079         return function(e){
22080             t.removeClass(overCls);
22081             if(tc.stopEvent !== false){
22082                 e.stopEvent();
22083             }
22084             if(tc.handler){
22085                 tc.handler.call(tc.scope || t, e, t, panel, tc);
22086             }
22087         };
22088     },
22089
22090     // private
22091     afterRender : function(){
22092         if(this.floating && !this.hidden){
22093             this.el.show();
22094         }
22095         if(this.title){
22096             this.setTitle(this.title);
22097         }
22098         this.setAutoScroll();
22099         if(this.html){
22100             this.body.update(Ext.isObject(this.html) ?
22101                              Ext.DomHelper.markup(this.html) :
22102                              this.html);
22103             delete this.html;
22104         }
22105         if(this.contentEl){
22106             var ce = Ext.getDom(this.contentEl);
22107             Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
22108             this.body.dom.appendChild(ce);
22109         }
22110         if(this.collapsed){
22111             this.collapsed = false;
22112             this.collapse(false);
22113         }
22114         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
22115         this.initEvents();
22116     },
22117
22118     // private
22119     setAutoScroll : function(){
22120         if(this.rendered && this.autoScroll){
22121             var el = this.body || this.el;
22122             if(el){
22123                 el.setOverflow('auto');
22124             }
22125         }
22126     },
22127
22128     // private
22129     getKeyMap : function(){
22130         if(!this.keyMap){
22131             this.keyMap = new Ext.KeyMap(this.el, this.keys);
22132         }
22133         return this.keyMap;
22134     },
22135
22136     // private
22137     initEvents : function(){
22138         if(this.keys){
22139             this.getKeyMap();
22140         }
22141         if(this.draggable){
22142             this.initDraggable();
22143         }
22144     },
22145
22146     // private
22147     initDraggable : function(){
22148         /**
22149          * <p>If this Panel is configured {@link #draggable}, this property will contain
22150          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
22151          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
22152          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
22153          * @type Ext.dd.DragSource.
22154          * @property dd
22155          */
22156         this.dd = new Ext.Panel.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);
22157     },
22158
22159     // private
22160     beforeEffect : function(){
22161         if(this.floating){
22162             this.el.beforeAction();
22163         }
22164         this.el.addClass('x-panel-animated');
22165     },
22166
22167     // private
22168     afterEffect : function(){
22169         this.syncShadow();
22170         this.el.removeClass('x-panel-animated');
22171     },
22172
22173     // private - wraps up an animation param with internal callbacks
22174     createEffect : function(a, cb, scope){
22175         var o = {
22176             scope:scope,
22177             block:true
22178         };
22179         if(a === true){
22180             o.callback = cb;
22181             return o;
22182         }else if(!a.callback){
22183             o.callback = cb;
22184         }else { // wrap it up
22185             o.callback = function(){
22186                 cb.call(scope);
22187                 Ext.callback(a.callback, a.scope);
22188             };
22189         }
22190         return Ext.applyIf(o, a);
22191     },
22192
22193     /**
22194      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
22195      * cancel the collapse action if it returns false.
22196      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22197      * {@link #animCollapse} panel config)
22198      * @return {Ext.Panel} this
22199      */
22200     collapse : function(animate){
22201         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
22202             return;
22203         }
22204         var doAnim = animate === true || (animate !== false && this.animCollapse);
22205         this.beforeEffect();
22206         this.onCollapse(doAnim, animate);
22207         return this;
22208     },
22209
22210     // private
22211     onCollapse : function(doAnim, animArg){
22212         if(doAnim){
22213             this[this.collapseEl].slideOut(this.slideAnchor,
22214                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
22215                         this.collapseDefaults));
22216         }else{
22217             this[this.collapseEl].hide();
22218             this.afterCollapse();
22219         }
22220     },
22221
22222     // private
22223     afterCollapse : function(){
22224         this.collapsed = true;
22225         this.el.addClass(this.collapsedCls);
22226         this.afterEffect();
22227         this.fireEvent('collapse', this);
22228     },
22229
22230     /**
22231      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
22232      * cancel the expand action if it returns false.
22233      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22234      * {@link #animCollapse} panel config)
22235      * @return {Ext.Panel} this
22236      */
22237     expand : function(animate){
22238         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
22239             return;
22240         }
22241         var doAnim = animate === true || (animate !== false && this.animCollapse);
22242         this.el.removeClass(this.collapsedCls);
22243         this.beforeEffect();
22244         this.onExpand(doAnim, animate);
22245         return this;
22246     },
22247
22248     // private
22249     onExpand : function(doAnim, animArg){
22250         if(doAnim){
22251             this[this.collapseEl].slideIn(this.slideAnchor,
22252                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
22253                         this.expandDefaults));
22254         }else{
22255             this[this.collapseEl].show();
22256             this.afterExpand();
22257         }
22258     },
22259
22260     // private
22261     afterExpand : function(){
22262         this.collapsed = false;
22263         this.afterEffect();
22264         if(this.deferLayout !== undefined){
22265             this.doLayout(true);
22266         }
22267         this.fireEvent('expand', this);
22268     },
22269
22270     /**
22271      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
22272      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22273      * {@link #animCollapse} panel config)
22274      * @return {Ext.Panel} this
22275      */
22276     toggleCollapse : function(animate){
22277         this[this.collapsed ? 'expand' : 'collapse'](animate);
22278         return this;
22279     },
22280
22281     // private
22282     onDisable : function(){
22283         if(this.rendered && this.maskDisabled){
22284             this.el.mask();
22285         }
22286         Ext.Panel.superclass.onDisable.call(this);
22287     },
22288
22289     // private
22290     onEnable : function(){
22291         if(this.rendered && this.maskDisabled){
22292             this.el.unmask();
22293         }
22294         Ext.Panel.superclass.onEnable.call(this);
22295     },
22296
22297     // private
22298     onResize : function(w, h){
22299         if(w !== undefined || h !== undefined){
22300             if(!this.collapsed){
22301                 if(typeof w == 'number'){
22302                     w = this.adjustBodyWidth(w - this.getFrameWidth());
22303                     if(this.tbar){
22304                         this.tbar.setWidth(w);
22305                         if(this.topToolbar){
22306                             this.topToolbar.setSize(w);
22307                         }
22308                     }
22309                     if(this.bbar){
22310                         this.bbar.setWidth(w);
22311                         if(this.bottomToolbar){
22312                             this.bottomToolbar.setSize(w);
22313                         }
22314                     }
22315                     if(this.fbar){
22316                         var f = this.fbar,
22317                             fWidth = 1,
22318                             strict = Ext.isStrict;
22319                         if(this.buttonAlign == 'left'){
22320                            fWidth = w - f.container.getFrameWidth('lr');
22321                         }else{
22322                             //center/right alignment off in webkit
22323                             if(Ext.isIE || Ext.isWebKit){
22324                                 //center alignment ok on webkit.
22325                                 //right broken in both, center on IE
22326                                 if(!(this.buttonAlign == 'center' && Ext.isWebKit) && (!strict || (!Ext.isIE8 && strict))){
22327                                     (function(){
22328                                         f.setWidth(f.getEl().child('.x-toolbar-ct').getWidth());
22329                                     }).defer(1);
22330                                 }else{
22331                                     fWidth = 'auto';
22332                                 }
22333                             }else{
22334                                 fWidth = 'auto';
22335                             }
22336                         }
22337                         f.setWidth(fWidth);
22338                     }
22339                     this.body.setWidth(w);
22340                 }else if(w == 'auto'){
22341                     this.body.setWidth(w);
22342                 }
22343
22344                 if(typeof h == 'number'){
22345                     h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight()));
22346                     this.body.setHeight(h);
22347                 }else if(h == 'auto'){
22348                     this.body.setHeight(h);
22349                 }
22350
22351                 if(this.disabled && this.el._mask){
22352                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
22353                 }
22354             }else{
22355                 this.queuedBodySize = {width: w, height: h};
22356                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
22357                     this.queuedExpand = true;
22358                     this.on('expand', function(){
22359                         delete this.queuedExpand;
22360                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
22361                         this.doLayout();
22362                     }, this, {single:true});
22363                 }
22364             }
22365             this.fireEvent('bodyresize', this, w, h);
22366         }
22367         this.syncShadow();
22368     },
22369
22370     // private
22371     adjustBodyHeight : function(h){
22372         return h;
22373     },
22374
22375     // private
22376     adjustBodyWidth : function(w){
22377         return w;
22378     },
22379
22380     // private
22381     onPosition : function(){
22382         this.syncShadow();
22383     },
22384
22385     /**
22386      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
22387      * retrieve the body width see {@link #getInnerWidth}.
22388      * @return {Number} The frame width
22389      */
22390     getFrameWidth : function(){
22391         var w = this.el.getFrameWidth('lr')+this.bwrap.getFrameWidth('lr');
22392
22393         if(this.frame){
22394             var l = this.bwrap.dom.firstChild;
22395             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
22396             var mc = this.bwrap.dom.firstChild.firstChild.firstChild;
22397             w += Ext.fly(mc).getFrameWidth('lr');
22398         }
22399         return w;
22400     },
22401
22402     /**
22403      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
22404      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
22405      * @return {Number} The frame height
22406      */
22407     getFrameHeight : function(){
22408         var h  = this.el.getFrameWidth('tb')+this.bwrap.getFrameWidth('tb');
22409         h += (this.tbar ? this.tbar.getHeight() : 0) +
22410              (this.bbar ? this.bbar.getHeight() : 0);
22411
22412         if(this.frame){
22413             var hd = this.el.dom.firstChild;
22414             var ft = this.bwrap.dom.lastChild;
22415             h += (hd.offsetHeight + ft.offsetHeight);
22416             var mc = this.bwrap.dom.firstChild.firstChild.firstChild;
22417             h += Ext.fly(mc).getFrameWidth('tb');
22418         }else{
22419             h += (this.header ? this.header.getHeight() : 0) +
22420                 (this.footer ? this.footer.getHeight() : 0);
22421         }
22422         return h;
22423     },
22424
22425     /**
22426      * Returns the width in pixels of the body element (not including the width of any framing elements).
22427      * For the frame width see {@link #getFrameWidth}.
22428      * @return {Number} The body width
22429      */
22430     getInnerWidth : function(){
22431         return this.getSize().width - this.getFrameWidth();
22432     },
22433
22434     /**
22435      * Returns the height in pixels of the body element (not including the height of any framing elements).
22436      * For the frame height see {@link #getFrameHeight}.
22437      * @return {Number} The body height
22438      */
22439     getInnerHeight : function(){
22440         return this.getSize().height - this.getFrameHeight();
22441     },
22442
22443     // private
22444     syncShadow : function(){
22445         if(this.floating){
22446             this.el.sync(true);
22447         }
22448     },
22449
22450     // private
22451     getLayoutTarget : function(){
22452         return this.body;
22453     },
22454
22455     /**
22456      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
22457      * <p>In order to be able to set the title, a header element must have been created
22458      * for the Panel. This is triggered either by configuring the Panel with a non-blank <tt>{@link #title}</tt>,
22459      * or configuring it with <tt><b>{@link #header}: true</b></tt>.</p>
22460      * @param {String} title The title text to set
22461      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
22462      */
22463     setTitle : function(title, iconCls){
22464         this.title = title;
22465         if(this.header && this.headerAsText){
22466             this.header.child('span').update(title);
22467         }
22468         if(iconCls){
22469             this.setIconClass(iconCls);
22470         }
22471         this.fireEvent('titlechange', this, title);
22472         return this;
22473     },
22474
22475     /**
22476      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
22477      * @return {Ext.Updater} The Updater
22478      */
22479     getUpdater : function(){
22480         return this.body.getUpdater();
22481     },
22482
22483      /**
22484      * Loads this content panel immediately with content returned from an XHR call.
22485      * @param {Object/String/Function} config A config object containing any of the following options:
22486 <pre><code>
22487 panel.load({
22488     url: "your-url.php",
22489     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
22490     callback: yourFunction,
22491     scope: yourObject, // optional scope for the callback
22492     discardUrl: false,
22493     nocache: false,
22494     text: "Loading...",
22495     timeout: 30,
22496     scripts: false
22497 });
22498 </code></pre>
22499      * The only required property is url. The optional properties nocache, text and scripts
22500      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
22501      * associated property on this panel Updater instance.
22502      * @return {Ext.Panel} this
22503      */
22504     load : function(){
22505         var um = this.body.getUpdater();
22506         um.update.apply(um, arguments);
22507         return this;
22508     },
22509
22510     // private
22511     beforeDestroy : function(){
22512         if(this.header){
22513             this.header.removeAllListeners();
22514             if(this.headerAsText){
22515                 Ext.Element.uncache(this.header.child('span'));
22516             }
22517         }
22518         Ext.Element.uncache(
22519             this.header,
22520             this.tbar,
22521             this.bbar,
22522             this.footer,
22523             this.body,
22524             this.bwrap
22525         );
22526         if(this.tools){
22527             for(var k in this.tools){
22528                 Ext.destroy(this.tools[k]);
22529             }
22530         }
22531         if(this.buttons){
22532             for(var b in this.buttons){
22533                 Ext.destroy(this.buttons[b]);
22534             }
22535         }
22536         Ext.destroy(this.toolbars);
22537         Ext.Panel.superclass.beforeDestroy.call(this);
22538     },
22539
22540     // private
22541     createClasses : function(){
22542         this.headerCls = this.baseCls + '-header';
22543         this.headerTextCls = this.baseCls + '-header-text';
22544         this.bwrapCls = this.baseCls + '-bwrap';
22545         this.tbarCls = this.baseCls + '-tbar';
22546         this.bodyCls = this.baseCls + '-body';
22547         this.bbarCls = this.baseCls + '-bbar';
22548         this.footerCls = this.baseCls + '-footer';
22549     },
22550
22551     // private
22552     createGhost : function(cls, useShim, appendTo){
22553         var el = document.createElement('div');
22554         el.className = 'x-panel-ghost ' + (cls ? cls : '');
22555         if(this.header){
22556             el.appendChild(this.el.dom.firstChild.cloneNode(true));
22557         }
22558         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
22559         el.style.width = this.el.dom.offsetWidth + 'px';;
22560         if(!appendTo){
22561             this.container.dom.appendChild(el);
22562         }else{
22563             Ext.getDom(appendTo).appendChild(el);
22564         }
22565         if(useShim !== false && this.el.useShim !== false){
22566             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
22567             layer.show();
22568             return layer;
22569         }else{
22570             return new Ext.Element(el);
22571         }
22572     },
22573
22574     // private
22575     doAutoLoad : function(){
22576         var u = this.body.getUpdater();
22577         if(this.renderer){
22578             u.setRenderer(this.renderer);
22579         }
22580         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
22581     },
22582
22583     /**
22584      * Retrieve a tool by id.
22585      * @param {String} id
22586      * @return {Object} tool
22587      */
22588     getTool : function(id) {
22589         return this.tools[id];
22590     }
22591
22592 /**
22593  * @cfg {String} autoEl @hide
22594  */
22595 });
22596 Ext.reg('panel', Ext.Panel);
22597 /**
22598  * @class Ext.Editor
22599  * @extends Ext.Component
22600  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
22601  * @constructor
22602  * Create a new Editor
22603  * @param {Object} config The config object
22604  * @xtype editor
22605  */
22606 Ext.Editor = function(field, config){
22607     if(field.field){
22608         this.field = Ext.create(field.field, 'textfield');
22609         config = Ext.apply({}, field); // copy so we don't disturb original config
22610         delete config.field;
22611     }else{
22612         this.field = field;
22613     }
22614     Ext.Editor.superclass.constructor.call(this, config);
22615 };
22616
22617 Ext.extend(Ext.Editor, Ext.Component, {
22618     /**
22619     * @cfg {Ext.form.Field} field
22620     * The Field object (or descendant) or config object for field
22621     */
22622     /**
22623      * @cfg {Boolean} allowBlur
22624      * True to {@link #completeEdit complete the editing process} if in edit mode when the
22625      * field is blurred. Defaults to <tt>false</tt>.
22626      */
22627     /**
22628      * @cfg {Boolean/String} autoSize
22629      * True for the editor to automatically adopt the size of the element being edited, "width" to adopt the width only,
22630      * or "height" to adopt the height only (defaults to false)
22631      */
22632     /**
22633      * @cfg {Boolean} revertInvalid
22634      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
22635      * validation fails (defaults to true)
22636      */
22637     /**
22638      * @cfg {Boolean} ignoreNoChange
22639      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
22640      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
22641      * will never be ignored.
22642      */
22643     /**
22644      * @cfg {Boolean} hideEl
22645      * False to keep the bound element visible while the editor is displayed (defaults to true)
22646      */
22647     /**
22648      * @cfg {Mixed} value
22649      * The data value of the underlying field (defaults to "")
22650      */
22651     value : "",
22652     /**
22653      * @cfg {String} alignment
22654      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
22655      */
22656     alignment: "c-c?",
22657     /**
22658      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
22659      * for bottom-right shadow (defaults to "frame")
22660      */
22661     shadow : "frame",
22662     /**
22663      * @cfg {Boolean} constrain True to constrain the editor to the viewport
22664      */
22665     constrain : false,
22666     /**
22667      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
22668      */
22669     swallowKeys : true,
22670     /**
22671      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
22672      */
22673     completeOnEnter : false,
22674     /**
22675      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
22676      */
22677     cancelOnEsc : false,
22678     /**
22679      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
22680      */
22681     updateEl : false,
22682
22683     initComponent : function(){
22684         Ext.Editor.superclass.initComponent.call(this);
22685         this.addEvents(
22686             /**
22687              * @event beforestartedit
22688              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
22689              * false from the handler of this event.
22690              * @param {Editor} this
22691              * @param {Ext.Element} boundEl The underlying element bound to this editor
22692              * @param {Mixed} value The field value being set
22693              */
22694             "beforestartedit",
22695             /**
22696              * @event startedit
22697              * Fires when this editor is displayed
22698              * @param {Ext.Element} boundEl The underlying element bound to this editor
22699              * @param {Mixed} value The starting field value
22700              */
22701             "startedit",
22702             /**
22703              * @event beforecomplete
22704              * Fires after a change has been made to the field, but before the change is reflected in the underlying
22705              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
22706              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
22707              * event will not fire since no edit actually occurred.
22708              * @param {Editor} this
22709              * @param {Mixed} value The current field value
22710              * @param {Mixed} startValue The original field value
22711              */
22712             "beforecomplete",
22713             /**
22714              * @event complete
22715              * Fires after editing is complete and any changed value has been written to the underlying field.
22716              * @param {Editor} this
22717              * @param {Mixed} value The current field value
22718              * @param {Mixed} startValue The original field value
22719              */
22720             "complete",
22721             /**
22722              * @event canceledit
22723              * Fires after editing has been canceled and the editor's value has been reset.
22724              * @param {Editor} this
22725              * @param {Mixed} value The user-entered field value that was discarded
22726              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
22727              */
22728             "canceledit",
22729             /**
22730              * @event specialkey
22731              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
22732              * {@link Ext.EventObject#getKey} to determine which key was pressed.
22733              * @param {Ext.form.Field} this
22734              * @param {Ext.EventObject} e The event object
22735              */
22736             "specialkey"
22737         );
22738     },
22739
22740     // private
22741     onRender : function(ct, position){
22742         this.el = new Ext.Layer({
22743             shadow: this.shadow,
22744             cls: "x-editor",
22745             parentEl : ct,
22746             shim : this.shim,
22747             shadowOffset: this.shadowOffset || 4,
22748             id: this.id,
22749             constrain: this.constrain
22750         });
22751         if(this.zIndex){
22752             this.el.setZIndex(this.zIndex);
22753         }
22754         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
22755         if(this.field.msgTarget != 'title'){
22756             this.field.msgTarget = 'qtip';
22757         }
22758         this.field.inEditor = true;
22759         this.field.render(this.el);
22760         if(Ext.isGecko){
22761             this.field.el.dom.setAttribute('autocomplete', 'off');
22762         }
22763         this.mon(this.field, "specialkey", this.onSpecialKey, this);
22764         if(this.swallowKeys){
22765             this.field.el.swallowEvent(['keydown','keypress']);
22766         }
22767         this.field.show();
22768         this.mon(this.field, "blur", this.onBlur, this);
22769         if(this.field.grow){
22770                 this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
22771         }
22772     },
22773
22774     // private
22775     onSpecialKey : function(field, e){
22776         var key = e.getKey();
22777         if(this.completeOnEnter && key == e.ENTER){
22778             e.stopEvent();
22779             this.completeEdit();
22780         }else if(this.cancelOnEsc && key == e.ESC){
22781             this.cancelEdit();
22782         }else{
22783             this.fireEvent('specialkey', field, e);
22784         }
22785         if(this.field.triggerBlur && (key == e.ENTER || key == e.ESC || key == e.TAB)){
22786             this.field.triggerBlur();
22787         }
22788     },
22789
22790     /**
22791      * Starts the editing process and shows the editor.
22792      * @param {Mixed} el The element to edit
22793      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
22794       * to the innerHTML of el.
22795      */
22796     startEdit : function(el, value){
22797         if(this.editing){
22798             this.completeEdit();
22799         }
22800         this.boundEl = Ext.get(el);
22801         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
22802         if(!this.rendered){
22803             this.render(this.parentEl || document.body);
22804         }
22805         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
22806             return;
22807         }
22808         this.startValue = v;
22809         this.field.setValue(v);
22810         this.doAutoSize();
22811         this.el.alignTo(this.boundEl, this.alignment);
22812         this.editing = true;
22813         this.show();
22814     },
22815
22816     // private
22817     doAutoSize : function(){
22818         if(this.autoSize){
22819             var sz = this.boundEl.getSize();
22820             switch(this.autoSize){
22821                 case "width":
22822                     this.setSize(sz.width,  "");
22823                 break;
22824                 case "height":
22825                     this.setSize("",  sz.height);
22826                 break;
22827                 default:
22828                     this.setSize(sz.width,  sz.height);
22829             }
22830         }
22831     },
22832
22833     /**
22834      * Sets the height and width of this editor.
22835      * @param {Number} width The new width
22836      * @param {Number} height The new height
22837      */
22838     setSize : function(w, h){
22839         delete this.field.lastSize;
22840         this.field.setSize(w, h);
22841         if(this.el){
22842             if(Ext.isGecko2 || Ext.isOpera){
22843                 // prevent layer scrollbars
22844                 this.el.setSize(w, h);
22845             }
22846             this.el.sync();
22847         }
22848     },
22849
22850     /**
22851      * Realigns the editor to the bound field based on the current alignment config value.
22852      */
22853     realign : function(){
22854         this.el.alignTo(this.boundEl, this.alignment);
22855     },
22856
22857     /**
22858      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
22859      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
22860      */
22861     completeEdit : function(remainVisible){
22862         if(!this.editing){
22863             return;
22864         }
22865         var v = this.getValue();
22866         if(!this.field.isValid()){
22867             if(this.revertInvalid !== false){
22868                 this.cancelEdit(remainVisible);
22869             }
22870             return;
22871         }
22872         if(String(v) === String(this.startValue) && this.ignoreNoChange){
22873             this.hideEdit(remainVisible);
22874             return;
22875         }
22876         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
22877             v = this.getValue();
22878             if(this.updateEl && this.boundEl){
22879                 this.boundEl.update(v);
22880             }
22881             this.hideEdit(remainVisible);
22882             this.fireEvent("complete", this, v, this.startValue);
22883         }
22884     },
22885
22886     // private
22887     onShow : function(){
22888         this.el.show();
22889         if(this.hideEl !== false){
22890             this.boundEl.hide();
22891         }
22892         this.field.show();
22893         if(Ext.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
22894             this.fixIEFocus = true;
22895             this.deferredFocus.defer(50, this);
22896         }else{
22897             this.field.focus();
22898         }
22899         this.fireEvent("startedit", this.boundEl, this.startValue);
22900     },
22901
22902     deferredFocus : function(){
22903         if(this.editing){
22904             this.field.focus();
22905         }
22906     },
22907
22908     /**
22909      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
22910      * reverted to the original starting value.
22911      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
22912      * cancel (defaults to false)
22913      */
22914     cancelEdit : function(remainVisible){
22915         if(this.editing){
22916             var v = this.getValue();
22917             this.setValue(this.startValue);
22918             this.hideEdit(remainVisible);
22919             this.fireEvent("canceledit", this, v, this.startValue);
22920         }
22921     },
22922     
22923     // private
22924     hideEdit: function(remainVisible){
22925         if(remainVisible !== true){
22926             this.editing = false;
22927             this.hide();
22928         }
22929     },
22930
22931     // private
22932     onBlur : function(){
22933         if(this.allowBlur !== true && this.editing){
22934             this.completeEdit();
22935         }
22936     },
22937
22938     // private
22939     onHide : function(){
22940         if(this.editing){
22941             this.completeEdit();
22942             return;
22943         }
22944         this.field.blur();
22945         if(this.field.collapse){
22946             this.field.collapse();
22947         }
22948         this.el.hide();
22949         if(this.hideEl !== false){
22950             this.boundEl.show();
22951         }
22952     },
22953
22954     /**
22955      * Sets the data value of the editor
22956      * @param {Mixed} value Any valid value supported by the underlying field
22957      */
22958     setValue : function(v){
22959         this.field.setValue(v);
22960     },
22961
22962     /**
22963      * Gets the data value of the editor
22964      * @return {Mixed} The data value
22965      */
22966     getValue : function(){
22967         return this.field.getValue();
22968     },
22969
22970     beforeDestroy : function(){
22971         Ext.destroy(this.field);
22972         this.field = null;
22973     }
22974 });
22975 Ext.reg('editor', Ext.Editor);/**
22976  * @class Ext.ColorPalette
22977  * @extends Ext.Component
22978  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
22979  * Here's an example of typical usage:
22980  * <pre><code>
22981 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
22982 cp.render('my-div');
22983
22984 cp.on('select', function(palette, selColor){
22985     // do something with selColor
22986 });
22987 </code></pre>
22988  * @constructor
22989  * Create a new ColorPalette
22990  * @param {Object} config The config object
22991  * @xtype colorpalette
22992  */
22993 Ext.ColorPalette = function(config){
22994     Ext.ColorPalette.superclass.constructor.call(this, config);
22995     this.addEvents(
22996         /**
22997              * @event select
22998              * Fires when a color is selected
22999              * @param {ColorPalette} this
23000              * @param {String} color The 6-digit color hex code (without the # symbol)
23001              */
23002         'select'
23003     );
23004
23005     if(this.handler){
23006         this.on("select", this.handler, this.scope, true);
23007     }
23008 };
23009 Ext.extend(Ext.ColorPalette, Ext.Component, {
23010         /**
23011          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
23012          */
23013     /**
23014      * @cfg {String} itemCls
23015      * The CSS class to apply to the containing element (defaults to "x-color-palette")
23016      */
23017     itemCls : "x-color-palette",
23018     /**
23019      * @cfg {String} value
23020      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
23021      * the hex codes are case-sensitive.
23022      */
23023     value : null,
23024     clickEvent:'click',
23025     // private
23026     ctype: "Ext.ColorPalette",
23027
23028     /**
23029      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
23030      */
23031     allowReselect : false,
23032
23033     /**
23034      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
23035      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
23036      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
23037      * of colors with the width setting until the box is symmetrical.</p>
23038      * <p>You can override individual colors if needed:</p>
23039      * <pre><code>
23040 var cp = new Ext.ColorPalette();
23041 cp.colors[0] = "FF0000";  // change the first box to red
23042 </code></pre>
23043
23044 Or you can provide a custom array of your own for complete control:
23045 <pre><code>
23046 var cp = new Ext.ColorPalette();
23047 cp.colors = ["000000", "993300", "333300"];
23048 </code></pre>
23049      * @type Array
23050      */
23051     colors : [
23052         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
23053         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
23054         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
23055         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
23056         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
23057     ],
23058
23059     // private
23060     onRender : function(container, position){
23061         var t = this.tpl || new Ext.XTemplate(
23062             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
23063         );
23064         var el = document.createElement("div");
23065         el.id = this.getId();
23066         el.className = this.itemCls;
23067         t.overwrite(el, this.colors);
23068         container.dom.insertBefore(el, position);
23069         this.el = Ext.get(el);
23070         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
23071         if(this.clickEvent != 'click'){
23072                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
23073         }
23074     },
23075
23076     // private
23077     afterRender : function(){
23078         Ext.ColorPalette.superclass.afterRender.call(this);
23079         if(this.value){
23080             var s = this.value;
23081             this.value = null;
23082             this.select(s);
23083         }
23084     },
23085
23086     // private
23087     handleClick : function(e, t){
23088         e.preventDefault();
23089         if(!this.disabled){
23090             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
23091             this.select(c.toUpperCase());
23092         }
23093     },
23094
23095     /**
23096      * Selects the specified color in the palette (fires the {@link #select} event)
23097      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
23098      */
23099     select : function(color){
23100         color = color.replace("#", "");
23101         if(color != this.value || this.allowReselect){
23102             var el = this.el;
23103             if(this.value){
23104                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
23105             }
23106             el.child("a.color-"+color).addClass("x-color-palette-sel");
23107             this.value = color;
23108             this.fireEvent("select", this, color);
23109         }
23110     }
23111
23112     /**
23113      * @cfg {String} autoEl @hide
23114      */
23115 });
23116 Ext.reg('colorpalette', Ext.ColorPalette);/**\r
23117  * @class Ext.DatePicker\r
23118  * @extends Ext.Component\r
23119  * Simple date picker class.\r
23120  * @constructor\r
23121  * Create a new DatePicker\r
23122  * @param {Object} config The config object\r
23123  * @xtype datepicker\r
23124  */\r
23125 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {\r
23126     /**\r
23127      * @cfg {String} todayText\r
23128      * The text to display on the button that selects the current date (defaults to <tt>'Today'</tt>)\r
23129      */\r
23130     todayText : 'Today',\r
23131     /**\r
23132      * @cfg {String} okText\r
23133      * The text to display on the ok button (defaults to <tt>'&#160;OK&#160;'</tt> to give the user extra clicking room)\r
23134      */\r
23135     okText : '&#160;OK&#160;',\r
23136     /**\r
23137      * @cfg {String} cancelText\r
23138      * The text to display on the cancel button (defaults to <tt>'Cancel'</tt>)\r
23139      */\r
23140     cancelText : 'Cancel',\r
23141     /**\r
23142      * @cfg {String} todayTip\r
23143      * The tooltip to display for the button that selects the current date (defaults to <tt>'{current date} (Spacebar)'</tt>)\r
23144      */\r
23145     todayTip : '{0} (Spacebar)',\r
23146     /**\r
23147      * @cfg {String} minText\r
23148      * The error text to display if the minDate validation fails (defaults to <tt>'This date is before the minimum date'</tt>)\r
23149      */\r
23150     minText : 'This date is before the minimum date',\r
23151     /**\r
23152      * @cfg {String} maxText\r
23153      * The error text to display if the maxDate validation fails (defaults to <tt>'This date is after the maximum date'</tt>)\r
23154      */\r
23155     maxText : 'This date is after the maximum date',\r
23156     /**\r
23157      * @cfg {String} format\r
23158      * The default date format string which can be overriden for localization support.  The format must be\r
23159      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/y'</tt>).\r
23160      */\r
23161     format : 'm/d/y',\r
23162     /**\r
23163      * @cfg {String} disabledDaysText\r
23164      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)\r
23165      */\r
23166     disabledDaysText : 'Disabled',\r
23167     /**\r
23168      * @cfg {String} disabledDatesText\r
23169      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)\r
23170      */\r
23171     disabledDatesText : 'Disabled',\r
23172     /**\r
23173      * @cfg {Array} monthNames\r
23174      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)\r
23175      */\r
23176     monthNames : Date.monthNames,\r
23177     /**\r
23178      * @cfg {Array} dayNames\r
23179      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)\r
23180      */\r
23181     dayNames : Date.dayNames,\r
23182     /**\r
23183      * @cfg {String} nextText\r
23184      * The next month navigation button tooltip (defaults to <tt>'Next Month (Control+Right)'</tt>)\r
23185      */\r
23186     nextText : 'Next Month (Control+Right)',\r
23187     /**\r
23188      * @cfg {String} prevText\r
23189      * The previous month navigation button tooltip (defaults to <tt>'Previous Month (Control+Left)'</tt>)\r
23190      */\r
23191     prevText : 'Previous Month (Control+Left)',\r
23192     /**\r
23193      * @cfg {String} monthYearText\r
23194      * The header month selector tooltip (defaults to <tt>'Choose a month (Control+Up/Down to move years)'</tt>)\r
23195      */\r
23196     monthYearText : 'Choose a month (Control+Up/Down to move years)',\r
23197     /**\r
23198      * @cfg {Number} startDay\r
23199      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)\r
23200      */\r
23201     startDay : 0,\r
23202     /**\r
23203      * @cfg {Boolean} showToday\r
23204      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar\r
23205      * that selects the current date (defaults to <tt>true</tt>).\r
23206      */\r
23207     showToday : true,\r
23208     /**\r
23209      * @cfg {Date} minDate\r
23210      * Minimum allowable date (JavaScript date object, defaults to null)\r
23211      */\r
23212     /**\r
23213      * @cfg {Date} maxDate\r
23214      * Maximum allowable date (JavaScript date object, defaults to null)\r
23215      */\r
23216     /**\r
23217      * @cfg {Array} disabledDays\r
23218      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).\r
23219      */\r
23220     /**\r
23221      * @cfg {RegExp} disabledDatesRE\r
23222      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}\r
23223      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the\r
23224      * disabledDates value.\r
23225      */\r
23226     /**\r
23227      * @cfg {Array} disabledDates\r
23228      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular\r
23229      * expression so they are very powerful. Some examples:\r
23230      * <ul>\r
23231      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>\r
23232      * <li>['03/08', '09/16'] would disable those days for every year</li>\r
23233      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>\r
23234      * <li>['03/../2006'] would disable every day in March 2006</li>\r
23235      * <li>['^03'] would disable every day in every March</li>\r
23236      * </ul>\r
23237      * Note that the format of the dates included in the array should exactly match the {@link #format} config.\r
23238      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to\r
23239      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].\r
23240      */\r
23241 \r
23242     // private\r
23243     initComponent : function(){\r
23244         Ext.DatePicker.superclass.initComponent.call(this);\r
23245 \r
23246         this.value = this.value ?\r
23247                  this.value.clearTime() : new Date().clearTime();\r
23248 \r
23249         this.addEvents(\r
23250             /**\r
23251              * @event select\r
23252              * Fires when a date is selected\r
23253              * @param {DatePicker} this\r
23254              * @param {Date} date The selected date\r
23255              */\r
23256             'select'\r
23257         );\r
23258 \r
23259         if(this.handler){\r
23260             this.on('select', this.handler,  this.scope || this);\r
23261         }\r
23262 \r
23263         this.initDisabledDays();\r
23264     },\r
23265 \r
23266     // private\r
23267     initDisabledDays : function(){\r
23268         if(!this.disabledDatesRE && this.disabledDates){\r
23269             var dd = this.disabledDates,\r
23270                 len = dd.length - 1,\r
23271                 re = '(?:';\r
23272                 \r
23273             Ext.each(dd, function(d, i){\r
23274                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];\r
23275                 if(i != len){\r
23276                     re += '|';\r
23277                 }\r
23278             }, this);\r
23279             this.disabledDatesRE = new RegExp(re + ')');\r
23280         }\r
23281     },\r
23282 \r
23283     /**\r
23284      * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
23285      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config\r
23286      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.\r
23287      */\r
23288     setDisabledDates : function(dd){\r
23289         if(Ext.isArray(dd)){\r
23290             this.disabledDates = dd;\r
23291             this.disabledDatesRE = null;\r
23292         }else{\r
23293             this.disabledDatesRE = dd;\r
23294         }\r
23295         this.initDisabledDays();\r
23296         this.update(this.value, true);\r
23297     },\r
23298 \r
23299     /**\r
23300      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
23301      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config\r
23302      * for details on supported values.\r
23303      */\r
23304     setDisabledDays : function(dd){\r
23305         this.disabledDays = dd;\r
23306         this.update(this.value, true);\r
23307     },\r
23308 \r
23309     /**\r
23310      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.\r
23311      * @param {Date} value The minimum date that can be selected\r
23312      */\r
23313     setMinDate : function(dt){\r
23314         this.minDate = dt;\r
23315         this.update(this.value, true);\r
23316     },\r
23317 \r
23318     /**\r
23319      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.\r
23320      * @param {Date} value The maximum date that can be selected\r
23321      */\r
23322     setMaxDate : function(dt){\r
23323         this.maxDate = dt;\r
23324         this.update(this.value, true);\r
23325     },\r
23326 \r
23327     /**\r
23328      * Sets the value of the date field\r
23329      * @param {Date} value The date to set\r
23330      */\r
23331     setValue : function(value){\r
23332         var old = this.value;\r
23333         this.value = value.clearTime(true);\r
23334         if(this.el){\r
23335             this.update(this.value);\r
23336         }\r
23337     },\r
23338 \r
23339     /**\r
23340      * Gets the current selected value of the date field\r
23341      * @return {Date} The selected date\r
23342      */\r
23343     getValue : function(){\r
23344         return this.value;\r
23345     },\r
23346 \r
23347     // private\r
23348     focus : function(){\r
23349         if(this.el){\r
23350             this.update(this.activeDate);\r
23351         }\r
23352     },\r
23353     \r
23354     // private\r
23355     onEnable: function(initial){\r
23356         Ext.DatePicker.superclass.onEnable.call(this);    \r
23357         this.doDisabled(false);\r
23358         this.update(initial ? this.value : this.activeDate);\r
23359         if(Ext.isIE){\r
23360             this.el.repaint();\r
23361         }\r
23362         \r
23363     },\r
23364     \r
23365     // private\r
23366     onDisable: function(){\r
23367         Ext.DatePicker.superclass.onDisable.call(this);   \r
23368         this.doDisabled(true);\r
23369         if(Ext.isIE && !Ext.isIE8){\r
23370             /* Really strange problem in IE6/7, when disabled, have to explicitly\r
23371              * repaint each of the nodes to get them to display correctly, simply\r
23372              * calling repaint on the main element doesn't appear to be enough.\r
23373              */\r
23374              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){\r
23375                  Ext.fly(el).repaint();\r
23376              });\r
23377         }\r
23378     },\r
23379     \r
23380     // private\r
23381     doDisabled: function(disabled){\r
23382         this.keyNav.setDisabled(disabled);\r
23383         this.prevRepeater.setDisabled(disabled);\r
23384         this.nextRepeater.setDisabled(disabled);\r
23385         if(this.showToday){\r
23386             this.todayKeyListener.setDisabled(disabled);\r
23387             this.todayBtn.setDisabled(disabled);\r
23388         }\r
23389     },\r
23390 \r
23391     // private\r
23392     onRender : function(container, position){\r
23393         var m = [\r
23394              '<table cellspacing="0">',\r
23395                 '<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>',\r
23396                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],\r
23397                 dn = this.dayNames,\r
23398                 i;\r
23399         for(i = 0; i < 7; i++){\r
23400             var d = this.startDay+i;\r
23401             if(d > 6){\r
23402                 d = d-7;\r
23403             }\r
23404             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');\r
23405         }\r
23406         m[m.length] = '</tr></thead><tbody><tr>';\r
23407         for(i = 0; i < 42; i++) {\r
23408             if(i % 7 === 0 && i !== 0){\r
23409                 m[m.length] = '</tr><tr>';\r
23410             }\r
23411             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';\r
23412         }\r
23413         m.push('</tr></tbody></table></td></tr>',\r
23414                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',\r
23415                 '</table><div class="x-date-mp"></div>');\r
23416 \r
23417         var el = document.createElement('div');\r
23418         el.className = 'x-date-picker';\r
23419         el.innerHTML = m.join('');\r
23420 \r
23421         container.dom.insertBefore(el, position);\r
23422 \r
23423         this.el = Ext.get(el);\r
23424         this.eventEl = Ext.get(el.firstChild);\r
23425 \r
23426         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {\r
23427             handler: this.showPrevMonth,\r
23428             scope: this,\r
23429             preventDefault:true,\r
23430             stopDefault:true\r
23431         });\r
23432 \r
23433         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {\r
23434             handler: this.showNextMonth,\r
23435             scope: this,\r
23436             preventDefault:true,\r
23437             stopDefault:true\r
23438         });\r
23439 \r
23440         this.monthPicker = this.el.down('div.x-date-mp');\r
23441         this.monthPicker.enableDisplayMode('block');\r
23442 \r
23443         this.keyNav = new Ext.KeyNav(this.eventEl, {\r
23444             'left' : function(e){\r
23445                 if(e.ctrlKey){\r
23446                     this.showPrevMonth();\r
23447                 }else{\r
23448                     this.update(this.activeDate.add('d', -1));    \r
23449                 }\r
23450             },\r
23451 \r
23452             'right' : function(e){\r
23453                 if(e.ctrlKey){\r
23454                     this.showNextMonth();\r
23455                 }else{\r
23456                     this.update(this.activeDate.add('d', 1));    \r
23457                 }\r
23458             },\r
23459 \r
23460             'up' : function(e){\r
23461                 if(e.ctrlKey){\r
23462                     this.showNextYear();\r
23463                 }else{\r
23464                     this.update(this.activeDate.add('d', -7));\r
23465                 }\r
23466             },\r
23467 \r
23468             'down' : function(e){\r
23469                 if(e.ctrlKey){\r
23470                     this.showPrevYear();\r
23471                 }else{\r
23472                     this.update(this.activeDate.add('d', 7));\r
23473                 }\r
23474             },\r
23475 \r
23476             'pageUp' : function(e){\r
23477                 this.showNextMonth();\r
23478             },\r
23479 \r
23480             'pageDown' : function(e){\r
23481                 this.showPrevMonth();\r
23482             },\r
23483 \r
23484             'enter' : function(e){\r
23485                 e.stopPropagation();\r
23486                 return true;\r
23487             },\r
23488 \r
23489             scope : this\r
23490         });\r
23491 \r
23492         this.el.unselectable();\r
23493 \r
23494         this.cells = this.el.select('table.x-date-inner tbody td');\r
23495         this.textNodes = this.el.query('table.x-date-inner tbody span');\r
23496 \r
23497         this.mbtn = new Ext.Button({\r
23498             text: '&#160;',\r
23499             tooltip: this.monthYearText,\r
23500             renderTo: this.el.child('td.x-date-middle', true)\r
23501         });\r
23502         this.mbtn.el.child('em').addClass('x-btn-arrow');\r
23503 \r
23504         if(this.showToday){\r
23505             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);\r
23506             var today = (new Date()).dateFormat(this.format);\r
23507             this.todayBtn = new Ext.Button({\r
23508                 renderTo: this.el.child('td.x-date-bottom', true),\r
23509                 text: String.format(this.todayText, today),\r
23510                 tooltip: String.format(this.todayTip, today),\r
23511                 handler: this.selectToday,\r
23512                 scope: this\r
23513             });\r
23514         }\r
23515         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);\r
23516         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});\r
23517         this.mon(this.mbtn, 'click', this.showMonthPicker, this);\r
23518         this.onEnable(true);\r
23519     },\r
23520 \r
23521     // private\r
23522     createMonthPicker : function(){\r
23523         if(!this.monthPicker.dom.firstChild){\r
23524             var buf = ['<table border="0" cellspacing="0">'];\r
23525             for(var i = 0; i < 6; i++){\r
23526                 buf.push(\r
23527                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',\r
23528                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',\r
23529                     i === 0 ?\r
23530                     '<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>' :\r
23531                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'\r
23532                 );\r
23533             }\r
23534             buf.push(\r
23535                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',\r
23536                     this.okText,\r
23537                     '</button><button type="button" class="x-date-mp-cancel">',\r
23538                     this.cancelText,\r
23539                     '</button></td></tr>',\r
23540                 '</table>'\r
23541             );\r
23542             this.monthPicker.update(buf.join(''));\r
23543 \r
23544             this.mon(this.monthPicker, 'click', this.onMonthClick, this);\r
23545             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);\r
23546 \r
23547             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');\r
23548             this.mpYears = this.monthPicker.select('td.x-date-mp-year');\r
23549 \r
23550             this.mpMonths.each(function(m, a, i){\r
23551                 i += 1;\r
23552                 if((i%2) === 0){\r
23553                     m.dom.xmonth = 5 + Math.round(i * 0.5);\r
23554                 }else{\r
23555                     m.dom.xmonth = Math.round((i-1) * 0.5);\r
23556                 }\r
23557             });\r
23558         }\r
23559     },\r
23560 \r
23561     // private\r
23562     showMonthPicker : function(){\r
23563         if(!this.disabled){\r
23564             this.createMonthPicker();\r
23565             var size = this.el.getSize();\r
23566             this.monthPicker.setSize(size);\r
23567             this.monthPicker.child('table').setSize(size);\r
23568 \r
23569             this.mpSelMonth = (this.activeDate || this.value).getMonth();\r
23570             this.updateMPMonth(this.mpSelMonth);\r
23571             this.mpSelYear = (this.activeDate || this.value).getFullYear();\r
23572             this.updateMPYear(this.mpSelYear);\r
23573 \r
23574             this.monthPicker.slideIn('t', {duration:0.2});\r
23575         }\r
23576     },\r
23577 \r
23578     // private\r
23579     updateMPYear : function(y){\r
23580         this.mpyear = y;\r
23581         var ys = this.mpYears.elements;\r
23582         for(var i = 1; i <= 10; i++){\r
23583             var td = ys[i-1], y2;\r
23584             if((i%2) === 0){\r
23585                 y2 = y + Math.round(i * 0.5);\r
23586                 td.firstChild.innerHTML = y2;\r
23587                 td.xyear = y2;\r
23588             }else{\r
23589                 y2 = y - (5-Math.round(i * 0.5));\r
23590                 td.firstChild.innerHTML = y2;\r
23591                 td.xyear = y2;\r
23592             }\r
23593             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
23594         }\r
23595     },\r
23596 \r
23597     // private\r
23598     updateMPMonth : function(sm){\r
23599         this.mpMonths.each(function(m, a, i){\r
23600             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
23601         });\r
23602     },\r
23603 \r
23604     // private\r
23605     selectMPMonth : function(m){\r
23606 \r
23607     },\r
23608 \r
23609     // private\r
23610     onMonthClick : function(e, t){\r
23611         e.stopEvent();\r
23612         var el = new Ext.Element(t), pn;\r
23613         if(el.is('button.x-date-mp-cancel')){\r
23614             this.hideMonthPicker();\r
23615         }\r
23616         else if(el.is('button.x-date-mp-ok')){\r
23617             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());\r
23618             if(d.getMonth() != this.mpSelMonth){\r
23619                 // 'fix' the JS rolling date conversion if needed\r
23620                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();\r
23621             }\r
23622             this.update(d);\r
23623             this.hideMonthPicker();\r
23624         }\r
23625         else if((pn = el.up('td.x-date-mp-month', 2))){\r
23626             this.mpMonths.removeClass('x-date-mp-sel');\r
23627             pn.addClass('x-date-mp-sel');\r
23628             this.mpSelMonth = pn.dom.xmonth;\r
23629         }\r
23630         else if((pn = el.up('td.x-date-mp-year', 2))){\r
23631             this.mpYears.removeClass('x-date-mp-sel');\r
23632             pn.addClass('x-date-mp-sel');\r
23633             this.mpSelYear = pn.dom.xyear;\r
23634         }\r
23635         else if(el.is('a.x-date-mp-prev')){\r
23636             this.updateMPYear(this.mpyear-10);\r
23637         }\r
23638         else if(el.is('a.x-date-mp-next')){\r
23639             this.updateMPYear(this.mpyear+10);\r
23640         }\r
23641     },\r
23642 \r
23643     // private\r
23644     onMonthDblClick : function(e, t){\r
23645         e.stopEvent();\r
23646         var el = new Ext.Element(t), pn;\r
23647         if((pn = el.up('td.x-date-mp-month', 2))){\r
23648             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));\r
23649             this.hideMonthPicker();\r
23650         }\r
23651         else if((pn = el.up('td.x-date-mp-year', 2))){\r
23652             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));\r
23653             this.hideMonthPicker();\r
23654         }\r
23655     },\r
23656 \r
23657     // private\r
23658     hideMonthPicker : function(disableAnim){\r
23659         if(this.monthPicker){\r
23660             if(disableAnim === true){\r
23661                 this.monthPicker.hide();\r
23662             }else{\r
23663                 this.monthPicker.slideOut('t', {duration:0.2});\r
23664             }\r
23665         }\r
23666     },\r
23667 \r
23668     // private\r
23669     showPrevMonth : function(e){\r
23670         this.update(this.activeDate.add('mo', -1));\r
23671     },\r
23672 \r
23673     // private\r
23674     showNextMonth : function(e){\r
23675         this.update(this.activeDate.add('mo', 1));\r
23676     },\r
23677 \r
23678     // private\r
23679     showPrevYear : function(){\r
23680         this.update(this.activeDate.add('y', -1));\r
23681     },\r
23682 \r
23683     // private\r
23684     showNextYear : function(){\r
23685         this.update(this.activeDate.add('y', 1));\r
23686     },\r
23687 \r
23688     // private\r
23689     handleMouseWheel : function(e){\r
23690         e.stopEvent();\r
23691         if(!this.disabled){\r
23692             var delta = e.getWheelDelta();\r
23693             if(delta > 0){\r
23694                 this.showPrevMonth();\r
23695             } else if(delta < 0){\r
23696                 this.showNextMonth();\r
23697             }\r
23698         }\r
23699     },\r
23700 \r
23701     // private\r
23702     handleDateClick : function(e, t){\r
23703         e.stopEvent();\r
23704         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){\r
23705             this.setValue(new Date(t.dateValue));\r
23706             this.fireEvent('select', this, this.value);\r
23707         }\r
23708     },\r
23709 \r
23710     // private\r
23711     selectToday : function(){\r
23712         if(this.todayBtn && !this.todayBtn.disabled){\r
23713             this.setValue(new Date().clearTime());\r
23714             this.fireEvent('select', this, this.value);\r
23715         }\r
23716     },\r
23717 \r
23718     // private\r
23719     update : function(date, forceRefresh){\r
23720         var vd = this.activeDate, vis = this.isVisible();\r
23721         this.activeDate = date;\r
23722         if(!forceRefresh && vd && this.el){\r
23723             var t = date.getTime();\r
23724             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){\r
23725                 this.cells.removeClass('x-date-selected');\r
23726                 this.cells.each(function(c){\r
23727                    if(c.dom.firstChild.dateValue == t){\r
23728                        c.addClass('x-date-selected');\r
23729                        if(vis){\r
23730                            Ext.fly(c.dom.firstChild).focus(50);\r
23731                        }\r
23732                        return false;\r
23733                    }\r
23734                 });\r
23735                 return;\r
23736             }\r
23737         }\r
23738         var days = date.getDaysInMonth();\r
23739         var firstOfMonth = date.getFirstDateOfMonth();\r
23740         var startingPos = firstOfMonth.getDay()-this.startDay;\r
23741 \r
23742         if(startingPos <= this.startDay){\r
23743             startingPos += 7;\r
23744         }\r
23745 \r
23746         var pm = date.add('mo', -1);\r
23747         var prevStart = pm.getDaysInMonth()-startingPos;\r
23748 \r
23749         var cells = this.cells.elements;\r
23750         var textEls = this.textNodes;\r
23751         days += startingPos;\r
23752 \r
23753         // convert everything to numbers so it's fast\r
23754         var day = 86400000;\r
23755         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();\r
23756         var today = new Date().clearTime().getTime();\r
23757         var sel = date.clearTime().getTime();\r
23758         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;\r
23759         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;\r
23760         var ddMatch = this.disabledDatesRE;\r
23761         var ddText = this.disabledDatesText;\r
23762         var ddays = this.disabledDays ? this.disabledDays.join('') : false;\r
23763         var ddaysText = this.disabledDaysText;\r
23764         var format = this.format;\r
23765 \r
23766         if(this.showToday){\r
23767             var td = new Date().clearTime();\r
23768             var disable = (td < min || td > max ||\r
23769                 (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||\r
23770                 (ddays && ddays.indexOf(td.getDay()) != -1));\r
23771 \r
23772             if(!this.disabled){\r
23773                 this.todayBtn.setDisabled(disable);\r
23774                 this.todayKeyListener[disable ? 'disable' : 'enable']();\r
23775             }\r
23776         }\r
23777 \r
23778         var setCellClass = function(cal, cell){\r
23779             cell.title = '';\r
23780             var t = d.getTime();\r
23781             cell.firstChild.dateValue = t;\r
23782             if(t == today){\r
23783                 cell.className += ' x-date-today';\r
23784                 cell.title = cal.todayText;\r
23785             }\r
23786             if(t == sel){\r
23787                 cell.className += ' x-date-selected';\r
23788                 if(vis){\r
23789                     Ext.fly(cell.firstChild).focus(50);\r
23790                 }\r
23791             }\r
23792             // disabling\r
23793             if(t < min) {\r
23794                 cell.className = ' x-date-disabled';\r
23795                 cell.title = cal.minText;\r
23796                 return;\r
23797             }\r
23798             if(t > max) {\r
23799                 cell.className = ' x-date-disabled';\r
23800                 cell.title = cal.maxText;\r
23801                 return;\r
23802             }\r
23803             if(ddays){\r
23804                 if(ddays.indexOf(d.getDay()) != -1){\r
23805                     cell.title = ddaysText;\r
23806                     cell.className = ' x-date-disabled';\r
23807                 }\r
23808             }\r
23809             if(ddMatch && format){\r
23810                 var fvalue = d.dateFormat(format);\r
23811                 if(ddMatch.test(fvalue)){\r
23812                     cell.title = ddText.replace('%0', fvalue);\r
23813                     cell.className = ' x-date-disabled';\r
23814                 }\r
23815             }\r
23816         };\r
23817 \r
23818         var i = 0;\r
23819         for(; i < startingPos; i++) {\r
23820             textEls[i].innerHTML = (++prevStart);\r
23821             d.setDate(d.getDate()+1);\r
23822             cells[i].className = 'x-date-prevday';\r
23823             setCellClass(this, cells[i]);\r
23824         }\r
23825         for(; i < days; i++){\r
23826             var intDay = i - startingPos + 1;\r
23827             textEls[i].innerHTML = (intDay);\r
23828             d.setDate(d.getDate()+1);\r
23829             cells[i].className = 'x-date-active';\r
23830             setCellClass(this, cells[i]);\r
23831         }\r
23832         var extraDays = 0;\r
23833         for(; i < 42; i++) {\r
23834              textEls[i].innerHTML = (++extraDays);\r
23835              d.setDate(d.getDate()+1);\r
23836              cells[i].className = 'x-date-nextday';\r
23837              setCellClass(this, cells[i]);\r
23838         }\r
23839 \r
23840         this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());\r
23841 \r
23842         if(!this.internalRender){\r
23843             var main = this.el.dom.firstChild;\r
23844             var w = main.offsetWidth;\r
23845             this.el.setWidth(w + this.el.getBorderWidth('lr'));\r
23846             Ext.fly(main).setWidth(w);\r
23847             this.internalRender = true;\r
23848             // opera does not respect the auto grow header center column\r
23849             // then, after it gets a width opera refuses to recalculate\r
23850             // without a second pass\r
23851             if(Ext.isOpera && !this.secondPass){\r
23852                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';\r
23853                 this.secondPass = true;\r
23854                 this.update.defer(10, this, [date]);\r
23855             }\r
23856         }\r
23857     },\r
23858 \r
23859     // private\r
23860     beforeDestroy : function() {\r
23861         if(this.rendered){\r
23862             this.keyNav.disable();\r
23863             this.keyNav = null;\r
23864             Ext.destroy(\r
23865                 this.leftClickRpt,\r
23866                 this.rightClickRpt,\r
23867                 this.monthPicker,\r
23868                 this.eventEl,\r
23869                 this.mbtn,\r
23870                 this.todayBtn\r
23871             );\r
23872         }\r
23873     }\r
23874 \r
23875     /**\r
23876      * @cfg {String} autoEl @hide\r
23877      */\r
23878 });\r
23879 \r
23880 Ext.reg('datepicker', Ext.DatePicker);\r
23881 /**
23882  * @class Ext.LoadMask
23883  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
23884  * config option is specified, the masking will be automatically synchronized with the store's loading
23885  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
23886  * element's Updater load indicator and will be destroyed after the initial load.
23887  * <p>Example usage:</p>
23888  *<pre><code>
23889 // Basic mask:
23890 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
23891 myMask.show();
23892 </code></pre>
23893  * @constructor
23894  * Create a new LoadMask
23895  * @param {Mixed} el The element or DOM node, or its id
23896  * @param {Object} config The config object
23897  */
23898 Ext.LoadMask = function(el, config){
23899     this.el = Ext.get(el);
23900     Ext.apply(this, config);
23901     if(this.store){
23902         this.store.on('beforeload', this.onBeforeLoad, this);
23903         this.store.on('load', this.onLoad, this);
23904         this.store.on('exception', this.onLoad, this);
23905         this.removeMask = Ext.value(this.removeMask, false);
23906     }else{
23907         var um = this.el.getUpdater();
23908         um.showLoadIndicator = false; // disable the default indicator
23909         um.on('beforeupdate', this.onBeforeLoad, this);
23910         um.on('update', this.onLoad, this);
23911         um.on('failure', this.onLoad, this);
23912         this.removeMask = Ext.value(this.removeMask, true);
23913     }
23914 };
23915
23916 Ext.LoadMask.prototype = {
23917     /**
23918      * @cfg {Ext.data.Store} store
23919      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
23920      * hidden on either load sucess, or load fail.
23921      */
23922     /**
23923      * @cfg {Boolean} removeMask
23924      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
23925      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
23926      */
23927     /**
23928      * @cfg {String} msg
23929      * The text to display in a centered loading message box (defaults to 'Loading...')
23930      */
23931     msg : 'Loading...',
23932     /**
23933      * @cfg {String} msgCls
23934      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
23935      */
23936     msgCls : 'x-mask-loading',
23937
23938     /**
23939      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
23940      * @type Boolean
23941      */
23942     disabled: false,
23943
23944     /**
23945      * Disables the mask to prevent it from being displayed
23946      */
23947     disable : function(){
23948        this.disabled = true;
23949     },
23950
23951     /**
23952      * Enables the mask so that it can be displayed
23953      */
23954     enable : function(){
23955         this.disabled = false;
23956     },
23957
23958     // private
23959     onLoad : function(){
23960         this.el.unmask(this.removeMask);
23961     },
23962
23963     // private
23964     onBeforeLoad : function(){
23965         if(!this.disabled){
23966             this.el.mask(this.msg, this.msgCls);
23967         }
23968     },
23969
23970     /**
23971      * Show this LoadMask over the configured Element.
23972      */
23973     show: function(){
23974         this.onBeforeLoad();
23975     },
23976
23977     /**
23978      * Hide this LoadMask.
23979      */
23980     hide: function(){
23981         this.onLoad();
23982     },
23983
23984     // private
23985     destroy : function(){
23986         if(this.store){
23987             this.store.un('beforeload', this.onBeforeLoad, this);
23988             this.store.un('load', this.onLoad, this);
23989             this.store.un('exception', this.onLoad, this);
23990         }else{
23991             var um = this.el.getUpdater();
23992             um.un('beforeupdate', this.onBeforeLoad, this);
23993             um.un('update', this.onLoad, this);
23994             um.un('failure', this.onLoad, this);
23995         }
23996     }
23997 };/**\r
23998  * @class Ext.Slider\r
23999  * @extends Ext.BoxComponent\r
24000  * Slider which supports vertical or horizontal orientation, keyboard adjustments,\r
24001  * configurable snapping, axis clicking and animation. Can be added as an item to\r
24002  * any container. Example usage:\r
24003 <pre><code>\r
24004 new Ext.Slider({\r
24005     renderTo: Ext.getBody(),\r
24006     width: 200,\r
24007     value: 50,\r
24008     increment: 10,\r
24009     minValue: 0,\r
24010     maxValue: 100\r
24011 });\r
24012 </code></pre>\r
24013  */\r
24014 Ext.Slider = Ext.extend(Ext.BoxComponent, {\r
24015         /**\r
24016          * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.\r
24017          */\r
24018         /**\r
24019          * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.\r
24020          */\r
24021     vertical: false,\r
24022         /**\r
24023          * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.\r
24024          */\r
24025     minValue: 0,\r
24026         /**\r
24027          * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.\r
24028          */\r
24029     maxValue: 100,\r
24030     /**\r
24031      * @cfg {Number/Boolean} decimalPrecision.\r
24032      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>\r
24033      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>\r
24034      */\r
24035     decimalPrecision: 0,\r
24036         /**\r
24037          * @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.\r
24038          */\r
24039     keyIncrement: 1,\r
24040         /**\r
24041          * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.\r
24042          */\r
24043     increment: 0,\r
24044         // private\r
24045     clickRange: [5,15],\r
24046         /**\r
24047          * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true\r
24048          */\r
24049     clickToChange : true,\r
24050         /**\r
24051          * @cfg {Boolean} animate Turn on or off animation. Defaults to true\r
24052          */\r
24053     animate: true,\r
24054 \r
24055     /**\r
24056      * True while the thumb is in a drag operation\r
24057      * @type boolean\r
24058      */\r
24059     dragging: false,\r
24060 \r
24061     // private override\r
24062     initComponent : function(){\r
24063         if(!Ext.isDefined(this.value)){\r
24064             this.value = this.minValue;\r
24065         }\r
24066         Ext.Slider.superclass.initComponent.call(this);\r
24067         this.keyIncrement = Math.max(this.increment, this.keyIncrement);\r
24068         this.addEvents(\r
24069             /**\r
24070              * @event beforechange\r
24071              * Fires before the slider value is changed. By returning false from an event handler,\r
24072              * you can cancel the event and prevent the slider from changing.\r
24073                          * @param {Ext.Slider} slider The slider\r
24074                          * @param {Number} newValue The new value which the slider is being changed to.\r
24075                          * @param {Number} oldValue The old value which the slider was previously.\r
24076              */\r
24077                         'beforechange',\r
24078                         /**\r
24079                          * @event change\r
24080                          * Fires when the slider value is changed.\r
24081                          * @param {Ext.Slider} slider The slider\r
24082                          * @param {Number} newValue The new value which the slider has been changed to.\r
24083                          */\r
24084                         'change',\r
24085                         /**\r
24086                          * @event changecomplete\r
24087                          * Fires when the slider value is changed by the user and any drag operations have completed.\r
24088                          * @param {Ext.Slider} slider The slider\r
24089                          * @param {Number} newValue The new value which the slider has been changed to.\r
24090                          */\r
24091                         'changecomplete',\r
24092                         /**\r
24093                          * @event dragstart\r
24094              * Fires after a drag operation has started.\r
24095                          * @param {Ext.Slider} slider The slider\r
24096                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24097                          */\r
24098                         'dragstart',\r
24099                         /**\r
24100                          * @event drag\r
24101              * Fires continuously during the drag operation while the mouse is moving.\r
24102                          * @param {Ext.Slider} slider The slider\r
24103                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24104                          */\r
24105                         'drag',\r
24106                         /**\r
24107                          * @event dragend\r
24108              * Fires after the drag operation has completed.\r
24109                          * @param {Ext.Slider} slider The slider\r
24110                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24111                          */\r
24112                         'dragend'\r
24113                 );\r
24114 \r
24115         if(this.vertical){\r
24116             Ext.apply(this, Ext.Slider.Vertical);\r
24117         }\r
24118     },\r
24119 \r
24120         // private override\r
24121     onRender : function(){\r
24122         this.autoEl = {\r
24123             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),\r
24124             cn:{cls:'x-slider-end',cn:{cls:'x-slider-inner',cn:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}}\r
24125         };\r
24126         Ext.Slider.superclass.onRender.apply(this, arguments);\r
24127         this.endEl = this.el.first();\r
24128         this.innerEl = this.endEl.first();\r
24129         this.thumb = this.innerEl.first();\r
24130         this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2;\r
24131         this.focusEl = this.thumb.next();\r
24132         this.initEvents();\r
24133     },\r
24134 \r
24135         // private override\r
24136     initEvents : function(){\r
24137         this.thumb.addClassOnOver('x-slider-thumb-over');\r
24138         this.mon(this.el, {\r
24139             scope: this,\r
24140             mousedown: this.onMouseDown,\r
24141             keydown: this.onKeyDown\r
24142         });\r
24143 \r
24144         this.focusEl.swallowEvent("click", true);\r
24145 \r
24146         this.tracker = new Ext.dd.DragTracker({\r
24147             onBeforeStart: this.onBeforeDragStart.createDelegate(this),\r
24148             onStart: this.onDragStart.createDelegate(this),\r
24149             onDrag: this.onDrag.createDelegate(this),\r
24150             onEnd: this.onDragEnd.createDelegate(this),\r
24151             tolerance: 3,\r
24152             autoStart: 300\r
24153         });\r
24154         this.tracker.initEl(this.thumb);\r
24155         this.on('beforedestroy', this.tracker.destroy, this.tracker);\r
24156     },\r
24157 \r
24158         // private override\r
24159     onMouseDown : function(e){\r
24160         if(this.disabled) {return;}\r
24161         if(this.clickToChange && e.target != this.thumb.dom){\r
24162             var local = this.innerEl.translatePoints(e.getXY());\r
24163             this.onClickChange(local);\r
24164         }\r
24165         this.focus();\r
24166     },\r
24167 \r
24168         // private\r
24169     onClickChange : function(local){\r
24170         if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){\r
24171             this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);\r
24172         }\r
24173     },\r
24174 \r
24175         // private\r
24176     onKeyDown : function(e){\r
24177         if(this.disabled){e.preventDefault();return;}\r
24178         var k = e.getKey();\r
24179         switch(k){\r
24180             case e.UP:\r
24181             case e.RIGHT:\r
24182                 e.stopEvent();\r
24183                 if(e.ctrlKey){\r
24184                     this.setValue(this.maxValue, undefined, true);\r
24185                 }else{\r
24186                     this.setValue(this.value+this.keyIncrement, undefined, true);\r
24187                 }\r
24188             break;\r
24189             case e.DOWN:\r
24190             case e.LEFT:\r
24191                 e.stopEvent();\r
24192                 if(e.ctrlKey){\r
24193                     this.setValue(this.minValue, undefined, true);\r
24194                 }else{\r
24195                     this.setValue(this.value-this.keyIncrement, undefined, true);\r
24196                 }\r
24197             break;\r
24198             default:\r
24199                 e.preventDefault();\r
24200         }\r
24201     },\r
24202 \r
24203         // private\r
24204     doSnap : function(value){\r
24205         if(!this.increment || this.increment == 1 || !value) {\r
24206             return value;\r
24207         }\r
24208         var newValue = value, inc = this.increment;\r
24209         var m = value % inc;\r
24210         if(m != 0){\r
24211             newValue -= m;\r
24212             if(m * 2 > inc){\r
24213                 newValue += inc;\r
24214             }else if(m * 2 < -inc){\r
24215                 newValue -= inc;\r
24216             }\r
24217         }\r
24218         return newValue.constrain(this.minValue,  this.maxValue);\r
24219     },\r
24220 \r
24221         // private\r
24222     afterRender : function(){\r
24223         Ext.Slider.superclass.afterRender.apply(this, arguments);\r
24224         if(this.value !== undefined){\r
24225             var v = this.normalizeValue(this.value);\r
24226             if(v !== this.value){\r
24227                 delete this.value;\r
24228                 this.setValue(v, false);\r
24229             }else{\r
24230                 this.moveThumb(this.translateValue(v), false);\r
24231             }\r
24232         }\r
24233     },\r
24234 \r
24235         // private\r
24236     getRatio : function(){\r
24237         var w = this.innerEl.getWidth();\r
24238         var v = this.maxValue - this.minValue;\r
24239         return v == 0 ? w : (w/v);\r
24240     },\r
24241 \r
24242         // private\r
24243     normalizeValue : function(v){\r
24244         v = this.doSnap(v);\r
24245         v = Ext.util.Format.round(v, this.decimalPrecision);\r
24246         v = v.constrain(this.minValue, this.maxValue);\r
24247         return v;\r
24248     },\r
24249 \r
24250         /**\r
24251          * Programmatically sets the value of the Slider. Ensures that the value is constrained within\r
24252          * the minValue and maxValue.\r
24253          * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)\r
24254          * @param {Boolean} animate Turn on or off animation, defaults to true\r
24255          */\r
24256     setValue : function(v, animate, changeComplete){\r
24257         v = this.normalizeValue(v);\r
24258         if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){\r
24259             this.value = v;\r
24260             this.moveThumb(this.translateValue(v), animate !== false);\r
24261             this.fireEvent('change', this, v);\r
24262             if(changeComplete){\r
24263                 this.fireEvent('changecomplete', this, v);\r
24264             }\r
24265         }\r
24266     },\r
24267 \r
24268         // private\r
24269     translateValue : function(v){\r
24270         var ratio = this.getRatio();\r
24271         return (v * ratio)-(this.minValue * ratio)-this.halfThumb;\r
24272     },\r
24273 \r
24274         reverseValue : function(pos){\r
24275         var ratio = this.getRatio();\r
24276         return (pos+this.halfThumb+(this.minValue * ratio))/ratio;\r
24277     },\r
24278 \r
24279         // private\r
24280     moveThumb: function(v, animate){\r
24281         if(!animate || this.animate === false){\r
24282             this.thumb.setLeft(v);\r
24283         }else{\r
24284             this.thumb.shift({left: v, stopFx: true, duration:.35});\r
24285         }\r
24286     },\r
24287 \r
24288         // private\r
24289     focus : function(){\r
24290         this.focusEl.focus(10);\r
24291     },\r
24292 \r
24293         // private\r
24294     onBeforeDragStart : function(e){\r
24295         return !this.disabled;\r
24296     },\r
24297 \r
24298         // private\r
24299     onDragStart: function(e){\r
24300         this.thumb.addClass('x-slider-thumb-drag');\r
24301         this.dragging = true;\r
24302         this.dragStartValue = this.value;\r
24303         this.fireEvent('dragstart', this, e);\r
24304     },\r
24305 \r
24306         // private\r
24307     onDrag: function(e){\r
24308         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
24309         this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false);\r
24310         this.fireEvent('drag', this, e);\r
24311     },\r
24312 \r
24313         // private\r
24314     onDragEnd: function(e){\r
24315         this.thumb.removeClass('x-slider-thumb-drag');\r
24316         this.dragging = false;\r
24317         this.fireEvent('dragend', this, e);\r
24318         if(this.dragStartValue != this.value){\r
24319             this.fireEvent('changecomplete', this, this.value);\r
24320         }\r
24321     },\r
24322 \r
24323         // private\r
24324     onResize : function(w, h){\r
24325         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));\r
24326         this.syncThumb();\r
24327     },\r
24328     \r
24329     //private\r
24330     onDisable: function(){\r
24331         Ext.Slider.superclass.onDisable.call(this);\r
24332         this.thumb.addClass(this.disabledClass);\r
24333         if(Ext.isIE){\r
24334             //IE breaks when using overflow visible and opacity other than 1.\r
24335             //Create a place holder for the thumb and display it.\r
24336             var xy = this.thumb.getXY();\r
24337             this.thumb.hide();\r
24338             this.innerEl.addClass(this.disabledClass).dom.disabled = true;\r
24339             if (!this.thumbHolder){\r
24340                 this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});    \r
24341             }\r
24342             this.thumbHolder.show().setXY(xy);\r
24343         }\r
24344     },\r
24345     \r
24346     //private\r
24347     onEnable: function(){\r
24348         Ext.Slider.superclass.onEnable.call(this);\r
24349         this.thumb.removeClass(this.disabledClass);\r
24350         if(Ext.isIE){\r
24351             this.innerEl.removeClass(this.disabledClass).dom.disabled = false;\r
24352             if (this.thumbHolder){\r
24353                 this.thumbHolder.hide();\r
24354             }\r
24355             this.thumb.show();\r
24356             this.syncThumb();\r
24357         }\r
24358     },\r
24359     \r
24360     /**\r
24361      * Synchronizes the thumb position to the proper proportion of the total component width based\r
24362      * on the current slider {@link #value}.  This will be called automatically when the Slider\r
24363      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
24364      * another resize handler to sync the Slider if necessary.\r
24365      */\r
24366     syncThumb : function(){\r
24367         if(this.rendered){\r
24368             this.moveThumb(this.translateValue(this.value));\r
24369         }\r
24370     },\r
24371 \r
24372         /**\r
24373          * Returns the current value of the slider\r
24374          * @return {Number} The current value of the slider\r
24375          */\r
24376     getValue : function(){\r
24377         return this.value;\r
24378     }\r
24379 });\r
24380 Ext.reg('slider', Ext.Slider);\r
24381 \r
24382 // private class to support vertical sliders\r
24383 Ext.Slider.Vertical = {\r
24384     onResize : function(w, h){\r
24385         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));\r
24386         this.syncThumb();\r
24387     },\r
24388 \r
24389     getRatio : function(){\r
24390         var h = this.innerEl.getHeight();\r
24391         var v = this.maxValue - this.minValue;\r
24392         return h/v;\r
24393     },\r
24394 \r
24395     moveThumb: function(v, animate){\r
24396         if(!animate || this.animate === false){\r
24397             this.thumb.setBottom(v);\r
24398         }else{\r
24399             this.thumb.shift({bottom: v, stopFx: true, duration:.35});\r
24400         }\r
24401     },\r
24402 \r
24403     onDrag: function(e){\r
24404         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
24405         var bottom = this.innerEl.getHeight()-pos.top;\r
24406         this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false);\r
24407         this.fireEvent('drag', this, e);\r
24408     },\r
24409 \r
24410     onClickChange : function(local){\r
24411         if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){\r
24412             var bottom = this.innerEl.getHeight()-local.top;\r
24413             this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true);\r
24414         }\r
24415     }\r
24416 };/**\r
24417  * @class Ext.ProgressBar\r
24418  * @extends Ext.BoxComponent\r
24419  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>\r
24420  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the\r
24421  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress\r
24422  * throughout an operation that has predictable points of interest at which you can update the control.</p>\r
24423  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it\r
24424  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time\r
24425  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in\r
24426  * which you have no need for indicating intermediate progress.</p>\r
24427  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
24428  * @cfg {String} text The progress bar text (defaults to '')\r
24429  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress\r
24430  * bar's internal text element)\r
24431  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)\r
24432  * @xtype progress\r
24433  */\r
24434 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {\r
24435    /**\r
24436     * @cfg {String} baseCls\r
24437     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')\r
24438     */\r
24439     baseCls : 'x-progress',\r
24440     \r
24441     /**\r
24442     * @cfg {Boolean} animate\r
24443     * True to animate the progress bar during transitions (defaults to false)\r
24444     */\r
24445     animate : false,\r
24446 \r
24447     // private\r
24448     waitTimer : null,\r
24449 \r
24450     // private\r
24451     initComponent : function(){\r
24452         Ext.ProgressBar.superclass.initComponent.call(this);\r
24453         this.addEvents(\r
24454             /**\r
24455              * @event update\r
24456              * Fires after each update interval\r
24457              * @param {Ext.ProgressBar} this\r
24458              * @param {Number} The current progress value\r
24459              * @param {String} The current progress text\r
24460              */\r
24461             "update"\r
24462         );\r
24463     },\r
24464 \r
24465     // private\r
24466     onRender : function(ct, position){\r
24467         var tpl = new Ext.Template(\r
24468             '<div class="{cls}-wrap">',\r
24469                 '<div class="{cls}-inner">',\r
24470                     '<div class="{cls}-bar">',\r
24471                         '<div class="{cls}-text">',\r
24472                             '<div>&#160;</div>',\r
24473                         '</div>',\r
24474                     '</div>',\r
24475                     '<div class="{cls}-text {cls}-text-back">',\r
24476                         '<div>&#160;</div>',\r
24477                     '</div>',\r
24478                 '</div>',\r
24479             '</div>'\r
24480         );\r
24481 \r
24482         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)\r
24483                 : tpl.append(ct, {cls: this.baseCls}, true);\r
24484                         \r
24485         if(this.id){\r
24486             this.el.dom.id = this.id;\r
24487         }\r
24488         var inner = this.el.dom.firstChild;\r
24489         this.progressBar = Ext.get(inner.firstChild);\r
24490 \r
24491         if(this.textEl){\r
24492             //use an external text el\r
24493             this.textEl = Ext.get(this.textEl);\r
24494             delete this.textTopEl;\r
24495         }else{\r
24496             //setup our internal layered text els\r
24497             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);\r
24498             var textBackEl = Ext.get(inner.childNodes[1]);\r
24499             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');\r
24500             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);\r
24501             this.textEl.setWidth(inner.offsetWidth);\r
24502         }\r
24503         this.progressBar.setHeight(inner.offsetHeight);\r
24504     },\r
24505     \r
24506     // private\r
24507     afterRender : function(){\r
24508         Ext.ProgressBar.superclass.afterRender.call(this);\r
24509         if(this.value){\r
24510             this.updateProgress(this.value, this.text);\r
24511         }else{\r
24512             this.updateText(this.text);\r
24513         }\r
24514     },\r
24515 \r
24516     /**\r
24517      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,\r
24518      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even\r
24519      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for\r
24520      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.\r
24521      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
24522      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
24523      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is\r
24524      * not specified, the default for the class is used (default to false)\r
24525      * @return {Ext.ProgressBar} this\r
24526      */\r
24527     updateProgress : function(value, text, animate){\r
24528         this.value = value || 0;\r
24529         if(text){\r
24530             this.updateText(text);\r
24531         }\r
24532         if(this.rendered){\r
24533             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);\r
24534             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));\r
24535             if(this.textTopEl){\r
24536                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves\r
24537                 this.textTopEl.removeClass('x-hidden').setWidth(w);\r
24538             }\r
24539         }\r
24540         this.fireEvent('update', this, value, text);\r
24541         return this;\r
24542     },\r
24543 \r
24544     /**\r
24545      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress\r
24546      * bar will automatically reset after a fixed amount of time and optionally call a callback function\r
24547      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must\r
24548      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with\r
24549      * the following properties:\r
24550      * <pre>\r
24551 Property   Type          Description\r
24552 ---------- ------------  ----------------------------------------------------------------------\r
24553 duration   Number        The length of time in milliseconds that the progress bar should\r
24554                          run before resetting itself (defaults to undefined, in which case it\r
24555                          will run indefinitely until reset is called)\r
24556 interval   Number        The length of time in milliseconds between each progress update\r
24557                          (defaults to 1000 ms)\r
24558 animate    Boolean       Whether to animate the transition of the progress bar. If this value is\r
24559                          not specified, the default for the class is used.                                                   \r
24560 increment  Number        The number of progress update segments to display within the progress\r
24561                          bar (defaults to 10).  If the bar reaches the end and is still\r
24562                          updating, it will automatically wrap back to the beginning.\r
24563 text       String        Optional text to display in the progress bar element (defaults to '').\r
24564 fn         Function      A callback function to execute after the progress bar finishes auto-\r
24565                          updating.  The function will be called with no arguments.  This function\r
24566                          will be ignored if duration is not specified since in that case the\r
24567                          progress bar can only be stopped programmatically, so any required function\r
24568                          should be called by the same code after it resets the progress bar.\r
24569 scope      Object        The scope that is passed to the callback function (only applies when\r
24570                          duration and fn are both passed).\r
24571 </pre>\r
24572          *\r
24573          * Example usage:\r
24574          * <pre><code>\r
24575 var p = new Ext.ProgressBar({\r
24576    renderTo: 'my-el'\r
24577 });\r
24578 \r
24579 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)\r
24580 p.wait({\r
24581    interval: 100, //bar will move fast!\r
24582    duration: 5000,\r
24583    increment: 15,\r
24584    text: 'Updating...',\r
24585    scope: this,\r
24586    fn: function(){\r
24587       Ext.fly('status').update('Done!');\r
24588    }\r
24589 });\r
24590 \r
24591 //Or update indefinitely until some async action completes, then reset manually\r
24592 p.wait();\r
24593 myAction.on('complete', function(){\r
24594     p.reset();\r
24595     Ext.fly('status').update('Done!');\r
24596 });\r
24597 </code></pre>\r
24598      * @param {Object} config (optional) Configuration options\r
24599      * @return {Ext.ProgressBar} this\r
24600      */\r
24601     wait : function(o){\r
24602         if(!this.waitTimer){\r
24603             var scope = this;\r
24604             o = o || {};\r
24605             this.updateText(o.text);\r
24606             this.waitTimer = Ext.TaskMgr.start({\r
24607                 run: function(i){\r
24608                     var inc = o.increment || 10;\r
24609                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);\r
24610                 },\r
24611                 interval: o.interval || 1000,\r
24612                 duration: o.duration,\r
24613                 onStop: function(){\r
24614                     if(o.fn){\r
24615                         o.fn.apply(o.scope || this);\r
24616                     }\r
24617                     this.reset();\r
24618                 },\r
24619                 scope: scope\r
24620             });\r
24621         }\r
24622         return this;\r
24623     },\r
24624 \r
24625     /**\r
24626      * Returns true if the progress bar is currently in a {@link #wait} operation\r
24627      * @return {Boolean} True if waiting, else false\r
24628      */\r
24629     isWaiting : function(){\r
24630         return this.waitTimer !== null;\r
24631     },\r
24632 \r
24633     /**\r
24634      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress\r
24635      * bar itself will display the updated text.\r
24636      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
24637      * @return {Ext.ProgressBar} this\r
24638      */\r
24639     updateText : function(text){\r
24640         this.text = text || '&#160;';\r
24641         if(this.rendered){\r
24642             this.textEl.update(this.text);\r
24643         }\r
24644         return this;\r
24645     },\r
24646     \r
24647     /**\r
24648      * Synchronizes the inner bar width to the proper proportion of the total componet width based\r
24649      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar\r
24650      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
24651      * another resize handler to sync the ProgressBar if necessary.\r
24652      */\r
24653     syncProgressBar : function(){\r
24654         if(this.value){\r
24655             this.updateProgress(this.value, this.text);\r
24656         }\r
24657         return this;\r
24658     },\r
24659 \r
24660     /**\r
24661      * Sets the size of the progress bar.\r
24662      * @param {Number} width The new width in pixels\r
24663      * @param {Number} height The new height in pixels\r
24664      * @return {Ext.ProgressBar} this\r
24665      */\r
24666     setSize : function(w, h){\r
24667         Ext.ProgressBar.superclass.setSize.call(this, w, h);\r
24668         if(this.textTopEl){\r
24669             var inner = this.el.dom.firstChild;\r
24670             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);\r
24671         }\r
24672         this.syncProgressBar();\r
24673         return this;\r
24674     },\r
24675 \r
24676     /**\r
24677      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress\r
24678      * bar will also be hidden (using the {@link #hideMode} property internally).\r
24679      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)\r
24680      * @return {Ext.ProgressBar} this\r
24681      */\r
24682     reset : function(hide){\r
24683         this.updateProgress(0);\r
24684         if(this.textTopEl){\r
24685             this.textTopEl.addClass('x-hidden');\r
24686         }\r
24687         if(this.waitTimer){\r
24688             this.waitTimer.onStop = null; //prevent recursion\r
24689             Ext.TaskMgr.stop(this.waitTimer);\r
24690             this.waitTimer = null;\r
24691         }\r
24692         if(hide === true){\r
24693             this.hide();\r
24694         }\r
24695         return this;\r
24696     }\r
24697 });\r
24698 Ext.reg('progress', Ext.ProgressBar);/*
24699  * These classes are derivatives of the similarly named classes in the YUI Library.
24700  * The original license:
24701  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
24702  * Code licensed under the BSD License:
24703  * http://developer.yahoo.net/yui/license.txt
24704  */
24705
24706 (function() {
24707
24708 var Event=Ext.EventManager;
24709 var Dom=Ext.lib.Dom;
24710
24711 /**
24712  * @class Ext.dd.DragDrop
24713  * Defines the interface and base operation of items that that can be
24714  * dragged or can be drop targets.  It was designed to be extended, overriding
24715  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
24716  * Up to three html elements can be associated with a DragDrop instance:
24717  * <ul>
24718  * <li>linked element: the element that is passed into the constructor.
24719  * This is the element which defines the boundaries for interaction with
24720  * other DragDrop objects.</li>
24721  * <li>handle element(s): The drag operation only occurs if the element that
24722  * was clicked matches a handle element.  By default this is the linked
24723  * element, but there are times that you will want only a portion of the
24724  * linked element to initiate the drag operation, and the setHandleElId()
24725  * method provides a way to define this.</li>
24726  * <li>drag element: this represents the element that would be moved along
24727  * with the cursor during a drag operation.  By default, this is the linked
24728  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
24729  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
24730  * </li>
24731  * </ul>
24732  * This class should not be instantiated until the onload event to ensure that
24733  * the associated elements are available.
24734  * The following would define a DragDrop obj that would interact with any
24735  * other DragDrop obj in the "group1" group:
24736  * <pre>
24737  *  dd = new Ext.dd.DragDrop("div1", "group1");
24738  * </pre>
24739  * Since none of the event handlers have been implemented, nothing would
24740  * actually happen if you were to run the code above.  Normally you would
24741  * override this class or one of the default implementations, but you can
24742  * also override the methods you want on an instance of the class...
24743  * <pre>
24744  *  dd.onDragDrop = function(e, id) {
24745  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
24746  *  }
24747  * </pre>
24748  * @constructor
24749  * @param {String} id of the element that is linked to this instance
24750  * @param {String} sGroup the group of related DragDrop objects
24751  * @param {object} config an object containing configurable attributes
24752  *                Valid properties for DragDrop:
24753  *                    padding, isTarget, maintainOffset, primaryButtonOnly
24754  */
24755 Ext.dd.DragDrop = function(id, sGroup, config) {
24756     if(id) {
24757         this.init(id, sGroup, config);
24758     }
24759 };
24760
24761 Ext.dd.DragDrop.prototype = {
24762
24763     /**
24764      * Set to false to enable a DragDrop object to fire drag events while dragging
24765      * over its own Element. Defaults to true - DragDrop objects do not by default
24766      * fire drag events to themselves.
24767      * @property ignoreSelf
24768      * @type Boolean
24769      */
24770
24771     /**
24772      * The id of the element associated with this object.  This is what we
24773      * refer to as the "linked element" because the size and position of
24774      * this element is used to determine when the drag and drop objects have
24775      * interacted.
24776      * @property id
24777      * @type String
24778      */
24779     id: null,
24780
24781     /**
24782      * Configuration attributes passed into the constructor
24783      * @property config
24784      * @type object
24785      */
24786     config: null,
24787
24788     /**
24789      * The id of the element that will be dragged.  By default this is same
24790      * as the linked element , but could be changed to another element. Ex:
24791      * Ext.dd.DDProxy
24792      * @property dragElId
24793      * @type String
24794      * @private
24795      */
24796     dragElId: null,
24797
24798     /**
24799      * The ID of the element that initiates the drag operation.  By default
24800      * this is the linked element, but could be changed to be a child of this
24801      * element.  This lets us do things like only starting the drag when the
24802      * header element within the linked html element is clicked.
24803      * @property handleElId
24804      * @type String
24805      * @private
24806      */
24807     handleElId: null,
24808
24809     /**
24810      * An object who's property names identify HTML tags to be considered invalid as drag handles.
24811      * A non-null property value identifies the tag as invalid. Defaults to the 
24812      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
24813 {
24814     A: "A"
24815 }</code></pre>
24816      * @property invalidHandleTypes
24817      * @type Object
24818      */
24819     invalidHandleTypes: null,
24820
24821     /**
24822      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
24823      * A non-null property value identifies the ID as invalid. For example, to prevent
24824      * dragging from being initiated on element ID "foo", use:<pre><code>
24825 {
24826     foo: true
24827 }</code></pre>
24828      * @property invalidHandleIds
24829      * @type Object
24830      */
24831     invalidHandleIds: null,
24832
24833     /**
24834      * An Array of CSS class names for elements to be considered in valid as drag handles.
24835      * @property invalidHandleClasses
24836      * @type Array
24837      */
24838     invalidHandleClasses: null,
24839
24840     /**
24841      * The linked element's absolute X position at the time the drag was
24842      * started
24843      * @property startPageX
24844      * @type int
24845      * @private
24846      */
24847     startPageX: 0,
24848
24849     /**
24850      * The linked element's absolute X position at the time the drag was
24851      * started
24852      * @property startPageY
24853      * @type int
24854      * @private
24855      */
24856     startPageY: 0,
24857
24858     /**
24859      * The group defines a logical collection of DragDrop objects that are
24860      * related.  Instances only get events when interacting with other
24861      * DragDrop object in the same group.  This lets us define multiple
24862      * groups using a single DragDrop subclass if we want.
24863      * @property groups
24864      * @type object An object in the format {'group1':true, 'group2':true}
24865      */
24866     groups: null,
24867
24868     /**
24869      * Individual drag/drop instances can be locked.  This will prevent
24870      * onmousedown start drag.
24871      * @property locked
24872      * @type boolean
24873      * @private
24874      */
24875     locked: false,
24876
24877     /**
24878      * Lock this instance
24879      * @method lock
24880      */
24881     lock: function() { this.locked = true; },
24882
24883     /**
24884      * When set to true, other DD objects in cooperating DDGroups do not receive
24885      * notification events when this DD object is dragged over them. Defaults to false.
24886      * @property moveOnly
24887      * @type boolean
24888      */
24889     moveOnly: false,
24890
24891     /**
24892      * Unlock this instace
24893      * @method unlock
24894      */
24895     unlock: function() { this.locked = false; },
24896
24897     /**
24898      * By default, all instances can be a drop target.  This can be disabled by
24899      * setting isTarget to false.
24900      * @property isTarget
24901      * @type boolean
24902      */
24903     isTarget: true,
24904
24905     /**
24906      * The padding configured for this drag and drop object for calculating
24907      * the drop zone intersection with this object.
24908      * @property padding
24909      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
24910      */
24911     padding: null,
24912
24913     /**
24914      * Cached reference to the linked element
24915      * @property _domRef
24916      * @private
24917      */
24918     _domRef: null,
24919
24920     /**
24921      * Internal typeof flag
24922      * @property __ygDragDrop
24923      * @private
24924      */
24925     __ygDragDrop: true,
24926
24927     /**
24928      * Set to true when horizontal contraints are applied
24929      * @property constrainX
24930      * @type boolean
24931      * @private
24932      */
24933     constrainX: false,
24934
24935     /**
24936      * Set to true when vertical contraints are applied
24937      * @property constrainY
24938      * @type boolean
24939      * @private
24940      */
24941     constrainY: false,
24942
24943     /**
24944      * The left constraint
24945      * @property minX
24946      * @type int
24947      * @private
24948      */
24949     minX: 0,
24950
24951     /**
24952      * The right constraint
24953      * @property maxX
24954      * @type int
24955      * @private
24956      */
24957     maxX: 0,
24958
24959     /**
24960      * The up constraint
24961      * @property minY
24962      * @type int
24963      * @type int
24964      * @private
24965      */
24966     minY: 0,
24967
24968     /**
24969      * The down constraint
24970      * @property maxY
24971      * @type int
24972      * @private
24973      */
24974     maxY: 0,
24975
24976     /**
24977      * Maintain offsets when we resetconstraints.  Set to true when you want
24978      * the position of the element relative to its parent to stay the same
24979      * when the page changes
24980      *
24981      * @property maintainOffset
24982      * @type boolean
24983      */
24984     maintainOffset: false,
24985
24986     /**
24987      * Array of pixel locations the element will snap to if we specified a
24988      * horizontal graduation/interval.  This array is generated automatically
24989      * when you define a tick interval.
24990      * @property xTicks
24991      * @type int[]
24992      */
24993     xTicks: null,
24994
24995     /**
24996      * Array of pixel locations the element will snap to if we specified a
24997      * vertical graduation/interval.  This array is generated automatically
24998      * when you define a tick interval.
24999      * @property yTicks
25000      * @type int[]
25001      */
25002     yTicks: null,
25003
25004     /**
25005      * By default the drag and drop instance will only respond to the primary
25006      * button click (left button for a right-handed mouse).  Set to true to
25007      * allow drag and drop to start with any mouse click that is propogated
25008      * by the browser
25009      * @property primaryButtonOnly
25010      * @type boolean
25011      */
25012     primaryButtonOnly: true,
25013
25014     /**
25015      * The availabe property is false until the linked dom element is accessible.
25016      * @property available
25017      * @type boolean
25018      */
25019     available: false,
25020
25021     /**
25022      * By default, drags can only be initiated if the mousedown occurs in the
25023      * region the linked element is.  This is done in part to work around a
25024      * bug in some browsers that mis-report the mousedown if the previous
25025      * mouseup happened outside of the window.  This property is set to true
25026      * if outer handles are defined.
25027      *
25028      * @property hasOuterHandles
25029      * @type boolean
25030      * @default false
25031      */
25032     hasOuterHandles: false,
25033
25034     /**
25035      * Code that executes immediately before the startDrag event
25036      * @method b4StartDrag
25037      * @private
25038      */
25039     b4StartDrag: function(x, y) { },
25040
25041     /**
25042      * Abstract method called after a drag/drop object is clicked
25043      * and the drag or mousedown time thresholds have beeen met.
25044      * @method startDrag
25045      * @param {int} X click location
25046      * @param {int} Y click location
25047      */
25048     startDrag: function(x, y) { /* override this */ },
25049
25050     /**
25051      * Code that executes immediately before the onDrag event
25052      * @method b4Drag
25053      * @private
25054      */
25055     b4Drag: function(e) { },
25056
25057     /**
25058      * Abstract method called during the onMouseMove event while dragging an
25059      * object.
25060      * @method onDrag
25061      * @param {Event} e the mousemove event
25062      */
25063     onDrag: function(e) { /* override this */ },
25064
25065     /**
25066      * Abstract method called when this element fist begins hovering over
25067      * another DragDrop obj
25068      * @method onDragEnter
25069      * @param {Event} e the mousemove event
25070      * @param {String|DragDrop[]} id In POINT mode, the element
25071      * id this is hovering over.  In INTERSECT mode, an array of one or more
25072      * dragdrop items being hovered over.
25073      */
25074     onDragEnter: function(e, id) { /* override this */ },
25075
25076     /**
25077      * Code that executes immediately before the onDragOver event
25078      * @method b4DragOver
25079      * @private
25080      */
25081     b4DragOver: function(e) { },
25082
25083     /**
25084      * Abstract method called when this element is hovering over another
25085      * DragDrop obj
25086      * @method onDragOver
25087      * @param {Event} e the mousemove event
25088      * @param {String|DragDrop[]} id In POINT mode, the element
25089      * id this is hovering over.  In INTERSECT mode, an array of dd items
25090      * being hovered over.
25091      */
25092     onDragOver: function(e, id) { /* override this */ },
25093
25094     /**
25095      * Code that executes immediately before the onDragOut event
25096      * @method b4DragOut
25097      * @private
25098      */
25099     b4DragOut: function(e) { },
25100
25101     /**
25102      * Abstract method called when we are no longer hovering over an element
25103      * @method onDragOut
25104      * @param {Event} e the mousemove event
25105      * @param {String|DragDrop[]} id In POINT mode, the element
25106      * id this was hovering over.  In INTERSECT mode, an array of dd items
25107      * that the mouse is no longer over.
25108      */
25109     onDragOut: function(e, id) { /* override this */ },
25110
25111     /**
25112      * Code that executes immediately before the onDragDrop event
25113      * @method b4DragDrop
25114      * @private
25115      */
25116     b4DragDrop: function(e) { },
25117
25118     /**
25119      * Abstract method called when this item is dropped on another DragDrop
25120      * obj
25121      * @method onDragDrop
25122      * @param {Event} e the mouseup event
25123      * @param {String|DragDrop[]} id In POINT mode, the element
25124      * id this was dropped on.  In INTERSECT mode, an array of dd items this
25125      * was dropped on.
25126      */
25127     onDragDrop: function(e, id) { /* override this */ },
25128
25129     /**
25130      * Abstract method called when this item is dropped on an area with no
25131      * drop target
25132      * @method onInvalidDrop
25133      * @param {Event} e the mouseup event
25134      */
25135     onInvalidDrop: function(e) { /* override this */ },
25136
25137     /**
25138      * Code that executes immediately before the endDrag event
25139      * @method b4EndDrag
25140      * @private
25141      */
25142     b4EndDrag: function(e) { },
25143
25144     /**
25145      * Fired when we are done dragging the object
25146      * @method endDrag
25147      * @param {Event} e the mouseup event
25148      */
25149     endDrag: function(e) { /* override this */ },
25150
25151     /**
25152      * Code executed immediately before the onMouseDown event
25153      * @method b4MouseDown
25154      * @param {Event} e the mousedown event
25155      * @private
25156      */
25157     b4MouseDown: function(e) {  },
25158
25159     /**
25160      * Event handler that fires when a drag/drop obj gets a mousedown
25161      * @method onMouseDown
25162      * @param {Event} e the mousedown event
25163      */
25164     onMouseDown: function(e) { /* override this */ },
25165
25166     /**
25167      * Event handler that fires when a drag/drop obj gets a mouseup
25168      * @method onMouseUp
25169      * @param {Event} e the mouseup event
25170      */
25171     onMouseUp: function(e) { /* override this */ },
25172
25173     /**
25174      * Override the onAvailable method to do what is needed after the initial
25175      * position was determined.
25176      * @method onAvailable
25177      */
25178     onAvailable: function () {
25179     },
25180
25181     /**
25182      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
25183      * @type Object
25184      */
25185     defaultPadding : {left:0, right:0, top:0, bottom:0},
25186
25187     /**
25188      * Initializes the drag drop object's constraints to restrict movement to a certain element.
25189  *
25190  * Usage:
25191  <pre><code>
25192  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
25193                 { dragElId: "existingProxyDiv" });
25194  dd.startDrag = function(){
25195      this.constrainTo("parent-id");
25196  };
25197  </code></pre>
25198  * Or you can initalize it using the {@link Ext.Element} object:
25199  <pre><code>
25200  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
25201      startDrag : function(){
25202          this.constrainTo("parent-id");
25203      }
25204  });
25205  </code></pre>
25206      * @param {Mixed} constrainTo The element to constrain to.
25207      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
25208      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
25209      * an object containing the sides to pad. For example: {right:10, bottom:10}
25210      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
25211      */
25212     constrainTo : function(constrainTo, pad, inContent){
25213         if(typeof pad == "number"){
25214             pad = {left: pad, right:pad, top:pad, bottom:pad};
25215         }
25216         pad = pad || this.defaultPadding;
25217         var b = Ext.get(this.getEl()).getBox();
25218         var ce = Ext.get(constrainTo);
25219         var s = ce.getScroll();
25220         var c, cd = ce.dom;
25221         if(cd == document.body){
25222             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
25223         }else{
25224             var xy = ce.getXY();
25225             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
25226         }
25227
25228
25229         var topSpace = b.y - c.y;
25230         var leftSpace = b.x - c.x;
25231
25232         this.resetConstraints();
25233         this.setXConstraint(leftSpace - (pad.left||0), // left
25234                 c.width - leftSpace - b.width - (pad.right||0), //right
25235                                 this.xTickSize
25236         );
25237         this.setYConstraint(topSpace - (pad.top||0), //top
25238                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
25239                                 this.yTickSize
25240         );
25241     },
25242
25243     /**
25244      * Returns a reference to the linked element
25245      * @method getEl
25246      * @return {HTMLElement} the html element
25247      */
25248     getEl: function() {
25249         if (!this._domRef) {
25250             this._domRef = Ext.getDom(this.id);
25251         }
25252
25253         return this._domRef;
25254     },
25255
25256     /**
25257      * Returns a reference to the actual element to drag.  By default this is
25258      * the same as the html element, but it can be assigned to another
25259      * element. An example of this can be found in Ext.dd.DDProxy
25260      * @method getDragEl
25261      * @return {HTMLElement} the html element
25262      */
25263     getDragEl: function() {
25264         return Ext.getDom(this.dragElId);
25265     },
25266
25267     /**
25268      * Sets up the DragDrop object.  Must be called in the constructor of any
25269      * Ext.dd.DragDrop subclass
25270      * @method init
25271      * @param id the id of the linked element
25272      * @param {String} sGroup the group of related items
25273      * @param {object} config configuration attributes
25274      */
25275     init: function(id, sGroup, config) {
25276         this.initTarget(id, sGroup, config);
25277         Event.on(this.id, "mousedown", this.handleMouseDown, this);
25278         // Event.on(this.id, "selectstart", Event.preventDefault);
25279     },
25280
25281     /**
25282      * Initializes Targeting functionality only... the object does not
25283      * get a mousedown handler.
25284      * @method initTarget
25285      * @param id the id of the linked element
25286      * @param {String} sGroup the group of related items
25287      * @param {object} config configuration attributes
25288      */
25289     initTarget: function(id, sGroup, config) {
25290
25291         // configuration attributes
25292         this.config = config || {};
25293
25294         // create a local reference to the drag and drop manager
25295         this.DDM = Ext.dd.DDM;
25296         // initialize the groups array
25297         this.groups = {};
25298
25299         // assume that we have an element reference instead of an id if the
25300         // parameter is not a string
25301         if (typeof id !== "string") {
25302             id = Ext.id(id);
25303         }
25304
25305         // set the id
25306         this.id = id;
25307
25308         // add to an interaction group
25309         this.addToGroup((sGroup) ? sGroup : "default");
25310
25311         // We don't want to register this as the handle with the manager
25312         // so we just set the id rather than calling the setter.
25313         this.handleElId = id;
25314
25315         // the linked element is the element that gets dragged by default
25316         this.setDragElId(id);
25317
25318         // by default, clicked anchors will not start drag operations.
25319         this.invalidHandleTypes = { A: "A" };
25320         this.invalidHandleIds = {};
25321         this.invalidHandleClasses = [];
25322
25323         this.applyConfig();
25324
25325         this.handleOnAvailable();
25326     },
25327
25328     /**
25329      * Applies the configuration parameters that were passed into the constructor.
25330      * This is supposed to happen at each level through the inheritance chain.  So
25331      * a DDProxy implentation will execute apply config on DDProxy, DD, and
25332      * DragDrop in order to get all of the parameters that are available in
25333      * each object.
25334      * @method applyConfig
25335      */
25336     applyConfig: function() {
25337
25338         // configurable properties:
25339         //    padding, isTarget, maintainOffset, primaryButtonOnly
25340         this.padding           = this.config.padding || [0, 0, 0, 0];
25341         this.isTarget          = (this.config.isTarget !== false);
25342         this.maintainOffset    = (this.config.maintainOffset);
25343         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
25344
25345     },
25346
25347     /**
25348      * Executed when the linked element is available
25349      * @method handleOnAvailable
25350      * @private
25351      */
25352     handleOnAvailable: function() {
25353         this.available = true;
25354         this.resetConstraints();
25355         this.onAvailable();
25356     },
25357
25358      /**
25359      * Configures the padding for the target zone in px.  Effectively expands
25360      * (or reduces) the virtual object size for targeting calculations.
25361      * Supports css-style shorthand; if only one parameter is passed, all sides
25362      * will have that padding, and if only two are passed, the top and bottom
25363      * will have the first param, the left and right the second.
25364      * @method setPadding
25365      * @param {int} iTop    Top pad
25366      * @param {int} iRight  Right pad
25367      * @param {int} iBot    Bot pad
25368      * @param {int} iLeft   Left pad
25369      */
25370     setPadding: function(iTop, iRight, iBot, iLeft) {
25371         // this.padding = [iLeft, iRight, iTop, iBot];
25372         if (!iRight && 0 !== iRight) {
25373             this.padding = [iTop, iTop, iTop, iTop];
25374         } else if (!iBot && 0 !== iBot) {
25375             this.padding = [iTop, iRight, iTop, iRight];
25376         } else {
25377             this.padding = [iTop, iRight, iBot, iLeft];
25378         }
25379     },
25380
25381     /**
25382      * Stores the initial placement of the linked element.
25383      * @method setInitPosition
25384      * @param {int} diffX   the X offset, default 0
25385      * @param {int} diffY   the Y offset, default 0
25386      */
25387     setInitPosition: function(diffX, diffY) {
25388         var el = this.getEl();
25389
25390         if (!this.DDM.verifyEl(el)) {
25391             return;
25392         }
25393
25394         var dx = diffX || 0;
25395         var dy = diffY || 0;
25396
25397         var p = Dom.getXY( el );
25398
25399         this.initPageX = p[0] - dx;
25400         this.initPageY = p[1] - dy;
25401
25402         this.lastPageX = p[0];
25403         this.lastPageY = p[1];
25404
25405
25406         this.setStartPosition(p);
25407     },
25408
25409     /**
25410      * Sets the start position of the element.  This is set when the obj
25411      * is initialized, the reset when a drag is started.
25412      * @method setStartPosition
25413      * @param pos current position (from previous lookup)
25414      * @private
25415      */
25416     setStartPosition: function(pos) {
25417         var p = pos || Dom.getXY( this.getEl() );
25418         this.deltaSetXY = null;
25419
25420         this.startPageX = p[0];
25421         this.startPageY = p[1];
25422     },
25423
25424     /**
25425      * Add this instance to a group of related drag/drop objects.  All
25426      * instances belong to at least one group, and can belong to as many
25427      * groups as needed.
25428      * @method addToGroup
25429      * @param sGroup {string} the name of the group
25430      */
25431     addToGroup: function(sGroup) {
25432         this.groups[sGroup] = true;
25433         this.DDM.regDragDrop(this, sGroup);
25434     },
25435
25436     /**
25437      * Remove's this instance from the supplied interaction group
25438      * @method removeFromGroup
25439      * @param {string}  sGroup  The group to drop
25440      */
25441     removeFromGroup: function(sGroup) {
25442         if (this.groups[sGroup]) {
25443             delete this.groups[sGroup];
25444         }
25445
25446         this.DDM.removeDDFromGroup(this, sGroup);
25447     },
25448
25449     /**
25450      * Allows you to specify that an element other than the linked element
25451      * will be moved with the cursor during a drag
25452      * @method setDragElId
25453      * @param id {string} the id of the element that will be used to initiate the drag
25454      */
25455     setDragElId: function(id) {
25456         this.dragElId = id;
25457     },
25458
25459     /**
25460      * Allows you to specify a child of the linked element that should be
25461      * used to initiate the drag operation.  An example of this would be if
25462      * you have a content div with text and links.  Clicking anywhere in the
25463      * content area would normally start the drag operation.  Use this method
25464      * to specify that an element inside of the content div is the element
25465      * that starts the drag operation.
25466      * @method setHandleElId
25467      * @param id {string} the id of the element that will be used to
25468      * initiate the drag.
25469      */
25470     setHandleElId: function(id) {
25471         if (typeof id !== "string") {
25472             id = Ext.id(id);
25473         }
25474         this.handleElId = id;
25475         this.DDM.regHandle(this.id, id);
25476     },
25477
25478     /**
25479      * Allows you to set an element outside of the linked element as a drag
25480      * handle
25481      * @method setOuterHandleElId
25482      * @param id the id of the element that will be used to initiate the drag
25483      */
25484     setOuterHandleElId: function(id) {
25485         if (typeof id !== "string") {
25486             id = Ext.id(id);
25487         }
25488         Event.on(id, "mousedown",
25489                 this.handleMouseDown, this);
25490         this.setHandleElId(id);
25491
25492         this.hasOuterHandles = true;
25493     },
25494
25495     /**
25496      * Remove all drag and drop hooks for this element
25497      * @method unreg
25498      */
25499     unreg: function() {
25500         Event.un(this.id, "mousedown",
25501                 this.handleMouseDown);
25502         this._domRef = null;
25503         this.DDM._remove(this);
25504     },
25505
25506     destroy : function(){
25507         this.unreg();
25508     },
25509
25510     /**
25511      * Returns true if this instance is locked, or the drag drop mgr is locked
25512      * (meaning that all drag/drop is disabled on the page.)
25513      * @method isLocked
25514      * @return {boolean} true if this obj or all drag/drop is locked, else
25515      * false
25516      */
25517     isLocked: function() {
25518         return (this.DDM.isLocked() || this.locked);
25519     },
25520
25521     /**
25522      * Fired when this object is clicked
25523      * @method handleMouseDown
25524      * @param {Event} e
25525      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
25526      * @private
25527      */
25528     handleMouseDown: function(e, oDD){
25529         if (this.primaryButtonOnly && e.button != 0) {
25530             return;
25531         }
25532
25533         if (this.isLocked()) {
25534             return;
25535         }
25536
25537         this.DDM.refreshCache(this.groups);
25538
25539         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
25540         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
25541         } else {
25542             if (this.clickValidator(e)) {
25543
25544                 // set the initial element position
25545                 this.setStartPosition();
25546
25547
25548                 this.b4MouseDown(e);
25549                 this.onMouseDown(e);
25550
25551                 this.DDM.handleMouseDown(e, this);
25552
25553                 this.DDM.stopEvent(e);
25554             } else {
25555
25556
25557             }
25558         }
25559     },
25560
25561     clickValidator: function(e) {
25562         var target = e.getTarget();
25563         return ( this.isValidHandleChild(target) &&
25564                     (this.id == this.handleElId ||
25565                         this.DDM.handleWasClicked(target, this.id)) );
25566     },
25567
25568     /**
25569      * Allows you to specify a tag name that should not start a drag operation
25570      * when clicked.  This is designed to facilitate embedding links within a
25571      * drag handle that do something other than start the drag.
25572      * @method addInvalidHandleType
25573      * @param {string} tagName the type of element to exclude
25574      */
25575     addInvalidHandleType: function(tagName) {
25576         var type = tagName.toUpperCase();
25577         this.invalidHandleTypes[type] = type;
25578     },
25579
25580     /**
25581      * Lets you to specify an element id for a child of a drag handle
25582      * that should not initiate a drag
25583      * @method addInvalidHandleId
25584      * @param {string} id the element id of the element you wish to ignore
25585      */
25586     addInvalidHandleId: function(id) {
25587         if (typeof id !== "string") {
25588             id = Ext.id(id);
25589         }
25590         this.invalidHandleIds[id] = id;
25591     },
25592
25593     /**
25594      * Lets you specify a css class of elements that will not initiate a drag
25595      * @method addInvalidHandleClass
25596      * @param {string} cssClass the class of the elements you wish to ignore
25597      */
25598     addInvalidHandleClass: function(cssClass) {
25599         this.invalidHandleClasses.push(cssClass);
25600     },
25601
25602     /**
25603      * Unsets an excluded tag name set by addInvalidHandleType
25604      * @method removeInvalidHandleType
25605      * @param {string} tagName the type of element to unexclude
25606      */
25607     removeInvalidHandleType: function(tagName) {
25608         var type = tagName.toUpperCase();
25609         // this.invalidHandleTypes[type] = null;
25610         delete this.invalidHandleTypes[type];
25611     },
25612
25613     /**
25614      * Unsets an invalid handle id
25615      * @method removeInvalidHandleId
25616      * @param {string} id the id of the element to re-enable
25617      */
25618     removeInvalidHandleId: function(id) {
25619         if (typeof id !== "string") {
25620             id = Ext.id(id);
25621         }
25622         delete this.invalidHandleIds[id];
25623     },
25624
25625     /**
25626      * Unsets an invalid css class
25627      * @method removeInvalidHandleClass
25628      * @param {string} cssClass the class of the element(s) you wish to
25629      * re-enable
25630      */
25631     removeInvalidHandleClass: function(cssClass) {
25632         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
25633             if (this.invalidHandleClasses[i] == cssClass) {
25634                 delete this.invalidHandleClasses[i];
25635             }
25636         }
25637     },
25638
25639     /**
25640      * Checks the tag exclusion list to see if this click should be ignored
25641      * @method isValidHandleChild
25642      * @param {HTMLElement} node the HTMLElement to evaluate
25643      * @return {boolean} true if this is a valid tag type, false if not
25644      */
25645     isValidHandleChild: function(node) {
25646
25647         var valid = true;
25648         // var n = (node.nodeName == "#text") ? node.parentNode : node;
25649         var nodeName;
25650         try {
25651             nodeName = node.nodeName.toUpperCase();
25652         } catch(e) {
25653             nodeName = node.nodeName;
25654         }
25655         valid = valid && !this.invalidHandleTypes[nodeName];
25656         valid = valid && !this.invalidHandleIds[node.id];
25657
25658         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
25659             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
25660         }
25661
25662
25663         return valid;
25664
25665     },
25666
25667     /**
25668      * Create the array of horizontal tick marks if an interval was specified
25669      * in setXConstraint().
25670      * @method setXTicks
25671      * @private
25672      */
25673     setXTicks: function(iStartX, iTickSize) {
25674         this.xTicks = [];
25675         this.xTickSize = iTickSize;
25676
25677         var tickMap = {};
25678
25679         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
25680             if (!tickMap[i]) {
25681                 this.xTicks[this.xTicks.length] = i;
25682                 tickMap[i] = true;
25683             }
25684         }
25685
25686         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
25687             if (!tickMap[i]) {
25688                 this.xTicks[this.xTicks.length] = i;
25689                 tickMap[i] = true;
25690             }
25691         }
25692
25693         this.xTicks.sort(this.DDM.numericSort) ;
25694     },
25695
25696     /**
25697      * Create the array of vertical tick marks if an interval was specified in
25698      * setYConstraint().
25699      * @method setYTicks
25700      * @private
25701      */
25702     setYTicks: function(iStartY, iTickSize) {
25703         this.yTicks = [];
25704         this.yTickSize = iTickSize;
25705
25706         var tickMap = {};
25707
25708         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
25709             if (!tickMap[i]) {
25710                 this.yTicks[this.yTicks.length] = i;
25711                 tickMap[i] = true;
25712             }
25713         }
25714
25715         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
25716             if (!tickMap[i]) {
25717                 this.yTicks[this.yTicks.length] = i;
25718                 tickMap[i] = true;
25719             }
25720         }
25721
25722         this.yTicks.sort(this.DDM.numericSort) ;
25723     },
25724
25725     /**
25726      * By default, the element can be dragged any place on the screen.  Use
25727      * this method to limit the horizontal travel of the element.  Pass in
25728      * 0,0 for the parameters if you want to lock the drag to the y axis.
25729      * @method setXConstraint
25730      * @param {int} iLeft the number of pixels the element can move to the left
25731      * @param {int} iRight the number of pixels the element can move to the
25732      * right
25733      * @param {int} iTickSize optional parameter for specifying that the
25734      * element
25735      * should move iTickSize pixels at a time.
25736      */
25737     setXConstraint: function(iLeft, iRight, iTickSize) {
25738         this.leftConstraint = iLeft;
25739         this.rightConstraint = iRight;
25740
25741         this.minX = this.initPageX - iLeft;
25742         this.maxX = this.initPageX + iRight;
25743         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
25744
25745         this.constrainX = true;
25746     },
25747
25748     /**
25749      * Clears any constraints applied to this instance.  Also clears ticks
25750      * since they can't exist independent of a constraint at this time.
25751      * @method clearConstraints
25752      */
25753     clearConstraints: function() {
25754         this.constrainX = false;
25755         this.constrainY = false;
25756         this.clearTicks();
25757     },
25758
25759     /**
25760      * Clears any tick interval defined for this instance
25761      * @method clearTicks
25762      */
25763     clearTicks: function() {
25764         this.xTicks = null;
25765         this.yTicks = null;
25766         this.xTickSize = 0;
25767         this.yTickSize = 0;
25768     },
25769
25770     /**
25771      * By default, the element can be dragged any place on the screen.  Set
25772      * this to limit the vertical travel of the element.  Pass in 0,0 for the
25773      * parameters if you want to lock the drag to the x axis.
25774      * @method setYConstraint
25775      * @param {int} iUp the number of pixels the element can move up
25776      * @param {int} iDown the number of pixels the element can move down
25777      * @param {int} iTickSize optional parameter for specifying that the
25778      * element should move iTickSize pixels at a time.
25779      */
25780     setYConstraint: function(iUp, iDown, iTickSize) {
25781         this.topConstraint = iUp;
25782         this.bottomConstraint = iDown;
25783
25784         this.minY = this.initPageY - iUp;
25785         this.maxY = this.initPageY + iDown;
25786         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
25787
25788         this.constrainY = true;
25789
25790     },
25791
25792     /**
25793      * resetConstraints must be called if you manually reposition a dd element.
25794      * @method resetConstraints
25795      * @param {boolean} maintainOffset
25796      */
25797     resetConstraints: function() {
25798
25799
25800         // Maintain offsets if necessary
25801         if (this.initPageX || this.initPageX === 0) {
25802             // figure out how much this thing has moved
25803             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
25804             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
25805
25806             this.setInitPosition(dx, dy);
25807
25808         // This is the first time we have detected the element's position
25809         } else {
25810             this.setInitPosition();
25811         }
25812
25813         if (this.constrainX) {
25814             this.setXConstraint( this.leftConstraint,
25815                                  this.rightConstraint,
25816                                  this.xTickSize        );
25817         }
25818
25819         if (this.constrainY) {
25820             this.setYConstraint( this.topConstraint,
25821                                  this.bottomConstraint,
25822                                  this.yTickSize         );
25823         }
25824     },
25825
25826     /**
25827      * Normally the drag element is moved pixel by pixel, but we can specify
25828      * that it move a number of pixels at a time.  This method resolves the
25829      * location when we have it set up like this.
25830      * @method getTick
25831      * @param {int} val where we want to place the object
25832      * @param {int[]} tickArray sorted array of valid points
25833      * @return {int} the closest tick
25834      * @private
25835      */
25836     getTick: function(val, tickArray) {
25837
25838         if (!tickArray) {
25839             // If tick interval is not defined, it is effectively 1 pixel,
25840             // so we return the value passed to us.
25841             return val;
25842         } else if (tickArray[0] >= val) {
25843             // The value is lower than the first tick, so we return the first
25844             // tick.
25845             return tickArray[0];
25846         } else {
25847             for (var i=0, len=tickArray.length; i<len; ++i) {
25848                 var next = i + 1;
25849                 if (tickArray[next] && tickArray[next] >= val) {
25850                     var diff1 = val - tickArray[i];
25851                     var diff2 = tickArray[next] - val;
25852                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
25853                 }
25854             }
25855
25856             // The value is larger than the last tick, so we return the last
25857             // tick.
25858             return tickArray[tickArray.length - 1];
25859         }
25860     },
25861
25862     /**
25863      * toString method
25864      * @method toString
25865      * @return {string} string representation of the dd obj
25866      */
25867     toString: function() {
25868         return ("DragDrop " + this.id);
25869     }
25870
25871 };
25872
25873 })();
25874 /**
25875  * The drag and drop utility provides a framework for building drag and drop
25876  * applications.  In addition to enabling drag and drop for specific elements,
25877  * the drag and drop elements are tracked by the manager class, and the
25878  * interactions between the various elements are tracked during the drag and
25879  * the implementing code is notified about these important moments.
25880  */
25881
25882 // Only load the library once.  Rewriting the manager class would orphan
25883 // existing drag and drop instances.
25884 if (!Ext.dd.DragDropMgr) {
25885
25886 /**
25887  * @class Ext.dd.DragDropMgr
25888  * DragDropMgr is a singleton that tracks the element interaction for
25889  * all DragDrop items in the window.  Generally, you will not call
25890  * this class directly, but it does have helper methods that could
25891  * be useful in your DragDrop implementations.
25892  * @singleton
25893  */
25894 Ext.dd.DragDropMgr = function() {
25895
25896     var Event = Ext.EventManager;
25897
25898     return {
25899
25900         /**
25901          * Two dimensional Array of registered DragDrop objects.  The first
25902          * dimension is the DragDrop item group, the second the DragDrop
25903          * object.
25904          * @property ids
25905          * @type {string: string}
25906          * @private
25907          * @static
25908          */
25909         ids: {},
25910
25911         /**
25912          * Array of element ids defined as drag handles.  Used to determine
25913          * if the element that generated the mousedown event is actually the
25914          * handle and not the html element itself.
25915          * @property handleIds
25916          * @type {string: string}
25917          * @private
25918          * @static
25919          */
25920         handleIds: {},
25921
25922         /**
25923          * the DragDrop object that is currently being dragged
25924          * @property dragCurrent
25925          * @type DragDrop
25926          * @private
25927          * @static
25928          **/
25929         dragCurrent: null,
25930
25931         /**
25932          * the DragDrop object(s) that are being hovered over
25933          * @property dragOvers
25934          * @type Array
25935          * @private
25936          * @static
25937          */
25938         dragOvers: {},
25939
25940         /**
25941          * the X distance between the cursor and the object being dragged
25942          * @property deltaX
25943          * @type int
25944          * @private
25945          * @static
25946          */
25947         deltaX: 0,
25948
25949         /**
25950          * the Y distance between the cursor and the object being dragged
25951          * @property deltaY
25952          * @type int
25953          * @private
25954          * @static
25955          */
25956         deltaY: 0,
25957
25958         /**
25959          * Flag to determine if we should prevent the default behavior of the
25960          * events we define. By default this is true, but this can be set to
25961          * false if you need the default behavior (not recommended)
25962          * @property preventDefault
25963          * @type boolean
25964          * @static
25965          */
25966         preventDefault: true,
25967
25968         /**
25969          * Flag to determine if we should stop the propagation of the events
25970          * we generate. This is true by default but you may want to set it to
25971          * false if the html element contains other features that require the
25972          * mouse click.
25973          * @property stopPropagation
25974          * @type boolean
25975          * @static
25976          */
25977         stopPropagation: true,
25978
25979         /**
25980          * Internal flag that is set to true when drag and drop has been
25981          * intialized
25982          * @property initialized
25983          * @private
25984          * @static
25985          */
25986         initialized: false,
25987
25988         /**
25989          * All drag and drop can be disabled.
25990          * @property locked
25991          * @private
25992          * @static
25993          */
25994         locked: false,
25995
25996         /**
25997          * Called the first time an element is registered.
25998          * @method init
25999          * @private
26000          * @static
26001          */
26002         init: function() {
26003             this.initialized = true;
26004         },
26005
26006         /**
26007          * In point mode, drag and drop interaction is defined by the
26008          * location of the cursor during the drag/drop
26009          * @property POINT
26010          * @type int
26011          * @static
26012          */
26013         POINT: 0,
26014
26015         /**
26016          * In intersect mode, drag and drop interaction is defined by the
26017          * overlap of two or more drag and drop objects.
26018          * @property INTERSECT
26019          * @type int
26020          * @static
26021          */
26022         INTERSECT: 1,
26023
26024         /**
26025          * The current drag and drop mode.  Default: POINT
26026          * @property mode
26027          * @type int
26028          * @static
26029          */
26030         mode: 0,
26031
26032         /**
26033          * Runs method on all drag and drop objects
26034          * @method _execOnAll
26035          * @private
26036          * @static
26037          */
26038         _execOnAll: function(sMethod, args) {
26039             for (var i in this.ids) {
26040                 for (var j in this.ids[i]) {
26041                     var oDD = this.ids[i][j];
26042                     if (! this.isTypeOfDD(oDD)) {
26043                         continue;
26044                     }
26045                     oDD[sMethod].apply(oDD, args);
26046                 }
26047             }
26048         },
26049
26050         /**
26051          * Drag and drop initialization.  Sets up the global event handlers
26052          * @method _onLoad
26053          * @private
26054          * @static
26055          */
26056         _onLoad: function() {
26057
26058             this.init();
26059
26060
26061             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
26062             Event.on(document, "mousemove", this.handleMouseMove, this, true);
26063             Event.on(window,   "unload",    this._onUnload, this, true);
26064             Event.on(window,   "resize",    this._onResize, this, true);
26065             // Event.on(window,   "mouseout",    this._test);
26066
26067         },
26068
26069         /**
26070          * Reset constraints on all drag and drop objs
26071          * @method _onResize
26072          * @private
26073          * @static
26074          */
26075         _onResize: function(e) {
26076             this._execOnAll("resetConstraints", []);
26077         },
26078
26079         /**
26080          * Lock all drag and drop functionality
26081          * @method lock
26082          * @static
26083          */
26084         lock: function() { this.locked = true; },
26085
26086         /**
26087          * Unlock all drag and drop functionality
26088          * @method unlock
26089          * @static
26090          */
26091         unlock: function() { this.locked = false; },
26092
26093         /**
26094          * Is drag and drop locked?
26095          * @method isLocked
26096          * @return {boolean} True if drag and drop is locked, false otherwise.
26097          * @static
26098          */
26099         isLocked: function() { return this.locked; },
26100
26101         /**
26102          * Location cache that is set for all drag drop objects when a drag is
26103          * initiated, cleared when the drag is finished.
26104          * @property locationCache
26105          * @private
26106          * @static
26107          */
26108         locationCache: {},
26109
26110         /**
26111          * Set useCache to false if you want to force object the lookup of each
26112          * drag and drop linked element constantly during a drag.
26113          * @property useCache
26114          * @type boolean
26115          * @static
26116          */
26117         useCache: true,
26118
26119         /**
26120          * The number of pixels that the mouse needs to move after the
26121          * mousedown before the drag is initiated.  Default=3;
26122          * @property clickPixelThresh
26123          * @type int
26124          * @static
26125          */
26126         clickPixelThresh: 3,
26127
26128         /**
26129          * The number of milliseconds after the mousedown event to initiate the
26130          * drag if we don't get a mouseup event. Default=1000
26131          * @property clickTimeThresh
26132          * @type int
26133          * @static
26134          */
26135         clickTimeThresh: 350,
26136
26137         /**
26138          * Flag that indicates that either the drag pixel threshold or the
26139          * mousdown time threshold has been met
26140          * @property dragThreshMet
26141          * @type boolean
26142          * @private
26143          * @static
26144          */
26145         dragThreshMet: false,
26146
26147         /**
26148          * Timeout used for the click time threshold
26149          * @property clickTimeout
26150          * @type Object
26151          * @private
26152          * @static
26153          */
26154         clickTimeout: null,
26155
26156         /**
26157          * The X position of the mousedown event stored for later use when a
26158          * drag threshold is met.
26159          * @property startX
26160          * @type int
26161          * @private
26162          * @static
26163          */
26164         startX: 0,
26165
26166         /**
26167          * The Y position of the mousedown event stored for later use when a
26168          * drag threshold is met.
26169          * @property startY
26170          * @type int
26171          * @private
26172          * @static
26173          */
26174         startY: 0,
26175
26176         /**
26177          * Each DragDrop instance must be registered with the DragDropMgr.
26178          * This is executed in DragDrop.init()
26179          * @method regDragDrop
26180          * @param {DragDrop} oDD the DragDrop object to register
26181          * @param {String} sGroup the name of the group this element belongs to
26182          * @static
26183          */
26184         regDragDrop: function(oDD, sGroup) {
26185             if (!this.initialized) { this.init(); }
26186
26187             if (!this.ids[sGroup]) {
26188                 this.ids[sGroup] = {};
26189             }
26190             this.ids[sGroup][oDD.id] = oDD;
26191         },
26192
26193         /**
26194          * Removes the supplied dd instance from the supplied group. Executed
26195          * by DragDrop.removeFromGroup, so don't call this function directly.
26196          * @method removeDDFromGroup
26197          * @private
26198          * @static
26199          */
26200         removeDDFromGroup: function(oDD, sGroup) {
26201             if (!this.ids[sGroup]) {
26202                 this.ids[sGroup] = {};
26203             }
26204
26205             var obj = this.ids[sGroup];
26206             if (obj && obj[oDD.id]) {
26207                 delete obj[oDD.id];
26208             }
26209         },
26210
26211         /**
26212          * Unregisters a drag and drop item.  This is executed in
26213          * DragDrop.unreg, use that method instead of calling this directly.
26214          * @method _remove
26215          * @private
26216          * @static
26217          */
26218         _remove: function(oDD) {
26219             for (var g in oDD.groups) {
26220                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
26221                     delete this.ids[g][oDD.id];
26222                 }
26223             }
26224             delete this.handleIds[oDD.id];
26225         },
26226
26227         /**
26228          * Each DragDrop handle element must be registered.  This is done
26229          * automatically when executing DragDrop.setHandleElId()
26230          * @method regHandle
26231          * @param {String} sDDId the DragDrop id this element is a handle for
26232          * @param {String} sHandleId the id of the element that is the drag
26233          * handle
26234          * @static
26235          */
26236         regHandle: function(sDDId, sHandleId) {
26237             if (!this.handleIds[sDDId]) {
26238                 this.handleIds[sDDId] = {};
26239             }
26240             this.handleIds[sDDId][sHandleId] = sHandleId;
26241         },
26242
26243         /**
26244          * Utility function to determine if a given element has been
26245          * registered as a drag drop item.
26246          * @method isDragDrop
26247          * @param {String} id the element id to check
26248          * @return {boolean} true if this element is a DragDrop item,
26249          * false otherwise
26250          * @static
26251          */
26252         isDragDrop: function(id) {
26253             return ( this.getDDById(id) ) ? true : false;
26254         },
26255
26256         /**
26257          * Returns the drag and drop instances that are in all groups the
26258          * passed in instance belongs to.
26259          * @method getRelated
26260          * @param {DragDrop} p_oDD the obj to get related data for
26261          * @param {boolean} bTargetsOnly if true, only return targetable objs
26262          * @return {DragDrop[]} the related instances
26263          * @static
26264          */
26265         getRelated: function(p_oDD, bTargetsOnly) {
26266             var oDDs = [];
26267             for (var i in p_oDD.groups) {
26268                 for (var j in this.ids[i]) {
26269                     var dd = this.ids[i][j];
26270                     if (! this.isTypeOfDD(dd)) {
26271                         continue;
26272                     }
26273                     if (!bTargetsOnly || dd.isTarget) {
26274                         oDDs[oDDs.length] = dd;
26275                     }
26276                 }
26277             }
26278
26279             return oDDs;
26280         },
26281
26282         /**
26283          * Returns true if the specified dd target is a legal target for
26284          * the specifice drag obj
26285          * @method isLegalTarget
26286          * @param {DragDrop} the drag obj
26287          * @param {DragDrop} the target
26288          * @return {boolean} true if the target is a legal target for the
26289          * dd obj
26290          * @static
26291          */
26292         isLegalTarget: function (oDD, oTargetDD) {
26293             var targets = this.getRelated(oDD, true);
26294             for (var i=0, len=targets.length;i<len;++i) {
26295                 if (targets[i].id == oTargetDD.id) {
26296                     return true;
26297                 }
26298             }
26299
26300             return false;
26301         },
26302
26303         /**
26304          * My goal is to be able to transparently determine if an object is
26305          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
26306          * returns "object", oDD.constructor.toString() always returns
26307          * "DragDrop" and not the name of the subclass.  So for now it just
26308          * evaluates a well-known variable in DragDrop.
26309          * @method isTypeOfDD
26310          * @param {Object} the object to evaluate
26311          * @return {boolean} true if typeof oDD = DragDrop
26312          * @static
26313          */
26314         isTypeOfDD: function (oDD) {
26315             return (oDD && oDD.__ygDragDrop);
26316         },
26317
26318         /**
26319          * Utility function to determine if a given element has been
26320          * registered as a drag drop handle for the given Drag Drop object.
26321          * @method isHandle
26322          * @param {String} id the element id to check
26323          * @return {boolean} true if this element is a DragDrop handle, false
26324          * otherwise
26325          * @static
26326          */
26327         isHandle: function(sDDId, sHandleId) {
26328             return ( this.handleIds[sDDId] &&
26329                             this.handleIds[sDDId][sHandleId] );
26330         },
26331
26332         /**
26333          * Returns the DragDrop instance for a given id
26334          * @method getDDById
26335          * @param {String} id the id of the DragDrop object
26336          * @return {DragDrop} the drag drop object, null if it is not found
26337          * @static
26338          */
26339         getDDById: function(id) {
26340             for (var i in this.ids) {
26341                 if (this.ids[i][id]) {
26342                     return this.ids[i][id];
26343                 }
26344             }
26345             return null;
26346         },
26347
26348         /**
26349          * Fired after a registered DragDrop object gets the mousedown event.
26350          * Sets up the events required to track the object being dragged
26351          * @method handleMouseDown
26352          * @param {Event} e the event
26353          * @param oDD the DragDrop object being dragged
26354          * @private
26355          * @static
26356          */
26357         handleMouseDown: function(e, oDD) {
26358             if(Ext.QuickTips){
26359                 Ext.QuickTips.disable();
26360             }
26361             if(this.dragCurrent){
26362                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
26363                 // so clean up first to avoid breaking the next drag
26364                 this.handleMouseUp(e);
26365             }
26366             
26367             this.currentTarget = e.getTarget();
26368             this.dragCurrent = oDD;
26369
26370             var el = oDD.getEl();
26371
26372             // track start position
26373             this.startX = e.getPageX();
26374             this.startY = e.getPageY();
26375
26376             this.deltaX = this.startX - el.offsetLeft;
26377             this.deltaY = this.startY - el.offsetTop;
26378
26379             this.dragThreshMet = false;
26380
26381             this.clickTimeout = setTimeout(
26382                     function() {
26383                         var DDM = Ext.dd.DDM;
26384                         DDM.startDrag(DDM.startX, DDM.startY);
26385                     },
26386                     this.clickTimeThresh );
26387         },
26388
26389         /**
26390          * Fired when either the drag pixel threshol or the mousedown hold
26391          * time threshold has been met.
26392          * @method startDrag
26393          * @param x {int} the X position of the original mousedown
26394          * @param y {int} the Y position of the original mousedown
26395          * @static
26396          */
26397         startDrag: function(x, y) {
26398             clearTimeout(this.clickTimeout);
26399             if (this.dragCurrent) {
26400                 this.dragCurrent.b4StartDrag(x, y);
26401                 this.dragCurrent.startDrag(x, y);
26402             }
26403             this.dragThreshMet = true;
26404         },
26405
26406         /**
26407          * Internal function to handle the mouseup event.  Will be invoked
26408          * from the context of the document.
26409          * @method handleMouseUp
26410          * @param {Event} e the event
26411          * @private
26412          * @static
26413          */
26414         handleMouseUp: function(e) {
26415
26416             if(Ext.QuickTips){
26417                 Ext.QuickTips.enable();
26418             }
26419             if (! this.dragCurrent) {
26420                 return;
26421             }
26422
26423             clearTimeout(this.clickTimeout);
26424
26425             if (this.dragThreshMet) {
26426                 this.fireEvents(e, true);
26427             } else {
26428             }
26429
26430             this.stopDrag(e);
26431
26432             this.stopEvent(e);
26433         },
26434
26435         /**
26436          * Utility to stop event propagation and event default, if these
26437          * features are turned on.
26438          * @method stopEvent
26439          * @param {Event} e the event as returned by this.getEvent()
26440          * @static
26441          */
26442         stopEvent: function(e){
26443             if(this.stopPropagation) {
26444                 e.stopPropagation();
26445             }
26446
26447             if (this.preventDefault) {
26448                 e.preventDefault();
26449             }
26450         },
26451
26452         /**
26453          * Internal function to clean up event handlers after the drag
26454          * operation is complete
26455          * @method stopDrag
26456          * @param {Event} e the event
26457          * @private
26458          * @static
26459          */
26460         stopDrag: function(e) {
26461             // Fire the drag end event for the item that was dragged
26462             if (this.dragCurrent) {
26463                 if (this.dragThreshMet) {
26464                     this.dragCurrent.b4EndDrag(e);
26465                     this.dragCurrent.endDrag(e);
26466                 }
26467
26468                 this.dragCurrent.onMouseUp(e);
26469             }
26470
26471             this.dragCurrent = null;
26472             this.dragOvers = {};
26473         },
26474
26475         /**
26476          * Internal function to handle the mousemove event.  Will be invoked
26477          * from the context of the html element.
26478          *
26479          * @TODO figure out what we can do about mouse events lost when the
26480          * user drags objects beyond the window boundary.  Currently we can
26481          * detect this in internet explorer by verifying that the mouse is
26482          * down during the mousemove event.  Firefox doesn't give us the
26483          * button state on the mousemove event.
26484          * @method handleMouseMove
26485          * @param {Event} e the event
26486          * @private
26487          * @static
26488          */
26489         handleMouseMove: function(e) {
26490             if (! this.dragCurrent) {
26491                 return true;
26492             }
26493             // var button = e.which || e.button;
26494
26495             // check for IE mouseup outside of page boundary
26496             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
26497                 this.stopEvent(e);
26498                 return this.handleMouseUp(e);
26499             }
26500
26501             if (!this.dragThreshMet) {
26502                 var diffX = Math.abs(this.startX - e.getPageX());
26503                 var diffY = Math.abs(this.startY - e.getPageY());
26504                 if (diffX > this.clickPixelThresh ||
26505                             diffY > this.clickPixelThresh) {
26506                     this.startDrag(this.startX, this.startY);
26507                 }
26508             }
26509
26510             if (this.dragThreshMet) {
26511                 this.dragCurrent.b4Drag(e);
26512                 this.dragCurrent.onDrag(e);
26513                 if(!this.dragCurrent.moveOnly){
26514                     this.fireEvents(e, false);
26515                 }
26516             }
26517
26518             this.stopEvent(e);
26519
26520             return true;
26521         },
26522
26523         /**
26524          * Iterates over all of the DragDrop elements to find ones we are
26525          * hovering over or dropping on
26526          * @method fireEvents
26527          * @param {Event} e the event
26528          * @param {boolean} isDrop is this a drop op or a mouseover op?
26529          * @private
26530          * @static
26531          */
26532         fireEvents: function(e, isDrop) {
26533             var dc = this.dragCurrent;
26534
26535             // If the user did the mouse up outside of the window, we could
26536             // get here even though we have ended the drag.
26537             if (!dc || dc.isLocked()) {
26538                 return;
26539             }
26540
26541             var pt = e.getPoint();
26542
26543             // cache the previous dragOver array
26544             var oldOvers = [];
26545
26546             var outEvts   = [];
26547             var overEvts  = [];
26548             var dropEvts  = [];
26549             var enterEvts = [];
26550
26551             // Check to see if the object(s) we were hovering over is no longer
26552             // being hovered over so we can fire the onDragOut event
26553             for (var i in this.dragOvers) {
26554
26555                 var ddo = this.dragOvers[i];
26556
26557                 if (! this.isTypeOfDD(ddo)) {
26558                     continue;
26559                 }
26560
26561                 if (! this.isOverTarget(pt, ddo, this.mode)) {
26562                     outEvts.push( ddo );
26563                 }
26564
26565                 oldOvers[i] = true;
26566                 delete this.dragOvers[i];
26567             }
26568
26569             for (var sGroup in dc.groups) {
26570
26571                 if ("string" != typeof sGroup) {
26572                     continue;
26573                 }
26574
26575                 for (i in this.ids[sGroup]) {
26576                     var oDD = this.ids[sGroup][i];
26577                     if (! this.isTypeOfDD(oDD)) {
26578                         continue;
26579                     }
26580
26581                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
26582                         if (this.isOverTarget(pt, oDD, this.mode)) {
26583                             // look for drop interactions
26584                             if (isDrop) {
26585                                 dropEvts.push( oDD );
26586                             // look for drag enter and drag over interactions
26587                             } else {
26588
26589                                 // initial drag over: dragEnter fires
26590                                 if (!oldOvers[oDD.id]) {
26591                                     enterEvts.push( oDD );
26592                                 // subsequent drag overs: dragOver fires
26593                                 } else {
26594                                     overEvts.push( oDD );
26595                                 }
26596
26597                                 this.dragOvers[oDD.id] = oDD;
26598                             }
26599                         }
26600                     }
26601                 }
26602             }
26603
26604             if (this.mode) {
26605                 if (outEvts.length) {
26606                     dc.b4DragOut(e, outEvts);
26607                     dc.onDragOut(e, outEvts);
26608                 }
26609
26610                 if (enterEvts.length) {
26611                     dc.onDragEnter(e, enterEvts);
26612                 }
26613
26614                 if (overEvts.length) {
26615                     dc.b4DragOver(e, overEvts);
26616                     dc.onDragOver(e, overEvts);
26617                 }
26618
26619                 if (dropEvts.length) {
26620                     dc.b4DragDrop(e, dropEvts);
26621                     dc.onDragDrop(e, dropEvts);
26622                 }
26623
26624             } else {
26625                 // fire dragout events
26626                 var len = 0;
26627                 for (i=0, len=outEvts.length; i<len; ++i) {
26628                     dc.b4DragOut(e, outEvts[i].id);
26629                     dc.onDragOut(e, outEvts[i].id);
26630                 }
26631
26632                 // fire enter events
26633                 for (i=0,len=enterEvts.length; i<len; ++i) {
26634                     // dc.b4DragEnter(e, oDD.id);
26635                     dc.onDragEnter(e, enterEvts[i].id);
26636                 }
26637
26638                 // fire over events
26639                 for (i=0,len=overEvts.length; i<len; ++i) {
26640                     dc.b4DragOver(e, overEvts[i].id);
26641                     dc.onDragOver(e, overEvts[i].id);
26642                 }
26643
26644                 // fire drop events
26645                 for (i=0, len=dropEvts.length; i<len; ++i) {
26646                     dc.b4DragDrop(e, dropEvts[i].id);
26647                     dc.onDragDrop(e, dropEvts[i].id);
26648                 }
26649
26650             }
26651
26652             // notify about a drop that did not find a target
26653             if (isDrop && !dropEvts.length) {
26654                 dc.onInvalidDrop(e);
26655             }
26656
26657         },
26658
26659         /**
26660          * Helper function for getting the best match from the list of drag
26661          * and drop objects returned by the drag and drop events when we are
26662          * in INTERSECT mode.  It returns either the first object that the
26663          * cursor is over, or the object that has the greatest overlap with
26664          * the dragged element.
26665          * @method getBestMatch
26666          * @param  {DragDrop[]} dds The array of drag and drop objects
26667          * targeted
26668          * @return {DragDrop}       The best single match
26669          * @static
26670          */
26671         getBestMatch: function(dds) {
26672             var winner = null;
26673             // Return null if the input is not what we expect
26674             //if (!dds || !dds.length || dds.length == 0) {
26675                // winner = null;
26676             // If there is only one item, it wins
26677             //} else if (dds.length == 1) {
26678
26679             var len = dds.length;
26680
26681             if (len == 1) {
26682                 winner = dds[0];
26683             } else {
26684                 // Loop through the targeted items
26685                 for (var i=0; i<len; ++i) {
26686                     var dd = dds[i];
26687                     // If the cursor is over the object, it wins.  If the
26688                     // cursor is over multiple matches, the first one we come
26689                     // to wins.
26690                     if (dd.cursorIsOver) {
26691                         winner = dd;
26692                         break;
26693                     // Otherwise the object with the most overlap wins
26694                     } else {
26695                         if (!winner ||
26696                             winner.overlap.getArea() < dd.overlap.getArea()) {
26697                             winner = dd;
26698                         }
26699                     }
26700                 }
26701             }
26702
26703             return winner;
26704         },
26705
26706         /**
26707          * Refreshes the cache of the top-left and bottom-right points of the
26708          * drag and drop objects in the specified group(s).  This is in the
26709          * format that is stored in the drag and drop instance, so typical
26710          * usage is:
26711          * <code>
26712          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
26713          * </code>
26714          * Alternatively:
26715          * <code>
26716          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
26717          * </code>
26718          * @TODO this really should be an indexed array.  Alternatively this
26719          * method could accept both.
26720          * @method refreshCache
26721          * @param {Object} groups an associative array of groups to refresh
26722          * @static
26723          */
26724         refreshCache: function(groups) {
26725             for (var sGroup in groups) {
26726                 if ("string" != typeof sGroup) {
26727                     continue;
26728                 }
26729                 for (var i in this.ids[sGroup]) {
26730                     var oDD = this.ids[sGroup][i];
26731
26732                     if (this.isTypeOfDD(oDD)) {
26733                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
26734                         var loc = this.getLocation(oDD);
26735                         if (loc) {
26736                             this.locationCache[oDD.id] = loc;
26737                         } else {
26738                             delete this.locationCache[oDD.id];
26739                             // this will unregister the drag and drop object if
26740                             // the element is not in a usable state
26741                             // oDD.unreg();
26742                         }
26743                     }
26744                 }
26745             }
26746         },
26747
26748         /**
26749          * This checks to make sure an element exists and is in the DOM.  The
26750          * main purpose is to handle cases where innerHTML is used to remove
26751          * drag and drop objects from the DOM.  IE provides an 'unspecified
26752          * error' when trying to access the offsetParent of such an element
26753          * @method verifyEl
26754          * @param {HTMLElement} el the element to check
26755          * @return {boolean} true if the element looks usable
26756          * @static
26757          */
26758         verifyEl: function(el) {
26759             if (el) {
26760                 var parent;
26761                 if(Ext.isIE){
26762                     try{
26763                         parent = el.offsetParent;
26764                     }catch(e){}
26765                 }else{
26766                     parent = el.offsetParent;
26767                 }
26768                 if (parent) {
26769                     return true;
26770                 }
26771             }
26772
26773             return false;
26774         },
26775
26776         /**
26777          * Returns a Region object containing the drag and drop element's position
26778          * and size, including the padding configured for it
26779          * @method getLocation
26780          * @param {DragDrop} oDD the drag and drop object to get the
26781          *                       location for
26782          * @return {Ext.lib.Region} a Region object representing the total area
26783          *                             the element occupies, including any padding
26784          *                             the instance is configured for.
26785          * @static
26786          */
26787         getLocation: function(oDD) {
26788             if (! this.isTypeOfDD(oDD)) {
26789                 return null;
26790             }
26791
26792             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
26793
26794             try {
26795                 pos= Ext.lib.Dom.getXY(el);
26796             } catch (e) { }
26797
26798             if (!pos) {
26799                 return null;
26800             }
26801
26802             x1 = pos[0];
26803             x2 = x1 + el.offsetWidth;
26804             y1 = pos[1];
26805             y2 = y1 + el.offsetHeight;
26806
26807             t = y1 - oDD.padding[0];
26808             r = x2 + oDD.padding[1];
26809             b = y2 + oDD.padding[2];
26810             l = x1 - oDD.padding[3];
26811
26812             return new Ext.lib.Region( t, r, b, l );
26813         },
26814
26815         /**
26816          * Checks the cursor location to see if it over the target
26817          * @method isOverTarget
26818          * @param {Ext.lib.Point} pt The point to evaluate
26819          * @param {DragDrop} oTarget the DragDrop object we are inspecting
26820          * @return {boolean} true if the mouse is over the target
26821          * @private
26822          * @static
26823          */
26824         isOverTarget: function(pt, oTarget, intersect) {
26825             // use cache if available
26826             var loc = this.locationCache[oTarget.id];
26827             if (!loc || !this.useCache) {
26828                 loc = this.getLocation(oTarget);
26829                 this.locationCache[oTarget.id] = loc;
26830
26831             }
26832
26833             if (!loc) {
26834                 return false;
26835             }
26836
26837             oTarget.cursorIsOver = loc.contains( pt );
26838
26839             // DragDrop is using this as a sanity check for the initial mousedown
26840             // in this case we are done.  In POINT mode, if the drag obj has no
26841             // contraints, we are also done. Otherwise we need to evaluate the
26842             // location of the target as related to the actual location of the
26843             // dragged element.
26844             var dc = this.dragCurrent;
26845             if (!dc || !dc.getTargetCoord ||
26846                     (!intersect && !dc.constrainX && !dc.constrainY)) {
26847                 return oTarget.cursorIsOver;
26848             }
26849
26850             oTarget.overlap = null;
26851
26852             // Get the current location of the drag element, this is the
26853             // location of the mouse event less the delta that represents
26854             // where the original mousedown happened on the element.  We
26855             // need to consider constraints and ticks as well.
26856             var pos = dc.getTargetCoord(pt.x, pt.y);
26857
26858             var el = dc.getDragEl();
26859             var curRegion = new Ext.lib.Region( pos.y,
26860                                                    pos.x + el.offsetWidth,
26861                                                    pos.y + el.offsetHeight,
26862                                                    pos.x );
26863
26864             var overlap = curRegion.intersect(loc);
26865
26866             if (overlap) {
26867                 oTarget.overlap = overlap;
26868                 return (intersect) ? true : oTarget.cursorIsOver;
26869             } else {
26870                 return false;
26871             }
26872         },
26873
26874         /**
26875          * unload event handler
26876          * @method _onUnload
26877          * @private
26878          * @static
26879          */
26880         _onUnload: function(e, me) {
26881             Ext.dd.DragDropMgr.unregAll();
26882         },
26883
26884         /**
26885          * Cleans up the drag and drop events and objects.
26886          * @method unregAll
26887          * @private
26888          * @static
26889          */
26890         unregAll: function() {
26891
26892             if (this.dragCurrent) {
26893                 this.stopDrag();
26894                 this.dragCurrent = null;
26895             }
26896
26897             this._execOnAll("unreg", []);
26898
26899             for (var i in this.elementCache) {
26900                 delete this.elementCache[i];
26901             }
26902
26903             this.elementCache = {};
26904             this.ids = {};
26905         },
26906
26907         /**
26908          * A cache of DOM elements
26909          * @property elementCache
26910          * @private
26911          * @static
26912          */
26913         elementCache: {},
26914
26915         /**
26916          * Get the wrapper for the DOM element specified
26917          * @method getElWrapper
26918          * @param {String} id the id of the element to get
26919          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
26920          * @private
26921          * @deprecated This wrapper isn't that useful
26922          * @static
26923          */
26924         getElWrapper: function(id) {
26925             var oWrapper = this.elementCache[id];
26926             if (!oWrapper || !oWrapper.el) {
26927                 oWrapper = this.elementCache[id] =
26928                     new this.ElementWrapper(Ext.getDom(id));
26929             }
26930             return oWrapper;
26931         },
26932
26933         /**
26934          * Returns the actual DOM element
26935          * @method getElement
26936          * @param {String} id the id of the elment to get
26937          * @return {Object} The element
26938          * @deprecated use Ext.lib.Ext.getDom instead
26939          * @static
26940          */
26941         getElement: function(id) {
26942             return Ext.getDom(id);
26943         },
26944
26945         /**
26946          * Returns the style property for the DOM element (i.e.,
26947          * document.getElById(id).style)
26948          * @method getCss
26949          * @param {String} id the id of the elment to get
26950          * @return {Object} The style property of the element
26951          * @deprecated use Ext.lib.Dom instead
26952          * @static
26953          */
26954         getCss: function(id) {
26955             var el = Ext.getDom(id);
26956             return (el) ? el.style : null;
26957         },
26958
26959         /**
26960          * Inner class for cached elements
26961          * @class DragDropMgr.ElementWrapper
26962          * @for DragDropMgr
26963          * @private
26964          * @deprecated
26965          */
26966         ElementWrapper: function(el) {
26967                 /**
26968                  * The element
26969                  * @property el
26970                  */
26971                 this.el = el || null;
26972                 /**
26973                  * The element id
26974                  * @property id
26975                  */
26976                 this.id = this.el && el.id;
26977                 /**
26978                  * A reference to the style property
26979                  * @property css
26980                  */
26981                 this.css = this.el && el.style;
26982             },
26983
26984         /**
26985          * Returns the X position of an html element
26986          * @method getPosX
26987          * @param el the element for which to get the position
26988          * @return {int} the X coordinate
26989          * @for DragDropMgr
26990          * @deprecated use Ext.lib.Dom.getX instead
26991          * @static
26992          */
26993         getPosX: function(el) {
26994             return Ext.lib.Dom.getX(el);
26995         },
26996
26997         /**
26998          * Returns the Y position of an html element
26999          * @method getPosY
27000          * @param el the element for which to get the position
27001          * @return {int} the Y coordinate
27002          * @deprecated use Ext.lib.Dom.getY instead
27003          * @static
27004          */
27005         getPosY: function(el) {
27006             return Ext.lib.Dom.getY(el);
27007         },
27008
27009         /**
27010          * Swap two nodes.  In IE, we use the native method, for others we
27011          * emulate the IE behavior
27012          * @method swapNode
27013          * @param n1 the first node to swap
27014          * @param n2 the other node to swap
27015          * @static
27016          */
27017         swapNode: function(n1, n2) {
27018             if (n1.swapNode) {
27019                 n1.swapNode(n2);
27020             } else {
27021                 var p = n2.parentNode;
27022                 var s = n2.nextSibling;
27023
27024                 if (s == n1) {
27025                     p.insertBefore(n1, n2);
27026                 } else if (n2 == n1.nextSibling) {
27027                     p.insertBefore(n2, n1);
27028                 } else {
27029                     n1.parentNode.replaceChild(n2, n1);
27030                     p.insertBefore(n1, s);
27031                 }
27032             }
27033         },
27034
27035         /**
27036          * Returns the current scroll position
27037          * @method getScroll
27038          * @private
27039          * @static
27040          */
27041         getScroll: function () {
27042             var t, l, dde=document.documentElement, db=document.body;
27043             if (dde && (dde.scrollTop || dde.scrollLeft)) {
27044                 t = dde.scrollTop;
27045                 l = dde.scrollLeft;
27046             } else if (db) {
27047                 t = db.scrollTop;
27048                 l = db.scrollLeft;
27049             } else {
27050
27051             }
27052             return { top: t, left: l };
27053         },
27054
27055         /**
27056          * Returns the specified element style property
27057          * @method getStyle
27058          * @param {HTMLElement} el          the element
27059          * @param {string}      styleProp   the style property
27060          * @return {string} The value of the style property
27061          * @deprecated use Ext.lib.Dom.getStyle
27062          * @static
27063          */
27064         getStyle: function(el, styleProp) {
27065             return Ext.fly(el).getStyle(styleProp);
27066         },
27067
27068         /**
27069          * Gets the scrollTop
27070          * @method getScrollTop
27071          * @return {int} the document's scrollTop
27072          * @static
27073          */
27074         getScrollTop: function () { return this.getScroll().top; },
27075
27076         /**
27077          * Gets the scrollLeft
27078          * @method getScrollLeft
27079          * @return {int} the document's scrollTop
27080          * @static
27081          */
27082         getScrollLeft: function () { return this.getScroll().left; },
27083
27084         /**
27085          * Sets the x/y position of an element to the location of the
27086          * target element.
27087          * @method moveToEl
27088          * @param {HTMLElement} moveEl      The element to move
27089          * @param {HTMLElement} targetEl    The position reference element
27090          * @static
27091          */
27092         moveToEl: function (moveEl, targetEl) {
27093             var aCoord = Ext.lib.Dom.getXY(targetEl);
27094             Ext.lib.Dom.setXY(moveEl, aCoord);
27095         },
27096
27097         /**
27098          * Numeric array sort function
27099          * @method numericSort
27100          * @static
27101          */
27102         numericSort: function(a, b) { return (a - b); },
27103
27104         /**
27105          * Internal counter
27106          * @property _timeoutCount
27107          * @private
27108          * @static
27109          */
27110         _timeoutCount: 0,
27111
27112         /**
27113          * Trying to make the load order less important.  Without this we get
27114          * an error if this file is loaded before the Event Utility.
27115          * @method _addListeners
27116          * @private
27117          * @static
27118          */
27119         _addListeners: function() {
27120             var DDM = Ext.dd.DDM;
27121             if ( Ext.lib.Event && document ) {
27122                 DDM._onLoad();
27123             } else {
27124                 if (DDM._timeoutCount > 2000) {
27125                 } else {
27126                     setTimeout(DDM._addListeners, 10);
27127                     if (document && document.body) {
27128                         DDM._timeoutCount += 1;
27129                     }
27130                 }
27131             }
27132         },
27133
27134         /**
27135          * Recursively searches the immediate parent and all child nodes for
27136          * the handle element in order to determine wheter or not it was
27137          * clicked.
27138          * @method handleWasClicked
27139          * @param node the html element to inspect
27140          * @static
27141          */
27142         handleWasClicked: function(node, id) {
27143             if (this.isHandle(id, node.id)) {
27144                 return true;
27145             } else {
27146                 // check to see if this is a text node child of the one we want
27147                 var p = node.parentNode;
27148
27149                 while (p) {
27150                     if (this.isHandle(id, p.id)) {
27151                         return true;
27152                     } else {
27153                         p = p.parentNode;
27154                     }
27155                 }
27156             }
27157
27158             return false;
27159         }
27160
27161     };
27162
27163 }();
27164
27165 // shorter alias, save a few bytes
27166 Ext.dd.DDM = Ext.dd.DragDropMgr;
27167 Ext.dd.DDM._addListeners();
27168
27169 }
27170
27171 /**
27172  * @class Ext.dd.DD
27173  * A DragDrop implementation where the linked element follows the
27174  * mouse cursor during a drag.
27175  * @extends Ext.dd.DragDrop
27176  * @constructor
27177  * @param {String} id the id of the linked element
27178  * @param {String} sGroup the group of related DragDrop items
27179  * @param {object} config an object containing configurable attributes
27180  *                Valid properties for DD:
27181  *                    scroll
27182  */
27183 Ext.dd.DD = function(id, sGroup, config) {
27184     if (id) {
27185         this.init(id, sGroup, config);
27186     }
27187 };
27188
27189 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
27190
27191     /**
27192      * When set to true, the utility automatically tries to scroll the browser
27193      * window when a drag and drop element is dragged near the viewport boundary.
27194      * Defaults to true.
27195      * @property scroll
27196      * @type boolean
27197      */
27198     scroll: true,
27199
27200     /**
27201      * Sets the pointer offset to the distance between the linked element's top
27202      * left corner and the location the element was clicked
27203      * @method autoOffset
27204      * @param {int} iPageX the X coordinate of the click
27205      * @param {int} iPageY the Y coordinate of the click
27206      */
27207     autoOffset: function(iPageX, iPageY) {
27208         var x = iPageX - this.startPageX;
27209         var y = iPageY - this.startPageY;
27210         this.setDelta(x, y);
27211     },
27212
27213     /**
27214      * Sets the pointer offset.  You can call this directly to force the
27215      * offset to be in a particular location (e.g., pass in 0,0 to set it
27216      * to the center of the object)
27217      * @method setDelta
27218      * @param {int} iDeltaX the distance from the left
27219      * @param {int} iDeltaY the distance from the top
27220      */
27221     setDelta: function(iDeltaX, iDeltaY) {
27222         this.deltaX = iDeltaX;
27223         this.deltaY = iDeltaY;
27224     },
27225
27226     /**
27227      * Sets the drag element to the location of the mousedown or click event,
27228      * maintaining the cursor location relative to the location on the element
27229      * that was clicked.  Override this if you want to place the element in a
27230      * location other than where the cursor is.
27231      * @method setDragElPos
27232      * @param {int} iPageX the X coordinate of the mousedown or drag event
27233      * @param {int} iPageY the Y coordinate of the mousedown or drag event
27234      */
27235     setDragElPos: function(iPageX, iPageY) {
27236         // the first time we do this, we are going to check to make sure
27237         // the element has css positioning
27238
27239         var el = this.getDragEl();
27240         this.alignElWithMouse(el, iPageX, iPageY);
27241     },
27242
27243     /**
27244      * Sets the element to the location of the mousedown or click event,
27245      * maintaining the cursor location relative to the location on the element
27246      * that was clicked.  Override this if you want to place the element in a
27247      * location other than where the cursor is.
27248      * @method alignElWithMouse
27249      * @param {HTMLElement} el the element to move
27250      * @param {int} iPageX the X coordinate of the mousedown or drag event
27251      * @param {int} iPageY the Y coordinate of the mousedown or drag event
27252      */
27253     alignElWithMouse: function(el, iPageX, iPageY) {
27254         var oCoord = this.getTargetCoord(iPageX, iPageY);
27255         var fly = el.dom ? el : Ext.fly(el, '_dd');
27256         if (!this.deltaSetXY) {
27257             var aCoord = [oCoord.x, oCoord.y];
27258             fly.setXY(aCoord);
27259             var newLeft = fly.getLeft(true);
27260             var newTop  = fly.getTop(true);
27261             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
27262         } else {
27263             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
27264         }
27265
27266         this.cachePosition(oCoord.x, oCoord.y);
27267         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
27268         return oCoord;
27269     },
27270
27271     /**
27272      * Saves the most recent position so that we can reset the constraints and
27273      * tick marks on-demand.  We need to know this so that we can calculate the
27274      * number of pixels the element is offset from its original position.
27275      * @method cachePosition
27276      * @param iPageX the current x position (optional, this just makes it so we
27277      * don't have to look it up again)
27278      * @param iPageY the current y position (optional, this just makes it so we
27279      * don't have to look it up again)
27280      */
27281     cachePosition: function(iPageX, iPageY) {
27282         if (iPageX) {
27283             this.lastPageX = iPageX;
27284             this.lastPageY = iPageY;
27285         } else {
27286             var aCoord = Ext.lib.Dom.getXY(this.getEl());
27287             this.lastPageX = aCoord[0];
27288             this.lastPageY = aCoord[1];
27289         }
27290     },
27291
27292     /**
27293      * Auto-scroll the window if the dragged object has been moved beyond the
27294      * visible window boundary.
27295      * @method autoScroll
27296      * @param {int} x the drag element's x position
27297      * @param {int} y the drag element's y position
27298      * @param {int} h the height of the drag element
27299      * @param {int} w the width of the drag element
27300      * @private
27301      */
27302     autoScroll: function(x, y, h, w) {
27303
27304         if (this.scroll) {
27305             // The client height
27306             var clientH = Ext.lib.Dom.getViewHeight();
27307
27308             // The client width
27309             var clientW = Ext.lib.Dom.getViewWidth();
27310
27311             // The amt scrolled down
27312             var st = this.DDM.getScrollTop();
27313
27314             // The amt scrolled right
27315             var sl = this.DDM.getScrollLeft();
27316
27317             // Location of the bottom of the element
27318             var bot = h + y;
27319
27320             // Location of the right of the element
27321             var right = w + x;
27322
27323             // The distance from the cursor to the bottom of the visible area,
27324             // adjusted so that we don't scroll if the cursor is beyond the
27325             // element drag constraints
27326             var toBot = (clientH + st - y - this.deltaY);
27327
27328             // The distance from the cursor to the right of the visible area
27329             var toRight = (clientW + sl - x - this.deltaX);
27330
27331
27332             // How close to the edge the cursor must be before we scroll
27333             // var thresh = (document.all) ? 100 : 40;
27334             var thresh = 40;
27335
27336             // How many pixels to scroll per autoscroll op.  This helps to reduce
27337             // clunky scrolling. IE is more sensitive about this ... it needs this
27338             // value to be higher.
27339             var scrAmt = (document.all) ? 80 : 30;
27340
27341             // Scroll down if we are near the bottom of the visible page and the
27342             // obj extends below the crease
27343             if ( bot > clientH && toBot < thresh ) {
27344                 window.scrollTo(sl, st + scrAmt);
27345             }
27346
27347             // Scroll up if the window is scrolled down and the top of the object
27348             // goes above the top border
27349             if ( y < st && st > 0 && y - st < thresh ) {
27350                 window.scrollTo(sl, st - scrAmt);
27351             }
27352
27353             // Scroll right if the obj is beyond the right border and the cursor is
27354             // near the border.
27355             if ( right > clientW && toRight < thresh ) {
27356                 window.scrollTo(sl + scrAmt, st);
27357             }
27358
27359             // Scroll left if the window has been scrolled to the right and the obj
27360             // extends past the left border
27361             if ( x < sl && sl > 0 && x - sl < thresh ) {
27362                 window.scrollTo(sl - scrAmt, st);
27363             }
27364         }
27365     },
27366
27367     /**
27368      * Finds the location the element should be placed if we want to move
27369      * it to where the mouse location less the click offset would place us.
27370      * @method getTargetCoord
27371      * @param {int} iPageX the X coordinate of the click
27372      * @param {int} iPageY the Y coordinate of the click
27373      * @return an object that contains the coordinates (Object.x and Object.y)
27374      * @private
27375      */
27376     getTargetCoord: function(iPageX, iPageY) {
27377
27378
27379         var x = iPageX - this.deltaX;
27380         var y = iPageY - this.deltaY;
27381
27382         if (this.constrainX) {
27383             if (x < this.minX) { x = this.minX; }
27384             if (x > this.maxX) { x = this.maxX; }
27385         }
27386
27387         if (this.constrainY) {
27388             if (y < this.minY) { y = this.minY; }
27389             if (y > this.maxY) { y = this.maxY; }
27390         }
27391
27392         x = this.getTick(x, this.xTicks);
27393         y = this.getTick(y, this.yTicks);
27394
27395
27396         return {x:x, y:y};
27397     },
27398
27399     /**
27400      * Sets up config options specific to this class. Overrides
27401      * Ext.dd.DragDrop, but all versions of this method through the
27402      * inheritance chain are called
27403      */
27404     applyConfig: function() {
27405         Ext.dd.DD.superclass.applyConfig.call(this);
27406         this.scroll = (this.config.scroll !== false);
27407     },
27408
27409     /**
27410      * Event that fires prior to the onMouseDown event.  Overrides
27411      * Ext.dd.DragDrop.
27412      */
27413     b4MouseDown: function(e) {
27414         // this.resetConstraints();
27415         this.autoOffset(e.getPageX(),
27416                             e.getPageY());
27417     },
27418
27419     /**
27420      * Event that fires prior to the onDrag event.  Overrides
27421      * Ext.dd.DragDrop.
27422      */
27423     b4Drag: function(e) {
27424         this.setDragElPos(e.getPageX(),
27425                             e.getPageY());
27426     },
27427
27428     toString: function() {
27429         return ("DD " + this.id);
27430     }
27431
27432     //////////////////////////////////////////////////////////////////////////
27433     // Debugging ygDragDrop events that can be overridden
27434     //////////////////////////////////////////////////////////////////////////
27435     /*
27436     startDrag: function(x, y) {
27437     },
27438
27439     onDrag: function(e) {
27440     },
27441
27442     onDragEnter: function(e, id) {
27443     },
27444
27445     onDragOver: function(e, id) {
27446     },
27447
27448     onDragOut: function(e, id) {
27449     },
27450
27451     onDragDrop: function(e, id) {
27452     },
27453
27454     endDrag: function(e) {
27455     }
27456
27457     */
27458
27459 });
27460 /**
27461  * @class Ext.dd.DDProxy
27462  * A DragDrop implementation that inserts an empty, bordered div into
27463  * the document that follows the cursor during drag operations.  At the time of
27464  * the click, the frame div is resized to the dimensions of the linked html
27465  * element, and moved to the exact location of the linked element.
27466  *
27467  * References to the "frame" element refer to the single proxy element that
27468  * was created to be dragged in place of all DDProxy elements on the
27469  * page.
27470  *
27471  * @extends Ext.dd.DD
27472  * @constructor
27473  * @param {String} id the id of the linked html element
27474  * @param {String} sGroup the group of related DragDrop objects
27475  * @param {object} config an object containing configurable attributes
27476  *                Valid properties for DDProxy in addition to those in DragDrop:
27477  *                   resizeFrame, centerFrame, dragElId
27478  */
27479 Ext.dd.DDProxy = function(id, sGroup, config) {
27480     if (id) {
27481         this.init(id, sGroup, config);
27482         this.initFrame();
27483     }
27484 };
27485
27486 /**
27487  * The default drag frame div id
27488  * @property Ext.dd.DDProxy.dragElId
27489  * @type String
27490  * @static
27491  */
27492 Ext.dd.DDProxy.dragElId = "ygddfdiv";
27493
27494 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
27495
27496     /**
27497      * By default we resize the drag frame to be the same size as the element
27498      * we want to drag (this is to get the frame effect).  We can turn it off
27499      * if we want a different behavior.
27500      * @property resizeFrame
27501      * @type boolean
27502      */
27503     resizeFrame: true,
27504
27505     /**
27506      * By default the frame is positioned exactly where the drag element is, so
27507      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
27508      * you do not have constraints on the obj is to have the drag frame centered
27509      * around the cursor.  Set centerFrame to true for this effect.
27510      * @property centerFrame
27511      * @type boolean
27512      */
27513     centerFrame: false,
27514
27515     /**
27516      * Creates the proxy element if it does not yet exist
27517      * @method createFrame
27518      */
27519     createFrame: function() {
27520         var self = this;
27521         var body = document.body;
27522
27523         if (!body || !body.firstChild) {
27524             setTimeout( function() { self.createFrame(); }, 50 );
27525             return;
27526         }
27527
27528         var div = this.getDragEl();
27529
27530         if (!div) {
27531             div    = document.createElement("div");
27532             div.id = this.dragElId;
27533             var s  = div.style;
27534
27535             s.position   = "absolute";
27536             s.visibility = "hidden";
27537             s.cursor     = "move";
27538             s.border     = "2px solid #aaa";
27539             s.zIndex     = 999;
27540
27541             // appendChild can blow up IE if invoked prior to the window load event
27542             // while rendering a table.  It is possible there are other scenarios
27543             // that would cause this to happen as well.
27544             body.insertBefore(div, body.firstChild);
27545         }
27546     },
27547
27548     /**
27549      * Initialization for the drag frame element.  Must be called in the
27550      * constructor of all subclasses
27551      * @method initFrame
27552      */
27553     initFrame: function() {
27554         this.createFrame();
27555     },
27556
27557     applyConfig: function() {
27558         Ext.dd.DDProxy.superclass.applyConfig.call(this);
27559
27560         this.resizeFrame = (this.config.resizeFrame !== false);
27561         this.centerFrame = (this.config.centerFrame);
27562         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
27563     },
27564
27565     /**
27566      * Resizes the drag frame to the dimensions of the clicked object, positions
27567      * it over the object, and finally displays it
27568      * @method showFrame
27569      * @param {int} iPageX X click position
27570      * @param {int} iPageY Y click position
27571      * @private
27572      */
27573     showFrame: function(iPageX, iPageY) {
27574         var el = this.getEl();
27575         var dragEl = this.getDragEl();
27576         var s = dragEl.style;
27577
27578         this._resizeProxy();
27579
27580         if (this.centerFrame) {
27581             this.setDelta( Math.round(parseInt(s.width,  10)/2),
27582                            Math.round(parseInt(s.height, 10)/2) );
27583         }
27584
27585         this.setDragElPos(iPageX, iPageY);
27586
27587         Ext.fly(dragEl).show();
27588     },
27589
27590     /**
27591      * The proxy is automatically resized to the dimensions of the linked
27592      * element when a drag is initiated, unless resizeFrame is set to false
27593      * @method _resizeProxy
27594      * @private
27595      */
27596     _resizeProxy: function() {
27597         if (this.resizeFrame) {
27598             var el = this.getEl();
27599             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
27600         }
27601     },
27602
27603     // overrides Ext.dd.DragDrop
27604     b4MouseDown: function(e) {
27605         var x = e.getPageX();
27606         var y = e.getPageY();
27607         this.autoOffset(x, y);
27608         this.setDragElPos(x, y);
27609     },
27610
27611     // overrides Ext.dd.DragDrop
27612     b4StartDrag: function(x, y) {
27613         // show the drag frame
27614         this.showFrame(x, y);
27615     },
27616
27617     // overrides Ext.dd.DragDrop
27618     b4EndDrag: function(e) {
27619         Ext.fly(this.getDragEl()).hide();
27620     },
27621
27622     // overrides Ext.dd.DragDrop
27623     // By default we try to move the element to the last location of the frame.
27624     // This is so that the default behavior mirrors that of Ext.dd.DD.
27625     endDrag: function(e) {
27626
27627         var lel = this.getEl();
27628         var del = this.getDragEl();
27629
27630         // Show the drag frame briefly so we can get its position
27631         del.style.visibility = "";
27632
27633         this.beforeMove();
27634         // Hide the linked element before the move to get around a Safari
27635         // rendering bug.
27636         lel.style.visibility = "hidden";
27637         Ext.dd.DDM.moveToEl(lel, del);
27638         del.style.visibility = "hidden";
27639         lel.style.visibility = "";
27640
27641         this.afterDrag();
27642     },
27643
27644     beforeMove : function(){
27645
27646     },
27647
27648     afterDrag : function(){
27649
27650     },
27651
27652     toString: function() {
27653         return ("DDProxy " + this.id);
27654     }
27655
27656 });
27657 /**
27658  * @class Ext.dd.DDTarget
27659  * A DragDrop implementation that does not move, but can be a drop
27660  * target.  You would get the same result by simply omitting implementation
27661  * for the event callbacks, but this way we reduce the processing cost of the
27662  * event listener and the callbacks.
27663  * @extends Ext.dd.DragDrop
27664  * @constructor
27665  * @param {String} id the id of the element that is a drop target
27666  * @param {String} sGroup the group of related DragDrop objects
27667  * @param {object} config an object containing configurable attributes
27668  *                 Valid properties for DDTarget in addition to those in
27669  *                 DragDrop:
27670  *                    none
27671  */
27672 Ext.dd.DDTarget = function(id, sGroup, config) {
27673     if (id) {
27674         this.initTarget(id, sGroup, config);
27675     }
27676 };
27677
27678 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
27679 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
27680     toString: function() {
27681         return ("DDTarget " + this.id);
27682     }
27683 });
27684 /**\r
27685  * @class Ext.dd.DragTracker\r
27686  * @extends Ext.util.Observable\r
27687  */\r
27688 Ext.dd.DragTracker = function(config){\r
27689     Ext.apply(this, config);\r
27690     this.addEvents(\r
27691         /**\r
27692          * @event mousedown\r
27693          * @param {Object} this\r
27694          * @param {Object} e event object\r
27695          */\r
27696         'mousedown',\r
27697         /**\r
27698          * @event mouseup\r
27699          * @param {Object} this\r
27700          * @param {Object} e event object\r
27701          */\r
27702         'mouseup',\r
27703         /**\r
27704          * @event mousemove\r
27705          * @param {Object} this\r
27706          * @param {Object} e event object\r
27707          */\r
27708         'mousemove',\r
27709         /**\r
27710          * @event dragstart\r
27711          * @param {Object} this\r
27712          * @param {Object} startXY the page coordinates of the event\r
27713          */\r
27714         'dragstart',\r
27715         /**\r
27716          * @event dragend\r
27717          * @param {Object} this\r
27718          * @param {Object} e event object\r
27719          */\r
27720         'dragend',\r
27721         /**\r
27722          * @event drag\r
27723          * @param {Object} this\r
27724          * @param {Object} e event object\r
27725          */\r
27726         'drag'\r
27727     );\r
27728 \r
27729     this.dragRegion = new Ext.lib.Region(0,0,0,0);\r
27730 \r
27731     if(this.el){\r
27732         this.initEl(this.el);\r
27733     }\r
27734 }\r
27735 \r
27736 Ext.extend(Ext.dd.DragTracker, Ext.util.Observable,  {\r
27737     /**\r
27738      * @cfg {Boolean} active\r
27739          * Defaults to <tt>false</tt>.\r
27740          */     \r
27741     active: false,\r
27742     /**\r
27743      * @cfg {Number} tolerance\r
27744          * Defaults to <tt>5</tt>.\r
27745          */     \r
27746     tolerance: 5,\r
27747     /**\r
27748      * @cfg {Boolean/Number} autoStart\r
27749          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.\r
27750          * Specify a Number for the number of milliseconds to defer trigger start.\r
27751          */     \r
27752     autoStart: false,\r
27753 \r
27754     initEl: function(el){\r
27755         this.el = Ext.get(el);\r
27756         el.on('mousedown', this.onMouseDown, this,\r
27757                 this.delegate ? {delegate: this.delegate} : undefined);\r
27758     },\r
27759 \r
27760     destroy : function(){\r
27761         this.el.un('mousedown', this.onMouseDown, this);\r
27762     },\r
27763 \r
27764     onMouseDown: function(e, target){\r
27765         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){\r
27766             this.startXY = this.lastXY = e.getXY();\r
27767             this.dragTarget = this.delegate ? target : this.el.dom;\r
27768             if(this.preventDefault !== false){\r
27769                 e.preventDefault();\r
27770             }\r
27771             var doc = Ext.getDoc();\r
27772             doc.on('mouseup', this.onMouseUp, this);\r
27773             doc.on('mousemove', this.onMouseMove, this);\r
27774             doc.on('selectstart', this.stopSelect, this);\r
27775             if(this.autoStart){\r
27776                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this);\r
27777             }\r
27778         }\r
27779     },\r
27780 \r
27781     onMouseMove: function(e, target){\r
27782         // HACK: IE hack to see if button was released outside of window. */\r
27783         if(this.active && Ext.isIE && !e.browserEvent.button){\r
27784             e.preventDefault();\r
27785             this.onMouseUp(e);\r
27786             return;\r
27787         }\r
27788 \r
27789         e.preventDefault();\r
27790         var xy = e.getXY(), s = this.startXY;\r
27791         this.lastXY = xy;\r
27792         if(!this.active){\r
27793             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){\r
27794                 this.triggerStart();\r
27795             }else{\r
27796                 return;\r
27797             }\r
27798         }\r
27799         this.fireEvent('mousemove', this, e);\r
27800         this.onDrag(e);\r
27801         this.fireEvent('drag', this, e);\r
27802     },\r
27803 \r
27804     onMouseUp: function(e){\r
27805         var doc = Ext.getDoc();\r
27806         doc.un('mousemove', this.onMouseMove, this);\r
27807         doc.un('mouseup', this.onMouseUp, this);\r
27808         doc.un('selectstart', this.stopSelect, this);\r
27809         e.preventDefault();\r
27810         this.clearStart();\r
27811         var wasActive = this.active;\r
27812         this.active = false;\r
27813         delete this.elRegion;\r
27814         this.fireEvent('mouseup', this, e);\r
27815         if(wasActive){\r
27816             this.onEnd(e);\r
27817             this.fireEvent('dragend', this, e);\r
27818         }\r
27819     },\r
27820 \r
27821     triggerStart: function(isTimer){\r
27822         this.clearStart();\r
27823         this.active = true;\r
27824         this.onStart(this.startXY);\r
27825         this.fireEvent('dragstart', this, this.startXY);\r
27826     },\r
27827 \r
27828     clearStart : function(){\r
27829         if(this.timer){\r
27830             clearTimeout(this.timer);\r
27831             delete this.timer;\r
27832         }\r
27833     },\r
27834 \r
27835     stopSelect : function(e){\r
27836         e.stopEvent();\r
27837         return false;\r
27838     },\r
27839 \r
27840     onBeforeStart : function(e){\r
27841 \r
27842     },\r
27843 \r
27844     onStart : function(xy){\r
27845 \r
27846     },\r
27847 \r
27848     onDrag : function(e){\r
27849 \r
27850     },\r
27851 \r
27852     onEnd : function(e){\r
27853 \r
27854     },\r
27855 \r
27856     getDragTarget : function(){\r
27857         return this.dragTarget;\r
27858     },\r
27859 \r
27860     getDragCt : function(){\r
27861         return this.el;\r
27862     },\r
27863 \r
27864     getXY : function(constrain){\r
27865         return constrain ?\r
27866                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;\r
27867     },\r
27868 \r
27869     getOffset : function(constrain){\r
27870         var xy = this.getXY(constrain);\r
27871         var s = this.startXY;\r
27872         return [s[0]-xy[0], s[1]-xy[1]];\r
27873     },\r
27874 \r
27875     constrainModes: {\r
27876         'point' : function(xy){\r
27877 \r
27878             if(!this.elRegion){\r
27879                 this.elRegion = this.getDragCt().getRegion();\r
27880             }\r
27881 \r
27882             var dr = this.dragRegion;\r
27883 \r
27884             dr.left = xy[0];\r
27885             dr.top = xy[1];\r
27886             dr.right = xy[0];\r
27887             dr.bottom = xy[1];\r
27888 \r
27889             dr.constrainTo(this.elRegion);\r
27890 \r
27891             return [dr.left, dr.top];\r
27892         }\r
27893     }\r
27894 });/**\r
27895  * @class Ext.dd.ScrollManager\r
27896  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>\r
27897  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,\r
27898  * but you can also override most of the configs per scroll container by adding a \r
27899  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},\r
27900  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:\r
27901  * <pre><code>\r
27902 var el = Ext.get('scroll-ct');\r
27903 el.ddScrollConfig = {\r
27904     vthresh: 50,\r
27905     hthresh: -1,\r
27906     frequency: 100,\r
27907     increment: 200\r
27908 };\r
27909 Ext.dd.ScrollManager.register(el);\r
27910 </code></pre>\r
27911  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>\r
27912  * @singleton\r
27913  */\r
27914 Ext.dd.ScrollManager = function(){\r
27915     var ddm = Ext.dd.DragDropMgr;\r
27916     var els = {};\r
27917     var dragEl = null;\r
27918     var proc = {};\r
27919     \r
27920     var onStop = function(e){\r
27921         dragEl = null;\r
27922         clearProc();\r
27923     };\r
27924     \r
27925     var triggerRefresh = function(){\r
27926         if(ddm.dragCurrent){\r
27927              ddm.refreshCache(ddm.dragCurrent.groups);\r
27928         }\r
27929     };\r
27930     \r
27931     var doScroll = function(){\r
27932         if(ddm.dragCurrent){\r
27933             var dds = Ext.dd.ScrollManager;\r
27934             var inc = proc.el.ddScrollConfig ?\r
27935                       proc.el.ddScrollConfig.increment : dds.increment;\r
27936             if(!dds.animate){\r
27937                 if(proc.el.scroll(proc.dir, inc)){\r
27938                     triggerRefresh();\r
27939                 }\r
27940             }else{\r
27941                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);\r
27942             }\r
27943         }\r
27944     };\r
27945     \r
27946     var clearProc = function(){\r
27947         if(proc.id){\r
27948             clearInterval(proc.id);\r
27949         }\r
27950         proc.id = 0;\r
27951         proc.el = null;\r
27952         proc.dir = "";\r
27953     };\r
27954     \r
27955     var startProc = function(el, dir){\r
27956         clearProc();\r
27957         proc.el = el;\r
27958         proc.dir = dir;\r
27959         var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? \r
27960                 el.ddScrollConfig.frequency : Ext.dd.ScrollManager.frequency;\r
27961         proc.id = setInterval(doScroll, freq);\r
27962     };\r
27963     \r
27964     var onFire = function(e, isDrop){\r
27965         if(isDrop || !ddm.dragCurrent){ return; }\r
27966         var dds = Ext.dd.ScrollManager;\r
27967         if(!dragEl || dragEl != ddm.dragCurrent){\r
27968             dragEl = ddm.dragCurrent;\r
27969             // refresh regions on drag start\r
27970             dds.refreshCache();\r
27971         }\r
27972         \r
27973         var xy = Ext.lib.Event.getXY(e);\r
27974         var pt = new Ext.lib.Point(xy[0], xy[1]);\r
27975         for(var id in els){\r
27976             var el = els[id], r = el._region;\r
27977             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;\r
27978             if(r && r.contains(pt) && el.isScrollable()){\r
27979                 if(r.bottom - pt.y <= c.vthresh){\r
27980                     if(proc.el != el){\r
27981                         startProc(el, "down");\r
27982                     }\r
27983                     return;\r
27984                 }else if(r.right - pt.x <= c.hthresh){\r
27985                     if(proc.el != el){\r
27986                         startProc(el, "left");\r
27987                     }\r
27988                     return;\r
27989                 }else if(pt.y - r.top <= c.vthresh){\r
27990                     if(proc.el != el){\r
27991                         startProc(el, "up");\r
27992                     }\r
27993                     return;\r
27994                 }else if(pt.x - r.left <= c.hthresh){\r
27995                     if(proc.el != el){\r
27996                         startProc(el, "right");\r
27997                     }\r
27998                     return;\r
27999                 }\r
28000             }\r
28001         }\r
28002         clearProc();\r
28003     };\r
28004     \r
28005     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);\r
28006     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);\r
28007     \r
28008     return {\r
28009         /**\r
28010          * Registers new overflow element(s) to auto scroll\r
28011          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either\r
28012          */\r
28013         register : function(el){\r
28014             if(Ext.isArray(el)){\r
28015                 for(var i = 0, len = el.length; i < len; i++) {\r
28016                         this.register(el[i]);\r
28017                 }\r
28018             }else{\r
28019                 el = Ext.get(el);\r
28020                 els[el.id] = el;\r
28021             }\r
28022         },\r
28023         \r
28024         /**\r
28025          * Unregisters overflow element(s) so they are no longer scrolled\r
28026          * @param {Mixed/Array} el The id of or the element to be removed or an array of either\r
28027          */\r
28028         unregister : function(el){\r
28029             if(Ext.isArray(el)){\r
28030                 for(var i = 0, len = el.length; i < len; i++) {\r
28031                         this.unregister(el[i]);\r
28032                 }\r
28033             }else{\r
28034                 el = Ext.get(el);\r
28035                 delete els[el.id];\r
28036             }\r
28037         },\r
28038         \r
28039         /**\r
28040          * The number of pixels from the top or bottom edge of a container the pointer needs to be to\r
28041          * trigger scrolling (defaults to 25)\r
28042          * @type Number\r
28043          */\r
28044         vthresh : 25,\r
28045         /**\r
28046          * The number of pixels from the right or left edge of a container the pointer needs to be to\r
28047          * trigger scrolling (defaults to 25)\r
28048          * @type Number\r
28049          */\r
28050         hthresh : 25,\r
28051 \r
28052         /**\r
28053          * The number of pixels to scroll in each scroll increment (defaults to 50)\r
28054          * @type Number\r
28055          */\r
28056         increment : 100,\r
28057         \r
28058         /**\r
28059          * The frequency of scrolls in milliseconds (defaults to 500)\r
28060          * @type Number\r
28061          */\r
28062         frequency : 500,\r
28063         \r
28064         /**\r
28065          * True to animate the scroll (defaults to true)\r
28066          * @type Boolean\r
28067          */\r
28068         animate: true,\r
28069         \r
28070         /**\r
28071          * The animation duration in seconds - \r
28072          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)\r
28073          * @type Number\r
28074          */\r
28075         animDuration: .4,\r
28076         \r
28077         /**\r
28078          * Manually trigger a cache refresh.\r
28079          */\r
28080         refreshCache : function(){\r
28081             for(var id in els){\r
28082                 if(typeof els[id] == 'object'){ // for people extending the object prototype\r
28083                     els[id]._region = els[id].getRegion();\r
28084                 }\r
28085             }\r
28086         }\r
28087     };\r
28088 }();/**\r
28089  * @class Ext.dd.Registry\r
28090  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either\r
28091  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.\r
28092  * @singleton\r
28093  */\r
28094 Ext.dd.Registry = function(){\r
28095     var elements = {}; \r
28096     var handles = {}; \r
28097     var autoIdSeed = 0;\r
28098 \r
28099     var getId = function(el, autogen){\r
28100         if(typeof el == "string"){\r
28101             return el;\r
28102         }\r
28103         var id = el.id;\r
28104         if(!id && autogen !== false){\r
28105             id = "extdd-" + (++autoIdSeed);\r
28106             el.id = id;\r
28107         }\r
28108         return id;\r
28109     };\r
28110     \r
28111     return {\r
28112     /**\r
28113      * Resgister a drag drop element\r
28114      * @param {String/HTMLElement) element The id or DOM node to register\r
28115      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved\r
28116      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code\r
28117      * knows how to interpret, plus there are some specific properties known to the Registry that should be\r
28118      * populated in the data object (if applicable):\r
28119      * <pre>\r
28120 Value      Description<br />\r
28121 ---------  ------------------------------------------<br />\r
28122 handles    Array of DOM nodes that trigger dragging<br />\r
28123            for the element being registered<br />\r
28124 isHandle   True if the element passed in triggers<br />\r
28125            dragging itself, else false\r
28126 </pre>\r
28127      */\r
28128         register : function(el, data){\r
28129             data = data || {};\r
28130             if(typeof el == "string"){\r
28131                 el = document.getElementById(el);\r
28132             }\r
28133             data.ddel = el;\r
28134             elements[getId(el)] = data;\r
28135             if(data.isHandle !== false){\r
28136                 handles[data.ddel.id] = data;\r
28137             }\r
28138             if(data.handles){\r
28139                 var hs = data.handles;\r
28140                 for(var i = 0, len = hs.length; i < len; i++){\r
28141                         handles[getId(hs[i])] = data;\r
28142                 }\r
28143             }\r
28144         },\r
28145 \r
28146     /**\r
28147      * Unregister a drag drop element\r
28148      * @param {String/HTMLElement) element The id or DOM node to unregister\r
28149      */\r
28150         unregister : function(el){\r
28151             var id = getId(el, false);\r
28152             var data = elements[id];\r
28153             if(data){\r
28154                 delete elements[id];\r
28155                 if(data.handles){\r
28156                     var hs = data.handles;\r
28157                     for(var i = 0, len = hs.length; i < len; i++){\r
28158                         delete handles[getId(hs[i], false)];\r
28159                     }\r
28160                 }\r
28161             }\r
28162         },\r
28163 \r
28164     /**\r
28165      * Returns the handle registered for a DOM Node by id\r
28166      * @param {String/HTMLElement} id The DOM node or id to look up\r
28167      * @return {Object} handle The custom handle data\r
28168      */\r
28169         getHandle : function(id){\r
28170             if(typeof id != "string"){ // must be element?\r
28171                 id = id.id;\r
28172             }\r
28173             return handles[id];\r
28174         },\r
28175 \r
28176     /**\r
28177      * Returns the handle that is registered for the DOM node that is the target of the event\r
28178      * @param {Event} e The event\r
28179      * @return {Object} handle The custom handle data\r
28180      */\r
28181         getHandleFromEvent : function(e){\r
28182             var t = Ext.lib.Event.getTarget(e);\r
28183             return t ? handles[t.id] : null;\r
28184         },\r
28185 \r
28186     /**\r
28187      * Returns a custom data object that is registered for a DOM node by id\r
28188      * @param {String/HTMLElement} id The DOM node or id to look up\r
28189      * @return {Object} data The custom data\r
28190      */\r
28191         getTarget : function(id){\r
28192             if(typeof id != "string"){ // must be element?\r
28193                 id = id.id;\r
28194             }\r
28195             return elements[id];\r
28196         },\r
28197 \r
28198     /**\r
28199      * Returns a custom data object that is registered for the DOM node that is the target of the event\r
28200      * @param {Event} e The event\r
28201      * @return {Object} data The custom data\r
28202      */\r
28203         getTargetFromEvent : function(e){\r
28204             var t = Ext.lib.Event.getTarget(e);\r
28205             return t ? elements[t.id] || handles[t.id] : null;\r
28206         }\r
28207     };\r
28208 }();/**\r
28209  * @class Ext.dd.StatusProxy\r
28210  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the\r
28211  * default drag proxy used by all Ext.dd components.\r
28212  * @constructor\r
28213  * @param {Object} config\r
28214  */\r
28215 Ext.dd.StatusProxy = function(config){\r
28216     Ext.apply(this, config);\r
28217     this.id = this.id || Ext.id();\r
28218     this.el = new Ext.Layer({\r
28219         dh: {\r
28220             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [\r
28221                 {tag: "div", cls: "x-dd-drop-icon"},\r
28222                 {tag: "div", cls: "x-dd-drag-ghost"}\r
28223             ]\r
28224         }, \r
28225         shadow: !config || config.shadow !== false\r
28226     });\r
28227     this.ghost = Ext.get(this.el.dom.childNodes[1]);\r
28228     this.dropStatus = this.dropNotAllowed;\r
28229 };\r
28230 \r
28231 Ext.dd.StatusProxy.prototype = {\r
28232     /**\r
28233      * @cfg {String} dropAllowed\r
28234      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").\r
28235      */\r
28236     dropAllowed : "x-dd-drop-ok",\r
28237     /**\r
28238      * @cfg {String} dropNotAllowed\r
28239      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
28240      */\r
28241     dropNotAllowed : "x-dd-drop-nodrop",\r
28242 \r
28243     /**\r
28244      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed\r
28245      * over the current target element.\r
28246      * @param {String} cssClass The css class for the new drop status indicator image\r
28247      */\r
28248     setStatus : function(cssClass){\r
28249         cssClass = cssClass || this.dropNotAllowed;\r
28250         if(this.dropStatus != cssClass){\r
28251             this.el.replaceClass(this.dropStatus, cssClass);\r
28252             this.dropStatus = cssClass;\r
28253         }\r
28254     },\r
28255 \r
28256     /**\r
28257      * Resets the status indicator to the default dropNotAllowed value\r
28258      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it\r
28259      */\r
28260     reset : function(clearGhost){\r
28261         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;\r
28262         this.dropStatus = this.dropNotAllowed;\r
28263         if(clearGhost){\r
28264             this.ghost.update("");\r
28265         }\r
28266     },\r
28267 \r
28268     /**\r
28269      * Updates the contents of the ghost element\r
28270      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a\r
28271      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).\r
28272      */\r
28273     update : function(html){\r
28274         if(typeof html == "string"){\r
28275             this.ghost.update(html);\r
28276         }else{\r
28277             this.ghost.update("");\r
28278             html.style.margin = "0";\r
28279             this.ghost.dom.appendChild(html);\r
28280         }\r
28281         var el = this.ghost.dom.firstChild; \r
28282         if(el){\r
28283             Ext.fly(el).setStyle('float', 'none');\r
28284         }\r
28285     },\r
28286 \r
28287     /**\r
28288      * Returns the underlying proxy {@link Ext.Layer}\r
28289      * @return {Ext.Layer} el\r
28290     */\r
28291     getEl : function(){\r
28292         return this.el;\r
28293     },\r
28294 \r
28295     /**\r
28296      * Returns the ghost element\r
28297      * @return {Ext.Element} el\r
28298      */\r
28299     getGhost : function(){\r
28300         return this.ghost;\r
28301     },\r
28302 \r
28303     /**\r
28304      * Hides the proxy\r
28305      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them\r
28306      */\r
28307     hide : function(clear){\r
28308         this.el.hide();\r
28309         if(clear){\r
28310             this.reset(true);\r
28311         }\r
28312     },\r
28313 \r
28314     /**\r
28315      * Stops the repair animation if it's currently running\r
28316      */\r
28317     stop : function(){\r
28318         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){\r
28319             this.anim.stop();\r
28320         }\r
28321     },\r
28322 \r
28323     /**\r
28324      * Displays this proxy\r
28325      */\r
28326     show : function(){\r
28327         this.el.show();\r
28328     },\r
28329 \r
28330     /**\r
28331      * Force the Layer to sync its shadow and shim positions to the element\r
28332      */\r
28333     sync : function(){\r
28334         this.el.sync();\r
28335     },\r
28336 \r
28337     /**\r
28338      * Causes the proxy to return to its position of origin via an animation.  Should be called after an\r
28339      * invalid drop operation by the item being dragged.\r
28340      * @param {Array} xy The XY position of the element ([x, y])\r
28341      * @param {Function} callback The function to call after the repair is complete\r
28342      * @param {Object} scope The scope in which to execute the callback\r
28343      */\r
28344     repair : function(xy, callback, scope){\r
28345         this.callback = callback;\r
28346         this.scope = scope;\r
28347         if(xy && this.animRepair !== false){\r
28348             this.el.addClass("x-dd-drag-repair");\r
28349             this.el.hideUnders(true);\r
28350             this.anim = this.el.shift({\r
28351                 duration: this.repairDuration || .5,\r
28352                 easing: 'easeOut',\r
28353                 xy: xy,\r
28354                 stopFx: true,\r
28355                 callback: this.afterRepair,\r
28356                 scope: this\r
28357             });\r
28358         }else{\r
28359             this.afterRepair();\r
28360         }\r
28361     },\r
28362 \r
28363     // private\r
28364     afterRepair : function(){\r
28365         this.hide(true);\r
28366         if(typeof this.callback == "function"){\r
28367             this.callback.call(this.scope || this);\r
28368         }\r
28369         this.callback = null;\r
28370         this.scope = null;\r
28371     }\r
28372 };/**\r
28373  * @class Ext.dd.DragSource\r
28374  * @extends Ext.dd.DDProxy\r
28375  * A simple class that provides the basic implementation needed to make any element draggable.\r
28376  * @constructor\r
28377  * @param {Mixed} el The container element\r
28378  * @param {Object} config\r
28379  */\r
28380 Ext.dd.DragSource = function(el, config){\r
28381     this.el = Ext.get(el);\r
28382     if(!this.dragData){\r
28383         this.dragData = {};\r
28384     }\r
28385     \r
28386     Ext.apply(this, config);\r
28387     \r
28388     if(!this.proxy){\r
28389         this.proxy = new Ext.dd.StatusProxy();\r
28390     }\r
28391     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
28392           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});\r
28393     \r
28394     this.dragging = false;\r
28395 };\r
28396 \r
28397 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {\r
28398     /**\r
28399      * @cfg {String} ddGroup\r
28400      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
28401      * interact with other drag drop objects in the same group (defaults to undefined).\r
28402      */\r
28403     /**\r
28404      * @cfg {String} dropAllowed\r
28405      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
28406      */\r
28407     dropAllowed : "x-dd-drop-ok",\r
28408     /**\r
28409      * @cfg {String} dropNotAllowed\r
28410      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
28411      */\r
28412     dropNotAllowed : "x-dd-drop-nodrop",\r
28413 \r
28414     /**\r
28415      * Returns the data object associated with this drag source\r
28416      * @return {Object} data An object containing arbitrary data\r
28417      */\r
28418     getDragData : function(e){\r
28419         return this.dragData;\r
28420     },\r
28421 \r
28422     // private\r
28423     onDragEnter : function(e, id){\r
28424         var target = Ext.dd.DragDropMgr.getDDById(id);\r
28425         this.cachedTarget = target;\r
28426         if(this.beforeDragEnter(target, e, id) !== false){\r
28427             if(target.isNotifyTarget){\r
28428                 var status = target.notifyEnter(this, e, this.dragData);\r
28429                 this.proxy.setStatus(status);\r
28430             }else{\r
28431                 this.proxy.setStatus(this.dropAllowed);\r
28432             }\r
28433             \r
28434             if(this.afterDragEnter){\r
28435                 /**\r
28436                  * An empty function by default, but provided so that you can perform a custom action\r
28437                  * when the dragged item enters the drop target by providing an implementation.\r
28438                  * @param {Ext.dd.DragDrop} target The drop target\r
28439                  * @param {Event} e The event object\r
28440                  * @param {String} id The id of the dragged element\r
28441                  * @method afterDragEnter\r
28442                  */\r
28443                 this.afterDragEnter(target, e, id);\r
28444             }\r
28445         }\r
28446     },\r
28447 \r
28448     /**\r
28449      * An empty function by default, but provided so that you can perform a custom action\r
28450      * before the dragged item enters the drop target and optionally cancel the onDragEnter.\r
28451      * @param {Ext.dd.DragDrop} target The drop target\r
28452      * @param {Event} e The event object\r
28453      * @param {String} id The id of the dragged element\r
28454      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
28455      */\r
28456     beforeDragEnter : function(target, e, id){\r
28457         return true;\r
28458     },\r
28459 \r
28460     // private\r
28461     alignElWithMouse: function() {\r
28462         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);\r
28463         this.proxy.sync();\r
28464     },\r
28465 \r
28466     // private\r
28467     onDragOver : function(e, id){\r
28468         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
28469         if(this.beforeDragOver(target, e, id) !== false){\r
28470             if(target.isNotifyTarget){\r
28471                 var status = target.notifyOver(this, e, this.dragData);\r
28472                 this.proxy.setStatus(status);\r
28473             }\r
28474 \r
28475             if(this.afterDragOver){\r
28476                 /**\r
28477                  * An empty function by default, but provided so that you can perform a custom action\r
28478                  * while the dragged item is over the drop target by providing an implementation.\r
28479                  * @param {Ext.dd.DragDrop} target The drop target\r
28480                  * @param {Event} e The event object\r
28481                  * @param {String} id The id of the dragged element\r
28482                  * @method afterDragOver\r
28483                  */\r
28484                 this.afterDragOver(target, e, id);\r
28485             }\r
28486         }\r
28487     },\r
28488 \r
28489     /**\r
28490      * An empty function by default, but provided so that you can perform a custom action\r
28491      * while the dragged item is over the drop target and optionally cancel the onDragOver.\r
28492      * @param {Ext.dd.DragDrop} target The drop target\r
28493      * @param {Event} e The event object\r
28494      * @param {String} id The id of the dragged element\r
28495      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
28496      */\r
28497     beforeDragOver : function(target, e, id){\r
28498         return true;\r
28499     },\r
28500 \r
28501     // private\r
28502     onDragOut : function(e, id){\r
28503         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
28504         if(this.beforeDragOut(target, e, id) !== false){\r
28505             if(target.isNotifyTarget){\r
28506                 target.notifyOut(this, e, this.dragData);\r
28507             }\r
28508             this.proxy.reset();\r
28509             if(this.afterDragOut){\r
28510                 /**\r
28511                  * An empty function by default, but provided so that you can perform a custom action\r
28512                  * after the dragged item is dragged out of the target without dropping.\r
28513                  * @param {Ext.dd.DragDrop} target The drop target\r
28514                  * @param {Event} e The event object\r
28515                  * @param {String} id The id of the dragged element\r
28516                  * @method afterDragOut\r
28517                  */\r
28518                 this.afterDragOut(target, e, id);\r
28519             }\r
28520         }\r
28521         this.cachedTarget = null;\r
28522     },\r
28523 \r
28524     /**\r
28525      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
28526      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.\r
28527      * @param {Ext.dd.DragDrop} target The drop target\r
28528      * @param {Event} e The event object\r
28529      * @param {String} id The id of the dragged element\r
28530      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
28531      */\r
28532     beforeDragOut : function(target, e, id){\r
28533         return true;\r
28534     },\r
28535     \r
28536     // private\r
28537     onDragDrop : function(e, id){\r
28538         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
28539         if(this.beforeDragDrop(target, e, id) !== false){\r
28540             if(target.isNotifyTarget){\r
28541                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?\r
28542                     this.onValidDrop(target, e, id);\r
28543                 }else{\r
28544                     this.onInvalidDrop(target, e, id);\r
28545                 }\r
28546             }else{\r
28547                 this.onValidDrop(target, e, id);\r
28548             }\r
28549             \r
28550             if(this.afterDragDrop){\r
28551                 /**\r
28552                  * An empty function by default, but provided so that you can perform a custom action\r
28553                  * after a valid drag drop has occurred by providing an implementation.\r
28554                  * @param {Ext.dd.DragDrop} target The drop target\r
28555                  * @param {Event} e The event object\r
28556                  * @param {String} id The id of the dropped element\r
28557                  * @method afterDragDrop\r
28558                  */\r
28559                 this.afterDragDrop(target, e, id);\r
28560             }\r
28561         }\r
28562         delete this.cachedTarget;\r
28563     },\r
28564 \r
28565     /**\r
28566      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
28567      * item is dropped onto the target and optionally cancel the onDragDrop.\r
28568      * @param {Ext.dd.DragDrop} target The drop target\r
28569      * @param {Event} e The event object\r
28570      * @param {String} id The id of the dragged element\r
28571      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel\r
28572      */\r
28573     beforeDragDrop : function(target, e, id){\r
28574         return true;\r
28575     },\r
28576 \r
28577     // private\r
28578     onValidDrop : function(target, e, id){\r
28579         this.hideProxy();\r
28580         if(this.afterValidDrop){\r
28581             /**\r
28582              * An empty function by default, but provided so that you can perform a custom action\r
28583              * after a valid drop has occurred by providing an implementation.\r
28584              * @param {Object} target The target DD \r
28585              * @param {Event} e The event object\r
28586              * @param {String} id The id of the dropped element\r
28587              * @method afterInvalidDrop\r
28588              */\r
28589             this.afterValidDrop(target, e, id);\r
28590         }\r
28591     },\r
28592 \r
28593     // private\r
28594     getRepairXY : function(e, data){\r
28595         return this.el.getXY();  \r
28596     },\r
28597 \r
28598     // private\r
28599     onInvalidDrop : function(target, e, id){\r
28600         this.beforeInvalidDrop(target, e, id);\r
28601         if(this.cachedTarget){\r
28602             if(this.cachedTarget.isNotifyTarget){\r
28603                 this.cachedTarget.notifyOut(this, e, this.dragData);\r
28604             }\r
28605             this.cacheTarget = null;\r
28606         }\r
28607         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);\r
28608 \r
28609         if(this.afterInvalidDrop){\r
28610             /**\r
28611              * An empty function by default, but provided so that you can perform a custom action\r
28612              * after an invalid drop has occurred by providing an implementation.\r
28613              * @param {Event} e The event object\r
28614              * @param {String} id The id of the dropped element\r
28615              * @method afterInvalidDrop\r
28616              */\r
28617             this.afterInvalidDrop(e, id);\r
28618         }\r
28619     },\r
28620 \r
28621     // private\r
28622     afterRepair : function(){\r
28623         if(Ext.enableFx){\r
28624             this.el.highlight(this.hlColor || "c3daf9");\r
28625         }\r
28626         this.dragging = false;\r
28627     },\r
28628 \r
28629     /**\r
28630      * An empty function by default, but provided so that you can perform a custom action after an invalid\r
28631      * drop has occurred.\r
28632      * @param {Ext.dd.DragDrop} target The drop target\r
28633      * @param {Event} e The event object\r
28634      * @param {String} id The id of the dragged element\r
28635      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel\r
28636      */\r
28637     beforeInvalidDrop : function(target, e, id){\r
28638         return true;\r
28639     },\r
28640 \r
28641     // private\r
28642     handleMouseDown : function(e){\r
28643         if(this.dragging) {\r
28644             return;\r
28645         }\r
28646         var data = this.getDragData(e);\r
28647         if(data && this.onBeforeDrag(data, e) !== false){\r
28648             this.dragData = data;\r
28649             this.proxy.stop();\r
28650             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);\r
28651         } \r
28652     },\r
28653 \r
28654     /**\r
28655      * An empty function by default, but provided so that you can perform a custom action before the initial\r
28656      * drag event begins and optionally cancel it.\r
28657      * @param {Object} data An object containing arbitrary data to be shared with drop targets\r
28658      * @param {Event} e The event object\r
28659      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
28660      */\r
28661     onBeforeDrag : function(data, e){\r
28662         return true;\r
28663     },\r
28664 \r
28665     /**\r
28666      * An empty function by default, but provided so that you can perform a custom action once the initial\r
28667      * drag event has begun.  The drag cannot be canceled from this function.\r
28668      * @param {Number} x The x position of the click on the dragged object\r
28669      * @param {Number} y The y position of the click on the dragged object\r
28670      */\r
28671     onStartDrag : Ext.emptyFn,\r
28672 \r
28673     // private override\r
28674     startDrag : function(x, y){\r
28675         this.proxy.reset();\r
28676         this.dragging = true;\r
28677         this.proxy.update("");\r
28678         this.onInitDrag(x, y);\r
28679         this.proxy.show();\r
28680     },\r
28681 \r
28682     // private\r
28683     onInitDrag : function(x, y){\r
28684         var clone = this.el.dom.cloneNode(true);\r
28685         clone.id = Ext.id(); // prevent duplicate ids\r
28686         this.proxy.update(clone);\r
28687         this.onStartDrag(x, y);\r
28688         return true;\r
28689     },\r
28690 \r
28691     /**\r
28692      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}\r
28693      * @return {Ext.dd.StatusProxy} proxy The StatusProxy\r
28694      */\r
28695     getProxy : function(){\r
28696         return this.proxy;  \r
28697     },\r
28698 \r
28699     /**\r
28700      * Hides the drag source's {@link Ext.dd.StatusProxy}\r
28701      */\r
28702     hideProxy : function(){\r
28703         this.proxy.hide();  \r
28704         this.proxy.reset(true);\r
28705         this.dragging = false;\r
28706     },\r
28707 \r
28708     // private\r
28709     triggerCacheRefresh : function(){\r
28710         Ext.dd.DDM.refreshCache(this.groups);\r
28711     },\r
28712 \r
28713     // private - override to prevent hiding\r
28714     b4EndDrag: function(e) {\r
28715     },\r
28716 \r
28717     // private - override to prevent moving\r
28718     endDrag : function(e){\r
28719         this.onEndDrag(this.dragData, e);\r
28720     },\r
28721 \r
28722     // private\r
28723     onEndDrag : function(data, e){\r
28724     },\r
28725     \r
28726     // private - pin to cursor\r
28727     autoOffset : function(x, y) {\r
28728         this.setDelta(-12, -20);\r
28729     }    \r
28730 });/**\r
28731  * @class Ext.dd.DropTarget\r
28732  * @extends Ext.dd.DDTarget\r
28733  * A simple class that provides the basic implementation needed to make any element a drop target that can have\r
28734  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.\r
28735  * @constructor\r
28736  * @param {Mixed} el The container element\r
28737  * @param {Object} config\r
28738  */\r
28739 Ext.dd.DropTarget = function(el, config){\r
28740     this.el = Ext.get(el);\r
28741     \r
28742     Ext.apply(this, config);\r
28743     \r
28744     if(this.containerScroll){\r
28745         Ext.dd.ScrollManager.register(this.el);\r
28746     }\r
28747     \r
28748     Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
28749           {isTarget: true});\r
28750 \r
28751 };\r
28752 \r
28753 Ext.extend(Ext.dd.DropTarget, Ext.dd.DDTarget, {\r
28754     /**\r
28755      * @cfg {String} ddGroup\r
28756      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
28757      * interact with other drag drop objects in the same group (defaults to undefined).\r
28758      */\r
28759     /**\r
28760      * @cfg {String} overClass\r
28761      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").\r
28762      */\r
28763     /**\r
28764      * @cfg {String} dropAllowed\r
28765      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
28766      */\r
28767     dropAllowed : "x-dd-drop-ok",\r
28768     /**\r
28769      * @cfg {String} dropNotAllowed\r
28770      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
28771      */\r
28772     dropNotAllowed : "x-dd-drop-nodrop",\r
28773 \r
28774     // private\r
28775     isTarget : true,\r
28776 \r
28777     // private\r
28778     isNotifyTarget : true,\r
28779 \r
28780     /**\r
28781      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the\r
28782      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element\r
28783      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.\r
28784      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
28785      * @param {Event} e The event\r
28786      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
28787      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
28788      * underlying {@link Ext.dd.StatusProxy} can be updated\r
28789      */\r
28790     notifyEnter : function(dd, e, data){\r
28791         if(this.overClass){\r
28792             this.el.addClass(this.overClass);\r
28793         }\r
28794         return this.dropAllowed;\r
28795     },\r
28796 \r
28797     /**\r
28798      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.\r
28799      * This method will be called on every mouse movement while the drag source is over the drop target.\r
28800      * This default implementation simply returns the dropAllowed config value.\r
28801      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
28802      * @param {Event} e The event\r
28803      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
28804      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
28805      * underlying {@link Ext.dd.StatusProxy} can be updated\r
28806      */\r
28807     notifyOver : function(dd, e, data){\r
28808         return this.dropAllowed;\r
28809     },\r
28810 \r
28811     /**\r
28812      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged\r
28813      * out of the target without dropping.  This default implementation simply removes the CSS class specified by\r
28814      * overClass (if any) from the drop element.\r
28815      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
28816      * @param {Event} e The event\r
28817      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
28818      */\r
28819     notifyOut : function(dd, e, data){\r
28820         if(this.overClass){\r
28821             this.el.removeClass(this.overClass);\r
28822         }\r
28823     },\r
28824 \r
28825     /**\r
28826      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has\r
28827      * been dropped on it.  This method has no default implementation and returns false, so you must provide an\r
28828      * implementation that does something to process the drop event and returns true so that the drag source's\r
28829      * repair action does not run.\r
28830      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
28831      * @param {Event} e The event\r
28832      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
28833      * @return {Boolean} True if the drop was valid, else false\r
28834      */\r
28835     notifyDrop : function(dd, e, data){\r
28836         return false;\r
28837     }\r
28838 });/**\r
28839  * @class Ext.dd.DragZone\r
28840  * @extends Ext.dd.DragSource\r
28841  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>\r
28842  * <p>This class does not move the drag target nodes, but a proxy element which may contain\r
28843  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a\r
28844  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>\r
28845  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some\r
28846  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class\r
28847  * is the most efficient way to "activate" those nodes.</p>\r
28848  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.\r
28849  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure\r
28850  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed\r
28851  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
28852  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
28853  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following\r
28854  * technique. Knowledge of the use of the DataView is required:</p><pre><code>\r
28855 myDataView.on('render', function() {\r
28856     myDataView.dragZone = new Ext.dd.DragZone(myDataView.getEl(), {\r
28857 \r
28858 //      On receipt of a mousedown event, see if it is within a DataView node.\r
28859 //      Return a drag data object if so.\r
28860         getDragData: function(e) {\r
28861 \r
28862 //          Use the DataView's own itemSelector (a mandatory property) to\r
28863 //          test if the mousedown is within one of the DataView's nodes.\r
28864             var sourceEl = e.getTarget(myDataView.itemSelector, 10);\r
28865 \r
28866 //          If the mousedown is within a DataView node, clone the node to produce\r
28867 //          a ddel element for use by the drag proxy. Also add application data\r
28868 //          to the returned data object.\r
28869             if (sourceEl) {\r
28870                 d = sourceEl.cloneNode(true);\r
28871                 d.id = Ext.id();\r
28872                 return {\r
28873                     ddel: d,\r
28874                     sourceEl: sourceEl,\r
28875                     repairXY: Ext.fly(sourceEl).getXY(),\r
28876                     sourceStore: myDataView.store,\r
28877                     draggedRecord: v.getRecord(sourceEl)\r
28878                 }\r
28879             }\r
28880         },\r
28881 \r
28882 //      Provide coordinates for the proxy to slide back to on failed drag.\r
28883 //      This is the original XY coordinates of the draggable element captured\r
28884 //      in the getDragData method.\r
28885         getRepairXY: function() {\r
28886             return this.dragData.repairXY;\r
28887         }\r
28888     });\r
28889 });</code></pre>\r
28890  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which\r
28891  * cooperates with this DragZone.\r
28892  * @constructor\r
28893  * @param {Mixed} el The container element\r
28894  * @param {Object} config\r
28895  */\r
28896 Ext.dd.DragZone = function(el, config){\r
28897     Ext.dd.DragZone.superclass.constructor.call(this, el, config);\r
28898     if(this.containerScroll){\r
28899         Ext.dd.ScrollManager.register(this.el);\r
28900     }\r
28901 };\r
28902 \r
28903 Ext.extend(Ext.dd.DragZone, Ext.dd.DragSource, {\r
28904     /**\r
28905      * This property contains the data representing the dragged object. This data is set up by the implementation\r
28906      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain\r
28907      * any other data according to the application's needs.\r
28908      * @type Object\r
28909      * @property dragData\r
28910      */\r
28911     /**\r
28912      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager\r
28913      * for auto scrolling during drag operations.\r
28914      */\r
28915     /**\r
28916      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair\r
28917      * method after a failed drop (defaults to "c3daf9" - light blue)\r
28918      */\r
28919 \r
28920     /**\r
28921      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}\r
28922      * for a valid target to drag based on the mouse down. Override this method\r
28923      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned\r
28924      * object has a "ddel" attribute (with an HTML Element) for other functions to work.\r
28925      * @param {EventObject} e The mouse down event\r
28926      * @return {Object} The dragData\r
28927      */\r
28928     getDragData : function(e){\r
28929         return Ext.dd.Registry.getHandleFromEvent(e);\r
28930     },\r
28931     \r
28932     /**\r
28933      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the\r
28934      * this.dragData.ddel\r
28935      * @param {Number} x The x position of the click on the dragged object\r
28936      * @param {Number} y The y position of the click on the dragged object\r
28937      * @return {Boolean} true to continue the drag, false to cancel\r
28938      */\r
28939     onInitDrag : function(x, y){\r
28940         this.proxy.update(this.dragData.ddel.cloneNode(true));\r
28941         this.onStartDrag(x, y);\r
28942         return true;\r
28943     },\r
28944     \r
28945     /**\r
28946      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel \r
28947      */\r
28948     afterRepair : function(){\r
28949         if(Ext.enableFx){\r
28950             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
28951         }\r
28952         this.dragging = false;\r
28953     },\r
28954 \r
28955     /**\r
28956      * Called before a repair of an invalid drop to get the XY to animate to. By default returns\r
28957      * the XY of this.dragData.ddel\r
28958      * @param {EventObject} e The mouse up event\r
28959      * @return {Array} The xy location (e.g. [100, 200])\r
28960      */\r
28961     getRepairXY : function(e){\r
28962         return Ext.Element.fly(this.dragData.ddel).getXY();  \r
28963     }\r
28964 });/**\r
28965  * @class Ext.dd.DropZone\r
28966  * @extends Ext.dd.DropTarget\r
28967  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>\r
28968  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.\r
28969  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the\r
28970  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed\r
28971  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
28972  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
28973  * {@link Ext.DomQuery} selector.</p>\r
28974  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over\r
28975  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},\r
28976  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations\r
28977  * of these methods to provide application-specific behaviour for these events to update both\r
28978  * application state, and UI state.</p>\r
28979  * <p>For example to make a GridPanel a cooperating target with the example illustrated in\r
28980  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>\r
28981 myGridPanel.on('render', function() {\r
28982     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {\r
28983 \r
28984 //      If the mouse is over a grid row, return that node. This is\r
28985 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions\r
28986         getTargetFromEvent: function(e) {\r
28987             return e.getTarget(myGridPanel.getView().rowSelector);\r
28988         },\r
28989 \r
28990 //      On entry into a target node, highlight that node.\r
28991         onNodeEnter : function(target, dd, e, data){ \r
28992             Ext.fly(target).addClass('my-row-highlight-class');\r
28993         },\r
28994 \r
28995 //      On exit from a target node, unhighlight that node.\r
28996         onNodeOut : function(target, dd, e, data){ \r
28997             Ext.fly(target).removeClass('my-row-highlight-class');\r
28998         },\r
28999 \r
29000 //      While over a target node, return the default drop allowed class which\r
29001 //      places a "tick" icon into the drag proxy.\r
29002         onNodeOver : function(target, dd, e, data){ \r
29003             return Ext.dd.DropZone.prototype.dropAllowed;\r
29004         },\r
29005 \r
29006 //      On node drop we can interrogate the target to find the underlying\r
29007 //      application object that is the real target of the dragged data.\r
29008 //      In this case, it is a Record in the GridPanel's Store.\r
29009 //      We can use the data set up by the DragZone's getDragData method to read\r
29010 //      any data we decided to attach in the DragZone's getDragData method.\r
29011         onNodeDrop : function(target, dd, e, data){\r
29012             var rowIndex = myGridPanel.getView().findRowIndex(target);\r
29013             var r = myGridPanel.getStore().getAt(rowIndex);\r
29014             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +\r
29015                 ' on Record id ' + r.id);\r
29016             return true;\r
29017         }\r
29018     });\r
29019 }\r
29020 </code></pre>\r
29021  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which\r
29022  * cooperates with this DropZone.\r
29023  * @constructor\r
29024  * @param {Mixed} el The container element\r
29025  * @param {Object} config\r
29026  */\r
29027 Ext.dd.DropZone = function(el, config){\r
29028     Ext.dd.DropZone.superclass.constructor.call(this, el, config);\r
29029 };\r
29030 \r
29031 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {\r
29032     /**\r
29033      * Returns a custom data object associated with the DOM node that is the target of the event.  By default\r
29034      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to\r
29035      * provide your own custom lookup.\r
29036      * @param {Event} e The event\r
29037      * @return {Object} data The custom data\r
29038      */\r
29039     getTargetFromEvent : function(e){\r
29040         return Ext.dd.Registry.getTargetFromEvent(e);\r
29041     },\r
29042 \r
29043     /**\r
29044      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node\r
29045      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
29046      * This method has no default implementation and should be overridden to provide\r
29047      * node-specific processing if necessary.\r
29048      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from \r
29049      * {@link #getTargetFromEvent} for this node)\r
29050      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29051      * @param {Event} e The event\r
29052      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29053      */\r
29054     onNodeEnter : function(n, dd, e, data){\r
29055         \r
29056     },\r
29057 \r
29058     /**\r
29059      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node\r
29060      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
29061      * The default implementation returns this.dropNotAllowed, so it should be\r
29062      * overridden to provide the proper feedback.\r
29063      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29064      * {@link #getTargetFromEvent} for this node)\r
29065      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29066      * @param {Event} e The event\r
29067      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29068      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29069      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29070      */\r
29071     onNodeOver : function(n, dd, e, data){\r
29072         return this.dropAllowed;\r
29073     },\r
29074 \r
29075     /**\r
29076      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of\r
29077      * the drop node without dropping.  This method has no default implementation and should be overridden to provide\r
29078      * node-specific processing if necessary.\r
29079      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29080      * {@link #getTargetFromEvent} for this node)\r
29081      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29082      * @param {Event} e The event\r
29083      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29084      */\r
29085     onNodeOut : function(n, dd, e, data){\r
29086         \r
29087     },\r
29088 \r
29089     /**\r
29090      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto\r
29091      * the drop node.  The default implementation returns false, so it should be overridden to provide the\r
29092      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.\r
29093      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29094      * {@link #getTargetFromEvent} for this node)\r
29095      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29096      * @param {Event} e The event\r
29097      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29098      * @return {Boolean} True if the drop was valid, else false\r
29099      */\r
29100     onNodeDrop : function(n, dd, e, data){\r
29101         return false;\r
29102     },\r
29103 \r
29104     /**\r
29105      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,\r
29106      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so\r
29107      * it should be overridden to provide the proper feedback if necessary.\r
29108      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29109      * @param {Event} e The event\r
29110      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29111      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29112      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29113      */\r
29114     onContainerOver : function(dd, e, data){\r
29115         return this.dropNotAllowed;\r
29116     },\r
29117 \r
29118     /**\r
29119      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,\r
29120      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be\r
29121      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to\r
29122      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.\r
29123      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29124      * @param {Event} e The event\r
29125      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29126      * @return {Boolean} True if the drop was valid, else false\r
29127      */\r
29128     onContainerDrop : function(dd, e, data){\r
29129         return false;\r
29130     },\r
29131 \r
29132     /**\r
29133      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over\r
29134      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop\r
29135      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops\r
29136      * you should override this method and provide a custom implementation.\r
29137      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29138      * @param {Event} e The event\r
29139      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29140      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29141      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29142      */\r
29143     notifyEnter : function(dd, e, data){\r
29144         return this.dropNotAllowed;\r
29145     },\r
29146 \r
29147     /**\r
29148      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.\r
29149      * This method will be called on every mouse movement while the drag source is over the drop zone.\r
29150      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically\r
29151      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits\r
29152      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a\r
29153      * registered node, it will call {@link #onContainerOver}.\r
29154      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29155      * @param {Event} e The event\r
29156      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29157      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29158      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29159      */\r
29160     notifyOver : function(dd, e, data){\r
29161         var n = this.getTargetFromEvent(e);\r
29162         if(!n){ // not over valid drop target\r
29163             if(this.lastOverNode){\r
29164                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
29165                 this.lastOverNode = null;\r
29166             }\r
29167             return this.onContainerOver(dd, e, data);\r
29168         }\r
29169         if(this.lastOverNode != n){\r
29170             if(this.lastOverNode){\r
29171                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
29172             }\r
29173             this.onNodeEnter(n, dd, e, data);\r
29174             this.lastOverNode = n;\r
29175         }\r
29176         return this.onNodeOver(n, dd, e, data);\r
29177     },\r
29178 \r
29179     /**\r
29180      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged\r
29181      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification\r
29182      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.\r
29183      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29184      * @param {Event} e The event\r
29185      * @param {Object} data An object containing arbitrary data supplied by the drag zone\r
29186      */\r
29187     notifyOut : function(dd, e, data){\r
29188         if(this.lastOverNode){\r
29189             this.onNodeOut(this.lastOverNode, dd, e, data);\r
29190             this.lastOverNode = null;\r
29191         }\r
29192     },\r
29193 \r
29194     /**\r
29195      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has\r
29196      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there\r
29197      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,\r
29198      * otherwise it will call {@link #onContainerDrop}.\r
29199      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29200      * @param {Event} e The event\r
29201      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29202      * @return {Boolean} True if the drop was valid, else false\r
29203      */\r
29204     notifyDrop : function(dd, e, data){\r
29205         if(this.lastOverNode){\r
29206             this.onNodeOut(this.lastOverNode, dd, e, data);\r
29207             this.lastOverNode = null;\r
29208         }\r
29209         var n = this.getTargetFromEvent(e);\r
29210         return n ?\r
29211             this.onNodeDrop(n, dd, e, data) :\r
29212             this.onContainerDrop(dd, e, data);\r
29213     },\r
29214 \r
29215     // private\r
29216     triggerCacheRefresh : function(){\r
29217         Ext.dd.DDM.refreshCache(this.groups);\r
29218     }  \r
29219 });/**\r
29220  * @class Ext.Element\r
29221  */\r
29222 Ext.Element.addMethods({\r
29223     /**\r
29224      * Initializes a {@link Ext.dd.DD} drag drop object for this element.\r
29225      * @param {String} group The group the DD object is member of\r
29226      * @param {Object} config The DD config object\r
29227      * @param {Object} overrides An object containing methods to override/implement on the DD object\r
29228      * @return {Ext.dd.DD} The DD object\r
29229      */\r
29230     initDD : function(group, config, overrides){\r
29231         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);\r
29232         return Ext.apply(dd, overrides);\r
29233     },\r
29234 \r
29235     /**\r
29236      * Initializes a {@link Ext.dd.DDProxy} object for this element.\r
29237      * @param {String} group The group the DDProxy object is member of\r
29238      * @param {Object} config The DDProxy config object\r
29239      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object\r
29240      * @return {Ext.dd.DDProxy} The DDProxy object\r
29241      */\r
29242     initDDProxy : function(group, config, overrides){\r
29243         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);\r
29244         return Ext.apply(dd, overrides);\r
29245     },\r
29246 \r
29247     /**\r
29248      * Initializes a {@link Ext.dd.DDTarget} object for this element.\r
29249      * @param {String} group The group the DDTarget object is member of\r
29250      * @param {Object} config The DDTarget config object\r
29251      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object\r
29252      * @return {Ext.dd.DDTarget} The DDTarget object\r
29253      */\r
29254     initDDTarget : function(group, config, overrides){\r
29255         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);\r
29256         return Ext.apply(dd, overrides);\r
29257     }\r
29258 });/**
29259  * @class Ext.data.Api
29260  * @extends Object
29261  * Ext.data.Api is a singleton designed to manage the data API including methods
29262  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
29263  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
29264  * GET, POST, PUT and DELETE to CRUD actions.
29265  * @singleton
29266  */
29267 Ext.data.Api = (function() {
29268
29269     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
29270     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
29271     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
29272     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
29273     var validActions = {};
29274
29275     return {
29276         /**
29277          * Defined actions corresponding to remote actions:
29278          * <pre><code>
29279 actions: {
29280     create  : 'create',  // Text representing the remote-action to create records on server.
29281     read    : 'read',    // Text representing the remote-action to read/load data from server.
29282     update  : 'update',  // Text representing the remote-action to update records on server.
29283     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
29284 }
29285          * </code></pre>
29286          * @property actions
29287          * @type Object
29288          */
29289         actions : {
29290             create  : 'create',
29291             read    : 'read',
29292             update  : 'update',
29293             destroy : 'destroy'
29294         },
29295
29296         /**
29297          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
29298          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
29299          * Defaults to:
29300          * <pre><code>
29301 restActions : {
29302     create  : 'POST',
29303     read    : 'GET',
29304     update  : 'PUT',
29305     destroy : 'DELETE'
29306 },
29307          * </code></pre>
29308          */
29309         restActions : {
29310             create  : 'POST',
29311             read    : 'GET',
29312             update  : 'PUT',
29313             destroy : 'DELETE'
29314         },
29315
29316         /**
29317          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
29318          * @param {String} action
29319          * @param {String[]}(Optional) List of available CRUD actions.  Pass in list when executing multiple times for efficiency.
29320          * @return {Boolean}
29321          */
29322         isAction : function(action) {
29323             return (Ext.data.Api.actions[action]) ? true : false;
29324         },
29325
29326         /**
29327          * 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
29328          * 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
29329          * 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
29330          * required.  This method will cache discovered KEYS into the private validActions hash.
29331          * @param {String} name The runtime name of the action.
29332          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
29333          * @nodoc
29334          */
29335         getVerb : function(name) {
29336             if (validActions[name]) {
29337                 return validActions[name];  // <-- found in cache.  return immediately.
29338             }
29339             for (var verb in this.actions) {
29340                 if (this.actions[verb] === name) {
29341                     validActions[name] = verb;
29342                     break;
29343                 }
29344             }
29345             return (validActions[name] !== undefined) ? validActions[name] : null;
29346         },
29347
29348         /**
29349          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
29350          * otherwise returns an array of mistakes.
29351          * @return {String[]||true}
29352          */
29353         isValid : function(api){
29354             var invalid = [];
29355             var crud = this.actions; // <-- cache a copy of the actions.
29356             for (var action in api) {
29357                 if (!(action in crud)) {
29358                     invalid.push(action);
29359                 }
29360             }
29361             return (!invalid.length) ? true : invalid;
29362         },
29363
29364         /**
29365          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
29366          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
29367          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
29368          * @param {Ext.data.DataProxy} proxy
29369          * @param {String} verb
29370          * @return {Boolean}
29371          */
29372         hasUniqueUrl : function(proxy, verb) {
29373             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
29374             var unique = true;
29375             for (var action in proxy.api) {
29376                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
29377                     break;
29378                 }
29379             }
29380             return unique;
29381         },
29382
29383         /**
29384          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
29385          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
29386          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
29387          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
29388          * be used.  If the method configuration parameter is not specified, POST will be used.
29389          <pre><code>
29390 new Ext.data.HttpProxy({
29391     method: "POST",     // <-- default HTTP method when not specified.
29392     api: {
29393         create: 'create.php',
29394         load: 'read.php',
29395         save: 'save.php',
29396         destroy: 'destroy.php'
29397     }
29398 });
29399
29400 // Alternatively, one can use the object-form to specify the API
29401 new Ext.data.HttpProxy({
29402     api: {
29403         load: {url: 'read.php', method: 'GET'},
29404         create: 'create.php',
29405         destroy: 'destroy.php',
29406         save: 'update.php'
29407     }
29408 });
29409         </code></pre>
29410          *
29411          * @param {Ext.data.DataProxy} proxy
29412          */
29413         prepare : function(proxy) {
29414             if (!proxy.api) {
29415                 proxy.api = {}; // <-- No api?  create a blank one.
29416             }
29417             for (var verb in this.actions) {
29418                 var action = this.actions[verb];
29419                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
29420                 if (typeof(proxy.api[action]) == 'string') {
29421                     proxy.api[action] = {
29422                         url: proxy.api[action]
29423                     };
29424                 }
29425             }
29426         },
29427
29428         /**
29429          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
29430          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
29431          * @param {Ext.data.DataProxy} proxy
29432          */
29433         restify : function(proxy) {
29434             proxy.restful = true;
29435             for (var verb in this.restActions) {
29436                 proxy.api[this.actions[verb]].method = this.restActions[verb];
29437             }
29438         }
29439     };
29440 })();
29441
29442 /**
29443  * @class Ext.data.Api.Error
29444  * @extends Ext.Error
29445  * Error class for Ext.data.Api errors
29446  */
29447 Ext.data.Api.Error = Ext.extend(Ext.Error, {
29448     constructor : function(message, arg) {
29449         this.arg = arg;
29450         Ext.Error.call(this, message);
29451     },
29452     name: 'Ext.data.Api'
29453 });
29454 Ext.apply(Ext.data.Api.Error.prototype, {
29455     lang: {
29456         '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.',
29457         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
29458         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
29459         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
29460     }
29461 });
29462 \r
29463 /**\r
29464  * @class Ext.data.SortTypes\r
29465  * @singleton\r
29466  * Defines the default sorting (casting?) comparison functions used when sorting data.\r
29467  */\r
29468 Ext.data.SortTypes = {\r
29469     /**\r
29470      * Default sort that does nothing\r
29471      * @param {Mixed} s The value being converted\r
29472      * @return {Mixed} The comparison value\r
29473      */\r
29474     none : function(s){\r
29475         return s;\r
29476     },\r
29477     \r
29478     /**\r
29479      * The regular expression used to strip tags\r
29480      * @type {RegExp}\r
29481      * @property\r
29482      */\r
29483     stripTagsRE : /<\/?[^>]+>/gi,\r
29484     \r
29485     /**\r
29486      * Strips all HTML tags to sort on text only\r
29487      * @param {Mixed} s The value being converted\r
29488      * @return {String} The comparison value\r
29489      */\r
29490     asText : function(s){\r
29491         return String(s).replace(this.stripTagsRE, "");\r
29492     },\r
29493     \r
29494     /**\r
29495      * Strips all HTML tags to sort on text only - Case insensitive\r
29496      * @param {Mixed} s The value being converted\r
29497      * @return {String} The comparison value\r
29498      */\r
29499     asUCText : function(s){\r
29500         return String(s).toUpperCase().replace(this.stripTagsRE, "");\r
29501     },\r
29502     \r
29503     /**\r
29504      * Case insensitive string\r
29505      * @param {Mixed} s The value being converted\r
29506      * @return {String} The comparison value\r
29507      */\r
29508     asUCString : function(s) {\r
29509         return String(s).toUpperCase();\r
29510     },\r
29511     \r
29512     /**\r
29513      * Date sorting\r
29514      * @param {Mixed} s The value being converted\r
29515      * @return {Number} The comparison value\r
29516      */\r
29517     asDate : function(s) {\r
29518         if(!s){\r
29519             return 0;\r
29520         }\r
29521         if(Ext.isDate(s)){\r
29522             return s.getTime();\r
29523         }\r
29524         return Date.parse(String(s));\r
29525     },\r
29526     \r
29527     /**\r
29528      * Float sorting\r
29529      * @param {Mixed} s The value being converted\r
29530      * @return {Float} The comparison value\r
29531      */\r
29532     asFloat : function(s) {\r
29533         var val = parseFloat(String(s).replace(/,/g, ""));\r
29534         return isNaN(val) ? 0 : val;\r
29535     },\r
29536     \r
29537     /**\r
29538      * Integer sorting\r
29539      * @param {Mixed} s The value being converted\r
29540      * @return {Number} The comparison value\r
29541      */\r
29542     asInt : function(s) {\r
29543         var val = parseInt(String(s).replace(/,/g, ""), 10);\r
29544         return isNaN(val) ? 0 : val;\r
29545     }\r
29546 };/**
29547  * @class Ext.data.Record
29548  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
29549  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
29550  * to access Records cached in an {@link Ext.data.Store} object.</p>
29551  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
29552  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
29553  * objects.</p>
29554  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
29555  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
29556  * copy of the Record, and insert the new instance into the other Store.</p>
29557  * <p>When serializing a Record for submission to the server, be aware that it contains many private
29558  * properties, and also a reference to its owning Store which in turn holds references to its Records.
29559  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
29560  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
29561  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
29562  * @constructor
29563  * This constructor should not be used to create Record objects. Instead, use {@link #create} to
29564  * generate a subclass of Ext.data.Record configured with information about its constituent fields.
29565  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
29566  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
29567  * for each field will be assigned.
29568  * @param {Object} id (Optional) The id of the Record. This id should be unique, and is used by the
29569  * {@link Ext.data.Store} object which owns the Record to index its collection of Records. If
29570  * an <code>id</code> is not specified a <b><code>{@link #phantom}</code></b> Record will be created
29571  * with an {@link #Record.id automatically generated id}.
29572  */
29573 Ext.data.Record = function(data, id){
29574     // if no id, call the auto id method
29575     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
29576     this.data = data || {};
29577 };
29578
29579 /**
29580  * Generate a constructor for a specific Record layout.
29581  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
29582  * The constructor generated by this method may be used to create new Record instances. The data
29583  * object must contain properties named after the {@link Ext.data.Field field}
29584  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
29585 // create a Record constructor from a description of the fields
29586 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
29587     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
29588     {name: 'author', mapping: 'username', allowBlank: false},
29589     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
29590     {name: 'lastPost', mapping: 'post_time', type: 'date'},
29591     {name: 'lastPoster', mapping: 'user2'},
29592     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
29593     // In the simplest case, if no properties other than <tt>name</tt> are required,
29594     // a field definition may consist of just a String for the field name.
29595     'signature'
29596 ]);
29597
29598 // create Record instance
29599 var myNewRecord = new TopicRecord(
29600     {
29601         title: 'Do my job please',
29602         author: 'noobie',
29603         totalPosts: 1,
29604         lastPost: new Date(),
29605         lastPoster: 'Animal',
29606         excerpt: 'No way dude!',
29607         signature: ''
29608     },
29609     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
29610 );
29611 myStore.{@link Ext.data.Store#add add}(myNewRecord);
29612 </code></pre>
29613  * @method create
29614  * @return {function} A constructor which is used to create new Records according
29615  * to the definition. The constructor has the same signature as {@link #Ext.data.Record}.
29616  * @static
29617  */
29618 Ext.data.Record.create = function(o){
29619     var f = Ext.extend(Ext.data.Record, {});
29620     var p = f.prototype;
29621     p.fields = new Ext.util.MixedCollection(false, function(field){
29622         return field.name;
29623     });
29624     for(var i = 0, len = o.length; i < len; i++){
29625         p.fields.add(new Ext.data.Field(o[i]));
29626     }
29627     f.getField = function(name){
29628         return p.fields.get(name);
29629     };
29630     return f;
29631 };
29632
29633 Ext.data.Record.PREFIX = 'ext-record';
29634 Ext.data.Record.AUTO_ID = 1;
29635 Ext.data.Record.EDIT = 'edit';
29636 Ext.data.Record.REJECT = 'reject';
29637 Ext.data.Record.COMMIT = 'commit';
29638
29639
29640 /**
29641  * Generates a sequential id. This method is typically called when a record is {@link #create}d
29642  * and {@link #Record no id has been specified}. The returned id takes the form:
29643  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
29644  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
29645  * (defaults to <tt>'ext-record'</tt>)</p></li>
29646  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
29647  * (defaults to <tt>1</tt> initially)</p></li>
29648  * </ul></div>
29649  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
29650  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
29651  */
29652 Ext.data.Record.id = function(rec) {
29653     rec.phantom = true;
29654     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
29655 };
29656
29657 Ext.data.Record.prototype = {
29658     /**
29659      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
29660      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
29661      * @property fields
29662      * @type Ext.util.MixedCollection
29663      */
29664     /**
29665      * An object hash representing the data for this Record. Every field name in the Record definition
29666      * is represented by a property of that name in this object. Note that unless you specified a field
29667      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
29668      * an <tt>id</tt> property.
29669      * @property data
29670      * @type {Object}
29671      */
29672     /**
29673      * The unique ID of the Record {@link #Record as specified at construction time}.
29674      * @property id
29675      * @type {Object}
29676      */
29677     /**
29678      * Readonly flag - true if this Record has been modified.
29679      * @type Boolean
29680      */
29681     dirty : false,
29682     editing : false,
29683     error: null,
29684     /**
29685      * This object contains a key and value storing the original values of all modified
29686      * fields or is null if no fields have been modified.
29687      * @property modified
29688      * @type {Object}
29689      */
29690     modified: null,
29691     /**
29692      * <tt>false</tt> when the record does not yet exist in a server-side database (see
29693      * {@link #markDirty}).  Any record which has a real database pk set as its id property
29694      * is NOT a phantom -- it's real.
29695      * @property phantom
29696      * @type {Boolean}
29697      */
29698     phantom : false,
29699
29700     // private
29701     join : function(store){
29702         /**
29703          * The {@link Ext.data.Store} to which this Record belongs.
29704          * @property store
29705          * @type {Ext.data.Store}
29706          */
29707         this.store = store;
29708     },
29709
29710     /**
29711      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
29712      * <pre><code>
29713 // record has a field named 'firstname'
29714 var Employee = Ext.data.Record.{@link #create}([
29715     {name: 'firstname'},
29716     ...
29717 ]);
29718
29719 // update the 2nd record in the store:
29720 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
29721
29722 // set the value (shows dirty flag):
29723 rec.set('firstname', 'Betty');
29724
29725 // commit the change (removes dirty flag):
29726 rec.{@link #commit}();
29727
29728 // update the record in the store, bypass setting dirty flag,
29729 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
29730 rec.{@link #data}['firstname'] = 'Wilma'); // updates record, but not the view
29731 rec.{@link #commit}(); // updates the view
29732      * </code></pre>
29733      * <b>Notes</b>:<div class="mdetail-params"><ul>
29734      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
29735      * will execute an XHR to the server.</li>
29736      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
29737      * event firing while using set().</li>
29738      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
29739      * event fire.</li>
29740      * </ul></div>
29741      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
29742      * @param {Object} value The value to set the field to.
29743      */
29744     set : function(name, value){
29745         var isObj = (typeof value === 'object');
29746         if(!isObj && String(this.data[name]) === String(value)){
29747             return;
29748         } else if (isObj && Ext.encode(this.data[name]) === Ext.encode(value)) {
29749             return;
29750         }
29751         this.dirty = true;
29752         if(!this.modified){
29753             this.modified = {};
29754         }
29755         if(typeof this.modified[name] == 'undefined'){
29756             this.modified[name] = this.data[name];
29757         }
29758         this.data[name] = value;
29759         if(!this.editing){
29760             this.afterEdit();
29761         }
29762     },
29763
29764     // private
29765     afterEdit: function(){
29766         if(this.store){
29767             this.store.afterEdit(this);
29768         }
29769     },
29770
29771     // private
29772     afterReject: function(){
29773         if(this.store){
29774             this.store.afterReject(this);
29775         }
29776     },
29777
29778     // private
29779     afterCommit: function(){
29780         if(this.store){
29781             this.store.afterCommit(this);
29782         }
29783     },
29784
29785     /**
29786      * Get the value of the {@link Ext.data.Field#name named field}.
29787      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
29788      * @return {Object} The value of the field.
29789      */
29790     get : function(name){
29791         return this.data[name];
29792     },
29793
29794     /**
29795      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
29796      * are relayed to the containing store.
29797      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
29798      */
29799     beginEdit : function(){
29800         this.editing = true;
29801         this.modified = this.modified || {};
29802     },
29803
29804     /**
29805      * Cancels all changes made in the current edit operation.
29806      */
29807     cancelEdit : function(){
29808         this.editing = false;
29809         delete this.modified;
29810     },
29811
29812     /**
29813      * End an edit. If any data was modified, the containing store is notified
29814      * (ie, the store's <code>update</code> event will fire).
29815      */
29816     endEdit : function(){
29817         this.editing = false;
29818         if(this.dirty){
29819             this.afterEdit();
29820         }
29821     },
29822
29823     /**
29824      * Usually called by the {@link Ext.data.Store} which owns the Record.
29825      * Rejects all changes made to the Record since either creation, or the last commit operation.
29826      * Modified fields are reverted to their original values.
29827      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
29828      * to have their code notified of reject operations.</p>
29829      * @param {Boolean} silent (optional) True to skip notification of the owning
29830      * store of the change (defaults to false)
29831      */
29832     reject : function(silent){
29833         var m = this.modified;
29834         for(var n in m){
29835             if(typeof m[n] != "function"){
29836                 this.data[n] = m[n];
29837             }
29838         }
29839         this.dirty = false;
29840         delete this.modified;
29841         this.editing = false;
29842         if(silent !== true){
29843             this.afterReject();
29844         }
29845     },
29846
29847     /**
29848      * Usually called by the {@link Ext.data.Store} which owns the Record.
29849      * Commits all changes made to the Record since either creation, or the last commit operation.
29850      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
29851      * to have their code notified of commit operations.</p>
29852      * @param {Boolean} silent (optional) True to skip notification of the owning
29853      * store of the change (defaults to false)
29854      */
29855     commit : function(silent){
29856         this.dirty = false;
29857         delete this.modified;
29858         this.editing = false;
29859         if(silent !== true){
29860             this.afterCommit();
29861         }
29862     },
29863
29864     /**
29865      * Gets a hash of only the fields that have been modified since this Record was created or commited.
29866      * @return Object
29867      */
29868     getChanges : function(){
29869         var m = this.modified, cs = {};
29870         for(var n in m){
29871             if(m.hasOwnProperty(n)){
29872                 cs[n] = this.data[n];
29873             }
29874         }
29875         return cs;
29876     },
29877
29878     // private
29879     hasError : function(){
29880         return this.error !== null;
29881     },
29882
29883     // private
29884     clearError : function(){
29885         this.error = null;
29886     },
29887
29888     /**
29889      * Creates a copy of this Record.
29890      * @param {String} id (optional) A new Record id, defaults to {@link #Record.id autogenerating an id}.
29891      * Note: if an <code>id</code> is not specified the copy created will be a
29892      * <code>{@link #phantom}</code> Record.
29893      * @return {Record}
29894      */
29895     copy : function(newId) {
29896         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
29897     },
29898
29899     /**
29900      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
29901      * since the load or last commit.
29902      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
29903      * @return {Boolean}
29904      */
29905     isModified : function(fieldName){
29906         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
29907     },
29908
29909     /**
29910      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
29911      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
29912      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
29913      * @return {Boolean}
29914      */
29915     isValid : function() {
29916         return this.fields.find(function(f) {
29917             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
29918         },this) ? false : true;
29919     },
29920
29921     /**
29922      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
29923      * is used interally when adding <code>{@link #phantom}</code> records to a
29924      * {@link Ext.data.Store#writer writer enabled store}.</p>
29925      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
29926      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
29927      * have a create action composed for it during {@link Ext.data.Store#save store save}
29928      * operations.</p>
29929      */
29930     markDirty : function(){
29931         this.dirty = true;
29932         if(!this.modified){
29933             this.modified = {};
29934         }
29935         this.fields.each(function(f) {
29936             this.modified[f.name] = this.data[f.name];
29937         },this);
29938     }
29939 };/**
29940  * @class Ext.StoreMgr
29941  * @extends Ext.util.MixedCollection
29942  * The default global group of stores.
29943  * @singleton
29944  */
29945 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
29946     /**
29947      * @cfg {Object} listeners @hide
29948      */
29949
29950     /**
29951      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
29952      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
29953      * @param {Ext.data.Store} store1 A Store instance
29954      * @param {Ext.data.Store} store2 (optional)
29955      * @param {Ext.data.Store} etc... (optional)
29956      */
29957     register : function(){
29958         for(var i = 0, s; (s = arguments[i]); i++){
29959             this.add(s);
29960         }
29961     },
29962
29963     /**
29964      * Unregisters one or more Stores with the StoreMgr
29965      * @param {String/Object} id1 The id of the Store, or a Store instance
29966      * @param {String/Object} id2 (optional)
29967      * @param {String/Object} etc... (optional)
29968      */
29969     unregister : function(){
29970         for(var i = 0, s; (s = arguments[i]); i++){
29971             this.remove(this.lookup(s));
29972         }
29973     },
29974
29975     /**
29976      * Gets a registered Store by id
29977      * @param {String/Object} id The id of the Store, or a Store instance
29978      * @return {Ext.data.Store}
29979      */
29980     lookup : function(id){
29981         if(Ext.isArray(id)){
29982             var fields = ['field1'], expand = !Ext.isArray(id[0]);
29983             if(!expand){
29984                 for(var i = 2, len = id[0].length; i <= len; ++i){
29985                     fields.push('field' + i);
29986                 }
29987             }
29988             return new Ext.data.ArrayStore({
29989                 fields: fields,
29990                 data: id,
29991                 expandData: expand,
29992                 autoDestroy: true,
29993                 autoCreated: true
29994
29995             });
29996         }
29997         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
29998     },
29999
30000     // getKey implementation for MixedCollection
30001     getKey : function(o){
30002          return o.storeId;
30003     }
30004 });/**
30005  * @class Ext.data.Store
30006  * @extends Ext.util.Observable
30007  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
30008  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
30009  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
30010  * <p><u>Retrieving Data</u></p>
30011  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
30012  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
30013  * <li>{@link #data} to automatically pass in data</li>
30014  * <li>{@link #loadData} to manually pass in data</li>
30015  * </ul></div></p>
30016  * <p><u>Reading Data</u></p>
30017  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
30018  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
30019  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
30020  * object.</p>
30021  * <p><u>Store Types</u></p>
30022  * <p>There are several implementations of Store available which are customized for use with
30023  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
30024  * creates a reader commensurate to an Array data object.</p>
30025  * <pre><code>
30026 var myStore = new Ext.data.ArrayStore({
30027     fields: ['fullname', 'first'],
30028     idIndex: 0 // id for each record will be the first element
30029 });
30030  * </code></pre>
30031  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
30032  * <pre><code>
30033 // create a {@link Ext.data.Record Record} constructor:
30034 var rt = Ext.data.Record.create([
30035     {name: 'fullname'},
30036     {name: 'first'}
30037 ]);
30038 var myStore = new Ext.data.Store({
30039     // explicitly create reader
30040     reader: new Ext.data.ArrayReader(
30041         {
30042             idIndex: 0  // id for each record will be the first element
30043         },
30044         rt // recordType
30045     )
30046 });
30047  * </code></pre>
30048  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
30049  * <pre><code>
30050 var myData = [
30051     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
30052     [2, 'Barney Rubble', 'Barney']
30053 ];
30054 myStore.loadData(myData);
30055  * </code></pre>
30056  * <p>Records are cached and made available through accessor functions.  An example of adding
30057  * a record to the store:</p>
30058  * <pre><code>
30059 var defaultData = {
30060     fullname: 'Full Name',
30061     first: 'First Name'
30062 };
30063 var recId = 100; // provide unique id for the record
30064 var r = new myStore.recordType(defaultData, ++recId); // create new record
30065 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
30066  * </code></pre>
30067  * @constructor
30068  * Creates a new Store.
30069  * @param {Object} config A config object containing the objects needed for the Store to access data,
30070  * and read the data into Records.
30071  * @xtype store
30072  */
30073 Ext.data.Store = function(config){
30074     this.data = new Ext.util.MixedCollection(false);
30075     this.data.getKey = function(o){
30076         return o.id;
30077     };
30078     /**
30079      * See the <code>{@link #baseParams corresponding configuration option}</code>
30080      * for a description of this property.
30081      * To modify this property see <code>{@link #setBaseParam}</code>.
30082      * @property
30083      */
30084     this.baseParams = {};
30085
30086     // temporary removed-records cache
30087     this.removed = [];
30088
30089     if(config && config.data){
30090         this.inlineData = config.data;
30091         delete config.data;
30092     }
30093
30094     Ext.apply(this, config);
30095     
30096     this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
30097
30098     if(this.url && !this.proxy){
30099         this.proxy = new Ext.data.HttpProxy({url: this.url});
30100     }
30101     // If Store is RESTful, so too is the DataProxy
30102     if (this.restful === true && this.proxy) {
30103         // When operating RESTfully, a unique transaction is generated for each record.
30104         this.batch = false;
30105         Ext.data.Api.restify(this.proxy);
30106     }
30107
30108     if(this.reader){ // reader passed
30109         if(!this.recordType){
30110             this.recordType = this.reader.recordType;
30111         }
30112         if(this.reader.onMetaChange){
30113             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
30114         }
30115         if (this.writer) { // writer passed
30116             this.writer.meta = this.reader.meta;
30117             this.pruneModifiedRecords = true;
30118         }
30119     }
30120
30121     /**
30122      * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
30123      * {@link Ext.data.DataReader Reader}. Read-only.
30124      * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
30125      * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
30126      * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
30127      * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
30128 // create the data store
30129 var store = new Ext.data.ArrayStore({
30130     autoDestroy: true,
30131     fields: [
30132        {name: 'company'},
30133        {name: 'price', type: 'float'},
30134        {name: 'change', type: 'float'},
30135        {name: 'pctChange', type: 'float'},
30136        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
30137     ]
30138 });
30139 store.loadData(myData);
30140
30141 // create the Grid
30142 var grid = new Ext.grid.EditorGridPanel({
30143     store: store,
30144     colModel: new Ext.grid.ColumnModel({
30145         columns: [
30146             {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
30147             {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
30148             {header: 'Change', renderer: change, dataIndex: 'change'},
30149             {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
30150             {header: 'Last Updated', width: 85,
30151                 renderer: Ext.util.Format.dateRenderer('m/d/Y'),
30152                 dataIndex: 'lastChange'}
30153         ],
30154         defaults: {
30155             sortable: true,
30156             width: 75
30157         }
30158     }),
30159     autoExpandColumn: 'company', // match the id specified in the column model
30160     height:350,
30161     width:600,
30162     title:'Array Grid',
30163     tbar: [{
30164         text: 'Add Record',
30165         handler : function(){
30166             var defaultData = {
30167                 change: 0,
30168                 company: 'New Company',
30169                 lastChange: (new Date()).clearTime(),
30170                 pctChange: 0,
30171                 price: 10
30172             };
30173             var recId = 3; // provide unique id
30174             var p = new store.recordType(defaultData, recId); // create new record
30175             grid.stopEditing();
30176             store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
30177             grid.startEditing(0, 0);
30178         }
30179     }]
30180 });
30181      * </code></pre>
30182      * @property recordType
30183      * @type Function
30184      */
30185
30186     if(this.recordType){
30187         /**
30188          * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
30189          * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
30190          * @property fields
30191          * @type Ext.util.MixedCollection
30192          */
30193         this.fields = this.recordType.prototype.fields;
30194     }
30195     this.modified = [];
30196
30197     this.addEvents(
30198         /**
30199          * @event datachanged
30200          * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
30201          * widget that is using this Store as a Record cache should refresh its view.
30202          * @param {Store} this
30203          */
30204         'datachanged',
30205         /**
30206          * @event metachange
30207          * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
30208          * @param {Store} this
30209          * @param {Object} meta The JSON metadata
30210          */
30211         'metachange',
30212         /**
30213          * @event add
30214          * Fires when Records have been {@link #add}ed to the Store
30215          * @param {Store} this
30216          * @param {Ext.data.Record[]} records The array of Records added
30217          * @param {Number} index The index at which the record(s) were added
30218          */
30219         'add',
30220         /**
30221          * @event remove
30222          * Fires when a Record has been {@link #remove}d from the Store
30223          * @param {Store} this
30224          * @param {Ext.data.Record} record The Record that was removed
30225          * @param {Number} index The index at which the record was removed
30226          */
30227         'remove',
30228         /**
30229          * @event update
30230          * Fires when a Record has been updated
30231          * @param {Store} this
30232          * @param {Ext.data.Record} record The Record that was updated
30233          * @param {String} operation The update operation being performed.  Value may be one of:
30234          * <pre><code>
30235  Ext.data.Record.EDIT
30236  Ext.data.Record.REJECT
30237  Ext.data.Record.COMMIT
30238          * </code></pre>
30239          */
30240         'update',
30241         /**
30242          * @event clear
30243          * Fires when the data cache has been cleared.
30244          * @param {Store} this
30245          */
30246         'clear',
30247         /**
30248          * @event exception
30249          * <p>Fires if an exception occurs in the Proxy during a remote request.
30250          * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
30251          * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
30252          * for additional details.
30253          * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
30254          * for description.
30255          */
30256         'exception',
30257         /**
30258          * @event beforeload
30259          * Fires before a request is made for a new data object.  If the beforeload handler returns
30260          * <tt>false</tt> the {@link #load} action will be canceled.
30261          * @param {Store} this
30262          * @param {Object} options The loading options that were specified (see {@link #load} for details)
30263          */
30264         'beforeload',
30265         /**
30266          * @event load
30267          * Fires after a new set of Records has been loaded.
30268          * @param {Store} this
30269          * @param {Ext.data.Record[]} records The Records that were loaded
30270          * @param {Object} options The loading options that were specified (see {@link #load} for details)
30271          */
30272         'load',
30273         /**
30274          * @event loadexception
30275          * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
30276          * event instead.</p>
30277          * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
30278          * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
30279          * for additional details.
30280          * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
30281          * for description.
30282          */
30283         'loadexception',
30284         /**
30285          * @event beforewrite
30286          * @param {DataProxy} this
30287          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
30288          * @param {Record/Array[Record]} rs
30289          * @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)
30290          * @param {Object} arg The callback's arg object passed to the {@link #request} function
30291          */
30292         'beforewrite',
30293         /**
30294          * @event write
30295          * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
30296          * Success or failure of the action is available in the <code>result['successProperty']</code> property.
30297          * The server-code might set the <code>successProperty</code> to <tt>false</tt> if a database validation
30298          * failed, for example.
30299          * @param {Ext.data.Store} store
30300          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
30301          * @param {Object} result The 'data' picked-out out of the response for convenience.
30302          * @param {Ext.Direct.Transaction} res
30303          * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
30304          */
30305         'write'
30306     );
30307
30308     if(this.proxy){
30309         this.relayEvents(this.proxy,  ['loadexception', 'exception']);
30310     }
30311     // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
30312     if (this.writer) {
30313         this.on({
30314             scope: this,
30315             add: this.createRecords,
30316             remove: this.destroyRecord,
30317             update: this.updateRecord
30318         });
30319     }
30320
30321     this.sortToggle = {};
30322     if(this.sortField){
30323         this.setDefaultSort(this.sortField, this.sortDir);
30324     }else if(this.sortInfo){
30325         this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
30326     }
30327
30328     Ext.data.Store.superclass.constructor.call(this);
30329
30330     if(this.id){
30331         this.storeId = this.id;
30332         delete this.id;
30333     }
30334     if(this.storeId){
30335         Ext.StoreMgr.register(this);
30336     }
30337     if(this.inlineData){
30338         this.loadData(this.inlineData);
30339         delete this.inlineData;
30340     }else if(this.autoLoad){
30341         this.load.defer(10, this, [
30342             typeof this.autoLoad == 'object' ?
30343                 this.autoLoad : undefined]);
30344     }
30345 };
30346 Ext.extend(Ext.data.Store, Ext.util.Observable, {
30347     /**
30348      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
30349      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
30350      * assignment.</p>
30351      */
30352     /**
30353      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
30354      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
30355      * Typically this option, or the <code>{@link #data}</code> option will be specified.
30356      */
30357     /**
30358      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
30359      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
30360      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
30361      * be passed to the store's {@link #load} method.
30362      */
30363     /**
30364      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
30365      * access to a data object.  See <code>{@link #url}</code>.
30366      */
30367     /**
30368      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
30369      * Typically this option, or the <code>{@link #url}</code> option will be specified.
30370      */
30371     /**
30372      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
30373      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
30374      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
30375      */
30376     /**
30377      * @cfg {Ext.data.DataWriter} writer
30378      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
30379      * to the server-side database.</p>
30380      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
30381      * events on the store are monitored in order to remotely {@link #createRecords create records},
30382      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
30383      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
30384      * <br><p>Sample implementation:
30385      * <pre><code>
30386 var writer = new {@link Ext.data.JsonWriter}({
30387     encode: true,
30388     writeAllFields: true // write all fields, not just those that changed
30389 });
30390
30391 // Typical Store collecting the Proxy, Reader and Writer together.
30392 var store = new Ext.data.Store({
30393     storeId: 'user',
30394     root: 'records',
30395     proxy: proxy,
30396     reader: reader,
30397     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
30398     paramsAsHash: true,
30399     autoSave: false    // <-- false to delay executing create, update, destroy requests
30400                         //     until specifically told to do so.
30401 });
30402      * </code></pre></p>
30403      */
30404     writer : undefined,
30405     /**
30406      * @cfg {Object} baseParams
30407      * <p>An object containing properties which are to be sent as parameters
30408      * for <i>every</i> HTTP request.</p>
30409      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
30410      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
30411      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
30412      * for more details.</p>
30413      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
30414      * method.
30415      * @property
30416      */
30417     /**
30418      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
30419      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
30420      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
30421      * For example:<pre><code>
30422 sortInfo: {
30423     field: 'fieldName',
30424     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
30425 }
30426 </code></pre>
30427      */
30428     /**
30429      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
30430      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
30431      * in place (defaults to <tt>false</tt>).
30432      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
30433      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
30434      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
30435      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
30436      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
30437      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
30438      * </ul></div></p>
30439      */
30440     remoteSort : false,
30441
30442     /**
30443      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
30444      * to is destroyed (defaults to <tt>false</tt>).
30445      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
30446      */
30447     autoDestroy : false,
30448
30449     /**
30450      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
30451      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
30452      * for the accessor method to retrieve the modified records.
30453      */
30454     pruneModifiedRecords : false,
30455
30456     /**
30457      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
30458      * for the details of what this may contain. This may be useful for accessing any params which were used
30459      * to load the current Record cache.
30460      * @property
30461      */
30462     lastOptions : null,
30463
30464     /**
30465      * @cfg {Boolean} autoSave
30466      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
30467      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
30468      * to send all modifiedRecords to the server.</p>
30469      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
30470      */
30471     autoSave : true,
30472
30473     /**
30474      * @cfg {Boolean} batch
30475      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
30476      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
30477      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
30478      * to <tt>false</tt>.</p>
30479      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
30480      * generated for each record.</p>
30481      */
30482     batch : true,
30483
30484     /**
30485      * @cfg {Boolean} restful
30486      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
30487      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
30488      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
30489      * action is described in {@link Ext.data.Api#restActions}.  For additional information
30490      * see {@link Ext.data.DataProxy#restful}.
30491      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
30492      * internally be set to <tt>false</tt>.</p>
30493      */
30494     restful: false,
30495     
30496     /**
30497      * @cfg {Object} paramNames
30498      * <p>An object containing properties which specify the names of the paging and
30499      * sorting parameters passed to remote servers when loading blocks of data. By default, this
30500      * object takes the following form:</p><pre><code>
30501 {
30502     start : 'start',  // The parameter name which specifies the start row
30503     limit : 'limit',  // The parameter name which specifies number of rows to return
30504     sort : 'sort',    // The parameter name which specifies the column to sort on
30505     dir : 'dir'       // The parameter name which specifies the sort direction
30506 }
30507 </code></pre>
30508      * <p>The server must produce the requested data block upon receipt of these parameter names.
30509      * If different parameter names are required, this property can be overriden using a configuration
30510      * property.</p>
30511      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
30512      * the parameter names to use in its {@link #load requests}.
30513      */
30514     paramNames : undefined,
30515     
30516     /**
30517      * @cfg {Object} defaultParamNames
30518      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
30519      * for all stores, this object should be changed on the store prototype.
30520      */
30521     defaultParamNames : {
30522         start : 'start',
30523         limit : 'limit',
30524         sort : 'sort',
30525         dir : 'dir'
30526     },
30527
30528     /**
30529      * Destroys the store.
30530      */
30531     destroy : function(){
30532         if(this.storeId){
30533             Ext.StoreMgr.unregister(this);
30534         }
30535         this.data = null;
30536         Ext.destroy(this.proxy);
30537         this.reader = this.writer = null;
30538         this.purgeListeners();
30539     },
30540
30541     /**
30542      * Add Records to the Store and fires the {@link #add} event.  To add Records
30543      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
30544      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
30545      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
30546      * to add to the cache. See {@link #recordType}.
30547      */
30548     add : function(records){
30549         records = [].concat(records);
30550         if(records.length < 1){
30551             return;
30552         }
30553         for(var i = 0, len = records.length; i < len; i++){
30554             records[i].join(this);
30555         }
30556         var index = this.data.length;
30557         this.data.addAll(records);
30558         if(this.snapshot){
30559             this.snapshot.addAll(records);
30560         }
30561         this.fireEvent('add', this, records, index);
30562     },
30563
30564     /**
30565      * (Local sort only) Inserts the passed Record into the Store at the index where it
30566      * should go based on the current sort information.
30567      * @param {Ext.data.Record} record
30568      */
30569     addSorted : function(record){
30570         var index = this.findInsertIndex(record);
30571         this.insert(index, record);
30572     },
30573
30574     /**
30575      * Remove a Record from the Store and fires the {@link #remove} event.
30576      * @param {Ext.data.Record} record The Ext.data.Record object to remove from the cache.
30577      */
30578     remove : function(record){
30579         var index = this.data.indexOf(record);
30580         if(index > -1){
30581             this.data.removeAt(index);
30582             if(this.pruneModifiedRecords){
30583                 this.modified.remove(record);
30584             }
30585             if(this.snapshot){
30586                 this.snapshot.remove(record);
30587             }
30588             this.fireEvent('remove', this, record, index);
30589         }
30590     },
30591
30592     /**
30593      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
30594      * @param {Number} index The index of the record to remove.
30595      */
30596     removeAt : function(index){
30597         this.remove(this.getAt(index));
30598     },
30599
30600     /**
30601      * Remove all Records from the Store and fires the {@link #clear} event.
30602      */
30603     removeAll : function(){
30604         this.data.clear();
30605         if(this.snapshot){
30606             this.snapshot.clear();
30607         }
30608         if(this.pruneModifiedRecords){
30609             this.modified = [];
30610         }
30611         this.fireEvent('clear', this);
30612     },
30613
30614     /**
30615      * Inserts Records into the Store at the given index and fires the {@link #add} event.
30616      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
30617      * @param {Number} index The start index at which to insert the passed Records.
30618      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
30619      */
30620     insert : function(index, records){
30621         records = [].concat(records);
30622         for(var i = 0, len = records.length; i < len; i++){
30623             this.data.insert(index, records[i]);
30624             records[i].join(this);
30625         }
30626         this.fireEvent('add', this, records, index);
30627     },
30628
30629     /**
30630      * Get the index within the cache of the passed Record.
30631      * @param {Ext.data.Record} record The Ext.data.Record object to find.
30632      * @return {Number} The index of the passed Record. Returns -1 if not found.
30633      */
30634     indexOf : function(record){
30635         return this.data.indexOf(record);
30636     },
30637
30638     /**
30639      * Get the index within the cache of the Record with the passed id.
30640      * @param {String} id The id of the Record to find.
30641      * @return {Number} The index of the Record. Returns -1 if not found.
30642      */
30643     indexOfId : function(id){
30644         return this.data.indexOfKey(id);
30645     },
30646
30647     /**
30648      * Get the Record with the specified id.
30649      * @param {String} id The id of the Record to find.
30650      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
30651      */
30652     getById : function(id){
30653         return this.data.key(id);
30654     },
30655
30656     /**
30657      * Get the Record at the specified index.
30658      * @param {Number} index The index of the Record to find.
30659      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
30660      */
30661     getAt : function(index){
30662         return this.data.itemAt(index);
30663     },
30664
30665     /**
30666      * Returns a range of Records between specified indices.
30667      * @param {Number} startIndex (optional) The starting index (defaults to 0)
30668      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
30669      * @return {Ext.data.Record[]} An array of Records
30670      */
30671     getRange : function(start, end){
30672         return this.data.getRange(start, end);
30673     },
30674
30675     // private
30676     storeOptions : function(o){
30677         o = Ext.apply({}, o);
30678         delete o.callback;
30679         delete o.scope;
30680         this.lastOptions = o;
30681     },
30682
30683     /**
30684      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
30685      * <br><p>Notes:</p><div class="mdetail-params"><ul>
30686      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
30687      * loaded. To perform any post-processing where information from the load call is required, specify
30688      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
30689      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
30690      * properties in the <code>options.params</code> property to establish the initial position within the
30691      * dataset, and the number of Records to cache on each read from the Proxy.</li>
30692      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
30693      * will be automatically included with the posted parameters according to the specified
30694      * <code>{@link #paramNames}</code>.</li>
30695      * </ul></div>
30696      * @param {Object} options An object containing properties which control loading options:<ul>
30697      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
30698      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
30699      * <code>{@link #baseParams}</code> of the same name.</p>
30700      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
30701      * <li><b><tt>callback</tt></b> : Function<div class="sub-desc"><p>A function to be called after the Records
30702      * have been loaded. The <tt>callback</tt> is called after the load event and is passed the following arguments:<ul>
30703      * <li><tt>r</tt> : Ext.data.Record[]</li>
30704      * <li><tt>options</tt>: Options object from the load call</li>
30705      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div></li>
30706      * <li><b><tt>scope</tt></b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
30707      * to the Store object)</p></div></li>
30708      * <li><b><tt>add</tt></b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
30709      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
30710      * </ul>
30711      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
30712      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
30713      */
30714     load : function(options) {
30715         options = options || {};
30716         this.storeOptions(options);
30717         if(this.sortInfo && this.remoteSort){
30718             var pn = this.paramNames;
30719             options.params = options.params || {};
30720             options.params[pn.sort] = this.sortInfo.field;
30721             options.params[pn.dir] = this.sortInfo.direction;
30722         }
30723         try {
30724             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
30725         } catch(e) {
30726             this.handleException(e);
30727             return false;
30728         }
30729     },
30730
30731     /**
30732      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
30733      * Listens to 'update' event.
30734      * @param {Object} store
30735      * @param {Object} record
30736      * @param {Object} action
30737      * @private
30738      */
30739     updateRecord : function(store, record, action) {
30740         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid))) {
30741             this.save();
30742         }
30743     },
30744
30745     /**
30746      * Should not be used directly.  Store#add will call this automatically if a Writer is set
30747      * @param {Object} store
30748      * @param {Object} rs
30749      * @param {Object} index
30750      * @private
30751      */
30752     createRecords : function(store, rs, index) {
30753         for (var i = 0, len = rs.length; i < len; i++) {
30754             if (rs[i].phantom && rs[i].isValid()) {
30755                 rs[i].markDirty();  // <-- Mark new records dirty
30756                 this.modified.push(rs[i]);  // <-- add to modified
30757             }
30758         }
30759         if (this.autoSave === true) {
30760             this.save();
30761         }
30762     },
30763
30764     /**
30765      * Destroys a record or records.  Should not be used directly.  It's called by Store#remove if a Writer is set.
30766      * @param {Store} this
30767      * @param {Ext.data.Record/Ext.data.Record[]}
30768      * @param {Number} index
30769      * @private
30770      */
30771     destroyRecord : function(store, record, index) {
30772         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
30773             this.modified.remove(record);
30774         }
30775         if (!record.phantom) {
30776             this.removed.push(record);
30777
30778             // since the record has already been removed from the store but the server request has not yet been executed,
30779             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
30780             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
30781             record.lastIndex = index;
30782
30783             if (this.autoSave === true) {
30784                 this.save();
30785             }
30786         }
30787     },
30788
30789     /**
30790      * This method should generally not be used directly.  This method is called internally
30791      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
30792      * {@link #remove}, or {@link #update} events fire.
30793      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
30794      * @param {Record/Record[]} rs
30795      * @param {Object} options
30796      * @throws Error
30797      * @private
30798      */
30799     execute : function(action, rs, options) {
30800         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
30801         if (!Ext.data.Api.isAction(action)) {
30802             throw new Ext.data.Api.Error('execute', action);
30803         }
30804         // make sure options has a params key
30805         options = Ext.applyIf(options||{}, {
30806             params: {}
30807         });
30808
30809         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
30810         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
30811         var doRequest = true;
30812
30813         if (action === 'read') {
30814             doRequest = this.fireEvent('beforeload', this, options);
30815         }
30816         else {
30817             // if Writer is configured as listful, force single-recoord rs to be [{}} instead of {}
30818             if (this.writer.listful === true && this.restful !== true) {
30819                 rs = (Ext.isArray(rs)) ? rs : [rs];
30820             }
30821             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
30822             else if (Ext.isArray(rs) && rs.length == 1) {
30823                 rs = rs.shift();
30824             }
30825             // Write the action to options.params
30826             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
30827                 this.writer.write(action, options.params, rs);
30828             }
30829         }
30830         if (doRequest !== false) {
30831             // Send request to proxy.
30832             var params = Ext.apply({}, options.params, this.baseParams);
30833             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
30834                 params.xaction = action;
30835             }
30836             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.  We'll flip it now
30837             // and send the value into DataProxy#request, since it's the value which maps to the DataProxy#api
30838             this.proxy.request(Ext.data.Api.actions[action], rs, params, this.reader, this.createCallback(action, rs), this, options);
30839         }
30840         return doRequest;
30841     },
30842
30843     /**
30844      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
30845      * the configured <code>{@link #url}</code> will be used.
30846      * <pre>
30847      * change            url
30848      * ---------------   --------------------
30849      * removed records   Ext.data.Api.actions.destroy
30850      * phantom records   Ext.data.Api.actions.create
30851      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
30852      * </pre>
30853      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
30854      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
30855      */
30856     save : function() {
30857         if (!this.writer) {
30858             throw new Ext.data.Store.Error('writer-undefined');
30859         }
30860
30861         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
30862         if (this.removed.length) {
30863             this.doTransaction('destroy', this.removed);
30864         }
30865
30866         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
30867         var rs = [].concat(this.getModifiedRecords());
30868         if (!rs.length) { // Bail-out if empty...
30869             return true;
30870         }
30871
30872         // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
30873         var phantoms = [];
30874         for (var i = rs.length-1; i >= 0; i--) {
30875             if (rs[i].phantom === true) {
30876                 var rec = rs.splice(i, 1).shift();
30877                 if (rec.isValid()) {
30878                     phantoms.push(rec);
30879                 }
30880             } else if (!rs[i].isValid()) { // <-- while we're here, splice-off any !isValid real records
30881                 rs.splice(i,1);
30882             }
30883         }
30884         // If we have valid phantoms, create them...
30885         if (phantoms.length) {
30886             this.doTransaction('create', phantoms);
30887         }
30888
30889         // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
30890         if (rs.length) {
30891             this.doTransaction('update', rs);
30892         }
30893         return true;
30894     },
30895
30896     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
30897     doTransaction : function(action, rs) {
30898         function transaction(records) {
30899             try {
30900                 this.execute(action, records);
30901             } catch (e) {
30902                 this.handleException(e);
30903             }
30904         }
30905         if (this.batch === false) {
30906             for (var i = 0, len = rs.length; i < len; i++) {
30907                 transaction.call(this, rs[i]);
30908             }
30909         } else {
30910             transaction.call(this, rs);
30911         }
30912     },
30913
30914     // @private callback-handler for remote CRUD actions
30915     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
30916     createCallback : function(action, rs) {
30917         var actions = Ext.data.Api.actions;
30918         return (action == 'read') ? this.loadRecords : function(data, response, success) {
30919             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
30920             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, data);
30921             // If success === false here, exception will have been called in DataProxy
30922             if (success === true) {
30923                 this.fireEvent('write', this, action, data, response, rs);
30924             }
30925         };
30926     },
30927
30928     // Clears records from modified array after an exception event.
30929     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
30930     clearModified : function(rs) {
30931         if (Ext.isArray(rs)) {
30932             for (var n=rs.length-1;n>=0;n--) {
30933                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
30934             }
30935         } else {
30936             this.modified.splice(this.modified.indexOf(rs), 1);
30937         }
30938     },
30939
30940     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
30941     reMap : function(record) {
30942         if (Ext.isArray(record)) {
30943             for (var i = 0, len = record.length; i < len; i++) {
30944                 this.reMap(record[i]);
30945             }
30946         } else {
30947             delete this.data.map[record._phid];
30948             this.data.map[record.id] = record;
30949             var index = this.data.keys.indexOf(record._phid);
30950             this.data.keys.splice(index, 1, record.id);
30951             delete record._phid;
30952         }
30953     },
30954
30955     // @protected onCreateRecord proxy callback for create action
30956     onCreateRecords : function(success, rs, data) {
30957         if (success === true) {
30958             try {
30959                 this.reader.realize(rs, data);
30960                 this.reMap(rs);
30961             }
30962             catch (e) {
30963                 this.handleException(e);
30964                 if (Ext.isArray(rs)) {
30965                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
30966                     this.onCreateRecords(success, rs, data);
30967                 }
30968             }
30969         }
30970     },
30971
30972     // @protected, onUpdateRecords proxy callback for update action
30973     onUpdateRecords : function(success, rs, data) {
30974         if (success === true) {
30975             try {
30976                 this.reader.update(rs, data);
30977             } catch (e) {
30978                 this.handleException(e);
30979                 if (Ext.isArray(rs)) {
30980                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
30981                     this.onUpdateRecords(success, rs, data);
30982                 }
30983             }
30984         }
30985     },
30986
30987     // @protected onDestroyRecords proxy callback for destroy action
30988     onDestroyRecords : function(success, rs, data) {
30989         // splice each rec out of this.removed
30990         rs = (rs instanceof Ext.data.Record) ? [rs] : rs;
30991         for (var i=0,len=rs.length;i<len;i++) {
30992             this.removed.splice(this.removed.indexOf(rs[i]), 1);
30993         }
30994         if (success === false) {
30995             // put records back into store if remote destroy fails.
30996             // @TODO: Might want to let developer decide.
30997             for (i=rs.length-1;i>=0;i--) {
30998                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
30999             }
31000         }
31001     },
31002
31003     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
31004     handleException : function(e) {
31005         // @see core/Error.js
31006         Ext.handleError(e);
31007     },
31008
31009     /**
31010      * <p>Reloads the Record cache from the configured Proxy using the configured {@link Ext.data.Reader Reader} and
31011      * the options from the last load operation performed.</p>
31012      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
31013      * @param {Object} options (optional) An <tt>Object</tt> containing {@link #load loading options} which may
31014      * override the options used in the last {@link #load} operation. See {@link #load} for details (defaults to
31015      * <tt>null</tt>, in which case the {@link #lastOptions} are used).
31016      */
31017     reload : function(options){
31018         this.load(Ext.applyIf(options||{}, this.lastOptions));
31019     },
31020
31021     // private
31022     // Called as a callback by the Reader during a load operation.
31023     loadRecords : function(o, options, success){
31024         if(!o || success === false){
31025             if(success !== false){
31026                 this.fireEvent('load', this, [], options);
31027             }
31028             if(options.callback){
31029                 options.callback.call(options.scope || this, [], options, false, o);
31030             }
31031             return;
31032         }
31033         var r = o.records, t = o.totalRecords || r.length;
31034         if(!options || options.add !== true){
31035             if(this.pruneModifiedRecords){
31036                 this.modified = [];
31037             }
31038             for(var i = 0, len = r.length; i < len; i++){
31039                 r[i].join(this);
31040             }
31041             if(this.snapshot){
31042                 this.data = this.snapshot;
31043                 delete this.snapshot;
31044             }
31045             this.data.clear();
31046             this.data.addAll(r);
31047             this.totalLength = t;
31048             this.applySort();
31049             this.fireEvent('datachanged', this);
31050         }else{
31051             this.totalLength = Math.max(t, this.data.length+r.length);
31052             this.add(r);
31053         }
31054         this.fireEvent('load', this, r, options);
31055         if(options.callback){
31056             options.callback.call(options.scope || this, r, options, true);
31057         }
31058     },
31059
31060     /**
31061      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
31062      * which understands the format of the data must have been configured in the constructor.
31063      * @param {Object} data The data block from which to read the Records.  The format of the data expected
31064      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
31065      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
31066      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
31067      * the existing cache.
31068      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
31069      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
31070      * new, unique ids will be added.
31071      */
31072     loadData : function(o, append){
31073         var r = this.reader.readRecords(o);
31074         this.loadRecords(r, {add: append}, true);
31075     },
31076
31077     /**
31078      * Gets the number of cached records.
31079      * <p>If using paging, this may not be the total size of the dataset. If the data object
31080      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
31081      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
31082      * @return {Number} The number of Records in the Store's cache.
31083      */
31084     getCount : function(){
31085         return this.data.length || 0;
31086     },
31087
31088     /**
31089      * Gets the total number of records in the dataset as returned by the server.
31090      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
31091      * must contain the dataset size. For remote data sources, the value for this property
31092      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
31093      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
31094      * <b>Note</b>: see the Important note in {@link #load}.</p>
31095      * @return {Number} The number of Records as specified in the data object passed to the Reader
31096      * by the Proxy.
31097      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
31098      */
31099     getTotalCount : function(){
31100         return this.totalLength || 0;
31101     },
31102
31103     /**
31104      * Returns an object describing the current sort state of this Store.
31105      * @return {Object} The sort state of the Store. An object with two properties:<ul>
31106      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
31107      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
31108      * </ul>
31109      * See <tt>{@link #sortInfo}</tt> for additional details.
31110      */
31111     getSortState : function(){
31112         return this.sortInfo;
31113     },
31114
31115     // private
31116     applySort : function(){
31117         if(this.sortInfo && !this.remoteSort){
31118             var s = this.sortInfo, f = s.field;
31119             this.sortData(f, s.direction);
31120         }
31121     },
31122
31123     // private
31124     sortData : function(f, direction){
31125         direction = direction || 'ASC';
31126         var st = this.fields.get(f).sortType;
31127         var fn = function(r1, r2){
31128             var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
31129             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
31130         };
31131         this.data.sort(direction, fn);
31132         if(this.snapshot && this.snapshot != this.data){
31133             this.snapshot.sort(direction, fn);
31134         }
31135     },
31136
31137     /**
31138      * Sets the default sort column and order to be used by the next {@link #load} operation.
31139      * @param {String} fieldName The name of the field to sort by.
31140      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
31141      */
31142     setDefaultSort : function(field, dir){
31143         dir = dir ? dir.toUpperCase() : 'ASC';
31144         this.sortInfo = {field: field, direction: dir};
31145         this.sortToggle[field] = dir;
31146     },
31147
31148     /**
31149      * Sort the Records.
31150      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
31151      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
31152      * @param {String} fieldName The name of the field to sort by.
31153      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
31154      */
31155     sort : function(fieldName, dir){
31156         var f = this.fields.get(fieldName);
31157         if(!f){
31158             return false;
31159         }
31160         if(!dir){
31161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
31162                 dir = (this.sortToggle[f.name] || 'ASC').toggle('ASC', 'DESC');
31163             }else{
31164                 dir = f.sortDir;
31165             }
31166         }
31167         var st = (this.sortToggle) ? this.sortToggle[f.name] : null;
31168         var si = (this.sortInfo) ? this.sortInfo : null;
31169
31170         this.sortToggle[f.name] = dir;
31171         this.sortInfo = {field: f.name, direction: dir};
31172         if(!this.remoteSort){
31173             this.applySort();
31174             this.fireEvent('datachanged', this);
31175         }else{
31176             if (!this.load(this.lastOptions)) {
31177                 if (st) {
31178                     this.sortToggle[f.name] = st;
31179                 }
31180                 if (si) {
31181                     this.sortInfo = si;
31182                 }
31183             }
31184         }
31185     },
31186
31187     /**
31188      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
31189      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
31190      * Returning <tt>false</tt> aborts and exits the iteration.
31191      * @param {Object} scope (optional) The scope in which to call the function (defaults to the {@link Ext.data.Record Record}).
31192      */
31193     each : function(fn, scope){
31194         this.data.each(fn, scope);
31195     },
31196
31197     /**
31198      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
31199      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
31200      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
31201      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
31202      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
31203      * modifications.  To obtain modified fields within a modified record see
31204      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
31205      */
31206     getModifiedRecords : function(){
31207         return this.modified;
31208     },
31209
31210     // private
31211     createFilterFn : function(property, value, anyMatch, caseSensitive){
31212         if(Ext.isEmpty(value, false)){
31213             return false;
31214         }
31215         value = this.data.createValueMatcher(value, anyMatch, caseSensitive);
31216         return function(r){
31217             return value.test(r.data[property]);
31218         };
31219     },
31220
31221     /**
31222      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
31223      * and <tt>end</tt> and returns the result.
31224      * @param {String} property A field in each record
31225      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
31226      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
31227      * @return {Number} The sum
31228      */
31229     sum : function(property, start, end){
31230         var rs = this.data.items, v = 0;
31231         start = start || 0;
31232         end = (end || end === 0) ? end : rs.length-1;
31233
31234         for(var i = start; i <= end; i++){
31235             v += (rs[i].data[property] || 0);
31236         }
31237         return v;
31238     },
31239
31240     /**
31241      * Filter the {@link Ext.data.Record records} by a specified property.
31242      * @param {String} field A field on your records
31243      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
31244      * against the field.
31245      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
31246      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
31247      */
31248     filter : function(property, value, anyMatch, caseSensitive){
31249         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
31250         return fn ? this.filterBy(fn) : this.clearFilter();
31251     },
31252
31253     /**
31254      * Filter by a function. The specified function will be called for each
31255      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
31256      * otherwise it is filtered out.
31257      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
31258      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
31259      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
31260      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
31261      * </ul>
31262      * @param {Object} scope (optional) The scope of the function (defaults to this)
31263      */
31264     filterBy : function(fn, scope){
31265         this.snapshot = this.snapshot || this.data;
31266         this.data = this.queryBy(fn, scope||this);
31267         this.fireEvent('datachanged', this);
31268     },
31269
31270     /**
31271      * Query the records by a specified property.
31272      * @param {String} field A field on your records
31273      * @param {String/RegExp} value Either a string that the field
31274      * should begin with, or a RegExp to test against the field.
31275      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
31276      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
31277      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
31278      */
31279     query : function(property, value, anyMatch, caseSensitive){
31280         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
31281         return fn ? this.queryBy(fn) : this.data.clone();
31282     },
31283
31284     /**
31285      * Query the cached records in this Store using a filtering function. The specified function
31286      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
31287      * included in the results.
31288      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
31289      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
31290      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
31291      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
31292      * </ul>
31293      * @param {Object} scope (optional) The scope of the function (defaults to this)
31294      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
31295      **/
31296     queryBy : function(fn, scope){
31297         var data = this.snapshot || this.data;
31298         return data.filterBy(fn, scope||this);
31299     },
31300
31301     /**
31302      * Finds the index of the first matching record in this store by a specific property/value.
31303      * @param {String} property A property on your objects
31304      * @param {String/RegExp} value Either a string that the property value
31305      * should begin with, or a RegExp to test against the property.
31306      * @param {Number} startIndex (optional) The index to start searching at
31307      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
31308      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
31309      * @return {Number} The matched index or -1
31310      */
31311     find : function(property, value, start, anyMatch, caseSensitive){
31312         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
31313         return fn ? this.data.findIndexBy(fn, null, start) : -1;
31314     },
31315
31316     /**
31317      * Finds the index of the first matching record in this store by a specific property/value.
31318      * @param {String} property A property on your objects
31319      * @param {String/RegExp} value The value to match against
31320      * @param {Number} startIndex (optional) The index to start searching at
31321      * @return {Number} The matched index or -1
31322      */
31323     findExact: function(property, value, start){
31324         return this.data.findIndexBy(function(rec){
31325             return rec.get(property) === value;
31326         }, this, start);
31327     },
31328
31329     /**
31330      * Find the index of the first matching Record in this Store by a function.
31331      * If the function returns <tt>true</tt> it is considered a match.
31332      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
31333      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
31334      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
31335      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
31336      * </ul>
31337      * @param {Object} scope (optional) The scope of the function (defaults to this)
31338      * @param {Number} startIndex (optional) The index to start searching at
31339      * @return {Number} The matched index or -1
31340      */
31341     findBy : function(fn, scope, start){
31342         return this.data.findIndexBy(fn, scope, start);
31343     },
31344
31345     /**
31346      * Collects unique values for a particular dataIndex from this store.
31347      * @param {String} dataIndex The property to collect
31348      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
31349      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
31350      * @return {Array} An array of the unique values
31351      **/
31352     collect : function(dataIndex, allowNull, bypassFilter){
31353         var d = (bypassFilter === true && this.snapshot) ?
31354                 this.snapshot.items : this.data.items;
31355         var v, sv, r = [], l = {};
31356         for(var i = 0, len = d.length; i < len; i++){
31357             v = d[i].data[dataIndex];
31358             sv = String(v);
31359             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
31360                 l[sv] = true;
31361                 r[r.length] = v;
31362             }
31363         }
31364         return r;
31365     },
31366
31367     /**
31368      * Revert to a view of the Record cache with no filtering applied.
31369      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
31370      * {@link #datachanged} event.
31371      */
31372     clearFilter : function(suppressEvent){
31373         if(this.isFiltered()){
31374             this.data = this.snapshot;
31375             delete this.snapshot;
31376             if(suppressEvent !== true){
31377                 this.fireEvent('datachanged', this);
31378             }
31379         }
31380     },
31381
31382     /**
31383      * Returns true if this store is currently filtered
31384      * @return {Boolean}
31385      */
31386     isFiltered : function(){
31387         return this.snapshot && this.snapshot != this.data;
31388     },
31389
31390     // private
31391     afterEdit : function(record){
31392         if(this.modified.indexOf(record) == -1){
31393             this.modified.push(record);
31394         }
31395         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
31396     },
31397
31398     // private
31399     afterReject : function(record){
31400         this.modified.remove(record);
31401         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
31402     },
31403
31404     // private
31405     afterCommit : function(record){
31406         this.modified.remove(record);
31407         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
31408     },
31409
31410     /**
31411      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
31412      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
31413      * Ext.data.Record.COMMIT.
31414      */
31415     commitChanges : function(){
31416         var m = this.modified.slice(0);
31417         this.modified = [];
31418         for(var i = 0, len = m.length; i < len; i++){
31419             m[i].commit();
31420         }
31421     },
31422
31423     /**
31424      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
31425      */
31426     rejectChanges : function(){
31427         var m = this.modified.slice(0);
31428         this.modified = [];
31429         for(var i = 0, len = m.length; i < len; i++){
31430             m[i].reject();
31431         }
31432     },
31433
31434     // private
31435     onMetaChange : function(meta, rtype, o){
31436         this.recordType = rtype;
31437         this.fields = rtype.prototype.fields;
31438         delete this.snapshot;
31439         if(meta.sortInfo){
31440             this.sortInfo = meta.sortInfo;
31441         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
31442             delete this.sortInfo;
31443         }
31444         this.modified = [];
31445         this.fireEvent('metachange', this, this.reader.meta);
31446     },
31447
31448     // private
31449     findInsertIndex : function(record){
31450         this.suspendEvents();
31451         var data = this.data.clone();
31452         this.data.add(record);
31453         this.applySort();
31454         var index = this.data.indexOf(record);
31455         this.data = data;
31456         this.resumeEvents();
31457         return index;
31458     },
31459
31460     /**
31461      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
31462 myStore.setBaseParam('foo', {bar:3});
31463 </code></pre>
31464      * @param {String} name Name of the property to assign
31465      * @param {Mixed} value Value to assign the <tt>name</tt>d property
31466      **/
31467     setBaseParam : function (name, value){
31468         this.baseParams = this.baseParams || {};
31469         this.baseParams[name] = value;
31470     }
31471 });
31472
31473 Ext.reg('store', Ext.data.Store);
31474
31475 /**
31476  * @class Ext.data.Store.Error
31477  * @extends Ext.Error
31478  * Store Error extension.
31479  * @param {String} name
31480  */
31481 Ext.data.Store.Error = Ext.extend(Ext.Error, {
31482     name: 'Ext.data.Store'
31483 });
31484 Ext.apply(Ext.data.Store.Error.prototype, {
31485     lang: {
31486         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
31487     }
31488 });
31489
31490 /**
31491  * @class Ext.data.Field
31492  * <p>This class encapsulates the field definition information specified in the field definition objects
31493  * passed to {@link Ext.data.Record#create}.</p>
31494  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
31495  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
31496  */
31497 Ext.data.Field = function(config){
31498     if(typeof config == "string"){
31499         config = {name: config};
31500     }
31501     Ext.apply(this, config);
31502
31503     if(!this.type){
31504         this.type = "auto";
31505     }
31506
31507     var st = Ext.data.SortTypes;
31508     // named sortTypes are supported, here we look them up
31509     if(typeof this.sortType == "string"){
31510         this.sortType = st[this.sortType];
31511     }
31512
31513     // set default sortType for strings and dates
31514     if(!this.sortType){
31515         switch(this.type){
31516             case "string":
31517                 this.sortType = st.asUCString;
31518                 break;
31519             case "date":
31520                 this.sortType = st.asDate;
31521                 break;
31522             default:
31523                 this.sortType = st.none;
31524         }
31525     }
31526
31527     // define once
31528     var stripRe = /[\$,%]/g;
31529
31530     // prebuilt conversion function for this field, instead of
31531     // switching every time we're reading a value
31532     if(!this.convert){
31533         var cv, dateFormat = this.dateFormat;
31534         switch(this.type){
31535             case "":
31536             case "auto":
31537             case undefined:
31538                 cv = function(v){ return v; };
31539                 break;
31540             case "string":
31541                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
31542                 break;
31543             case "int":
31544                 cv = function(v){
31545                     return v !== undefined && v !== null && v !== '' ?
31546                            parseInt(String(v).replace(stripRe, ""), 10) : '';
31547                     };
31548                 break;
31549             case "float":
31550                 cv = function(v){
31551                     return v !== undefined && v !== null && v !== '' ?
31552                            parseFloat(String(v).replace(stripRe, ""), 10) : '';
31553                     };
31554                 break;
31555             case "bool":
31556             case "boolean":
31557                 cv = function(v){ return v === true || v === "true" || v == 1; };
31558                 break;
31559             case "date":
31560                 cv = function(v){
31561                     if(!v){
31562                         return '';
31563                     }
31564                     if(Ext.isDate(v)){
31565                         return v;
31566                     }
31567                     if(dateFormat){
31568                         if(dateFormat == "timestamp"){
31569                             return new Date(v*1000);
31570                         }
31571                         if(dateFormat == "time"){
31572                             return new Date(parseInt(v, 10));
31573                         }
31574                         return Date.parseDate(v, dateFormat);
31575                     }
31576                     var parsed = Date.parse(v);
31577                     return parsed ? new Date(parsed) : null;
31578                 };
31579              break;
31580
31581         }
31582         this.convert = cv;
31583     }
31584 };
31585
31586 Ext.data.Field.prototype = {
31587     /**
31588      * @cfg {String} name
31589      * The name by which the field is referenced within the Record. This is referenced by, for example,
31590      * the <tt>dataIndex</tt> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
31591      * <p>Note: In the simplest case, if no properties other than <tt>name</tt> are required, a field
31592      * definition may consist of just a String for the field name.</p>
31593      */
31594     /**
31595      * @cfg {String} type
31596      * (Optional) The data type for conversion to displayable value if <tt>{@link Ext.data.Field#convert convert}</tt>
31597      * has not been specified. Possible values are
31598      * <div class="mdetail-params"><ul>
31599      * <li>auto (Default, implies no conversion)</li>
31600      * <li>string</li>
31601      * <li>int</li>
31602      * <li>float</li>
31603      * <li>boolean</li>
31604      * <li>date</li></ul></div>
31605      */
31606     /**
31607      * @cfg {Function} convert
31608      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
31609      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
31610      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
31611      * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
31612      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
31613      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
31614      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
31615      * </ul></div>
31616      * <pre><code>
31617 // example of convert function
31618 function fullName(v, record){
31619     return record.name.last + ', ' + record.name.first;
31620 }
31621
31622 function location(v, record){
31623     return !record.city ? '' : (record.city + ', ' + record.state);
31624 }
31625
31626 var Dude = Ext.data.Record.create([
31627     {name: 'fullname',  convert: fullName},
31628     {name: 'firstname', mapping: 'name.first'},
31629     {name: 'lastname',  mapping: 'name.last'},
31630     {name: 'city', defaultValue: 'homeless'},
31631     'state',
31632     {name: 'location',  convert: location}
31633 ]);
31634
31635 // create the data store
31636 var store = new Ext.data.Store({
31637     reader: new Ext.data.JsonReader(
31638         {
31639             idProperty: 'key',
31640             root: 'daRoot',  
31641             totalProperty: 'total'
31642         },
31643         Dude  // recordType
31644     )
31645 });
31646
31647 var myData = [
31648     { key: 1,
31649       name: { first: 'Fat',    last:  'Albert' }
31650       // notice no city, state provided in data object
31651     },
31652     { key: 2,
31653       name: { first: 'Barney', last:  'Rubble' },
31654       city: 'Bedrock', state: 'Stoneridge'
31655     },
31656     { key: 3,
31657       name: { first: 'Cliff',  last:  'Claven' },
31658       city: 'Boston',  state: 'MA'
31659     }
31660 ];
31661      * </code></pre>
31662      */
31663     /**
31664      * @cfg {String} dateFormat
31665      * (Optional) A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
31666      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
31667      * javascript millisecond timestamp.
31668      */
31669     dateFormat: null,
31670     /**
31671      * @cfg {Mixed} defaultValue
31672      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
31673      * when the item referenced by the <tt>{@link Ext.data.Field#mapping mapping}</tt> does not exist in the data
31674      * object (i.e. undefined). (defaults to "")
31675      */
31676     defaultValue: "",
31677     /**
31678      * @cfg {String/Number} mapping
31679      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
31680      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
31681      * If the path expression is the same as the field name, the mapping may be omitted.</p>
31682      * <p>The form of the mapping expression depends on the Reader being used.</p>
31683      * <div class="mdetail-params"><ul>
31684      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
31685      * 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>
31686      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
31687      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
31688      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
31689      * of the field's value. Defaults to the field specification's Array position.</div></li>
31690      * </ul></div>
31691      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
31692      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
31693      * return the desired data.</p>
31694      */
31695     mapping: null,
31696     /**
31697      * @cfg {Function} sortType
31698      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
31699      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
31700      * sort example:<pre><code>
31701 // current sort     after sort we want
31702 // +-+------+          +-+------+
31703 // |1|First |          |1|First |
31704 // |2|Last  |          |3|Second|
31705 // |3|Second|          |2|Last  |
31706 // +-+------+          +-+------+
31707
31708 sortType: function(value) {
31709    switch (value.toLowerCase()) // native toLowerCase():
31710    {
31711       case 'first': return 1;
31712       case 'second': return 2;
31713       default: return 3;
31714    }
31715 }
31716      * </code></pre>
31717      */
31718     sortType : null,
31719     /**
31720      * @cfg {String} sortDir
31721      * (Optional) Initial direction to sort (<tt>"ASC"</tt> or  <tt>"DESC"</tt>).  Defaults to
31722      * <tt>"ASC"</tt>.
31723      */
31724     sortDir : "ASC",
31725         /**
31726          * @cfg {Boolean} allowBlank 
31727          * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <tt>true</tt>.
31728          * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
31729          * to evaluate to <tt>false</tt>.
31730          */
31731         allowBlank : true
31732 };/**\r
31733  * @class Ext.data.DataReader\r
31734  * Abstract base class for reading structured data from a data source and converting\r
31735  * it into an object containing {@link Ext.data.Record} objects and metadata for use\r
31736  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not\r
31737  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},\r
31738  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.\r
31739  * @constructor Create a new DataReader\r
31740  * @param {Object} meta Metadata configuration options (implementation-specific).\r
31741  * @param {Array/Object} recordType\r
31742  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
31743  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
31744  * constructor created using {@link Ext.data.Record#create}.</p>\r
31745  */\r
31746 Ext.data.DataReader = function(meta, recordType){\r
31747     /**\r
31748      * This DataReader's configured metadata as passed to the constructor.\r
31749      * @type Mixed\r
31750      * @property meta\r
31751      */\r
31752     this.meta = meta;\r
31753     /**\r
31754      * @cfg {Array/Object} fields\r
31755      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
31756      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
31757      * constructor created from {@link Ext.data.Record#create}.</p>\r
31758      */\r
31759     this.recordType = Ext.isArray(recordType) ?\r
31760         Ext.data.Record.create(recordType) : recordType;\r
31761 };\r
31762 \r
31763 Ext.data.DataReader.prototype = {\r
31764 \r
31765     /**\r
31766      * Abstract method, overridden in {@link Ext.data.JsonReader}\r
31767      */\r
31768     buildExtractors : Ext.emptyFn,\r
31769 \r
31770     /**\r
31771      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.\r
31772      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming\r
31773      * data from server will be merged with the data in the local record.\r
31774      * In addition, you <b>must</b> return record-data from the server in the same order received.\r
31775      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.\r
31776      * @param {Record/Record[]} record The phantom record to be realized.\r
31777      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.\r
31778      */\r
31779     realize: function(rs, data){\r
31780         if (Ext.isArray(rs)) {\r
31781             for (var i = rs.length - 1; i >= 0; i--) {\r
31782                 // recurse\r
31783                 if (Ext.isArray(data)) {\r
31784                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
31785                 }\r
31786                 else {\r
31787                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.\r
31788                     // the else clause below will detect !this.isData and throw exception.\r
31789                     this.realize(rs.splice(i,1).shift(), data);\r
31790                 }\r
31791             }\r
31792         }\r
31793         else {\r
31794             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
31795             if (Ext.isArray(data) && data.length == 1) {\r
31796                 data = data.shift();\r
31797             }\r
31798             if (!this.isData(data)) {\r
31799                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.\r
31800                 //rs.commit();\r
31801                 throw new Ext.data.DataReader.Error('realize', rs);\r
31802             }\r
31803             this.buildExtractors();\r
31804             var values = this.extractValues(data, rs.fields.items, rs.fields.items.length);\r
31805             rs.phantom = false; // <-- That's what it's all about\r
31806             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords\r
31807             rs.id = data[this.meta.idProperty];\r
31808             rs.data = values;\r
31809             rs.commit();\r
31810         }\r
31811     },\r
31812 \r
31813     /**\r
31814      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.\r
31815      * You <b>must</b> return a complete new record from the server.  If you don't, your local record's missing fields\r
31816      * will be populated with the default values specified in your Ext.data.Record.create specification.  Without a defaultValue,\r
31817      * local fields will be populated with empty string "".  So return your entire record's data after both remote create and update.\r
31818      * In addition, you <b>must</b> return record-data from the server in the same order received.\r
31819      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed as the record receives\r
31820      * a fresh new data-hash.\r
31821      * @param {Record/Record[]} rs\r
31822      * @param {Object/Object[]} data\r
31823      */\r
31824     update : function(rs, data) {\r
31825         if (Ext.isArray(rs)) {\r
31826             for (var i=rs.length-1; i >= 0; i--) {\r
31827                 if (Ext.isArray(data)) {\r
31828                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
31829                 }\r
31830                 else {\r
31831                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.\r
31832                     // the else clause below will detect !this.isData and throw exception.\r
31833                     this.update(rs.splice(i,1).shift(), data);\r
31834                 }\r
31835             }\r
31836         }\r
31837         else {\r
31838                      // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
31839             if (Ext.isArray(data) && data.length == 1) {\r
31840                 data = data.shift();\r
31841             }\r
31842             if (!this.isData(data)) {\r
31843                 // TODO: create custom Exception class to return record in thrown exception.  Allow exception-handler the choice\r
31844                 // to commit or not rather than blindly rs.commit() here.\r
31845                 rs.commit();\r
31846                 throw new Ext.data.DataReader.Error('update', rs);\r
31847             }\r
31848             this.buildExtractors();\r
31849             rs.data = this.extractValues(Ext.apply(rs.data, data), rs.fields.items, rs.fields.items.length);\r
31850             rs.commit();\r
31851         }\r
31852     },\r
31853 \r
31854     /**\r
31855      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key\r
31856      * corresponding to idProperty defined in your DataReader config containing non-empty pk.\r
31857      * @param {Object} data\r
31858      * @return {Boolean}\r
31859      */\r
31860     isData : function(data) {\r
31861         return (data && Ext.isObject(data) && !Ext.isEmpty(data[this.meta.idProperty])) ? true : false;\r
31862     }\r
31863 };\r
31864 \r
31865 /**\r
31866  * @class Ext.data.DataReader.Error\r
31867  * @extends Ext.Error\r
31868  * General error class for Ext.data.DataReader\r
31869  */\r
31870 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {\r
31871     constructor : function(message, arg) {\r
31872         this.arg = arg;\r
31873         Ext.Error.call(this, message);\r
31874     },\r
31875     name: 'Ext.data.DataReader'\r
31876 });\r
31877 Ext.apply(Ext.data.DataReader.Error.prototype, {\r
31878     lang : {\r
31879         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",\r
31880         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",\r
31881         'invalid-response': "#readResponse received an invalid response from the server."\r
31882     }\r
31883 });\r
31884 \r
31885 \r
31886 /**
31887  * @class Ext.data.DataWriter
31888  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
31889  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
31890  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
31891  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
31892  * and should not be created directly. For existing implementations, see
31893  * {@link Ext.data.JsonWriter}.</p>
31894  * <p>Creating a writer is simple:</p>
31895  * <pre><code>
31896 var writer = new Ext.data.JsonWriter();
31897  * </code></pre>
31898  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
31899  * <pre><code>
31900 // Create a standard HttpProxy instance.
31901 var proxy = new Ext.data.HttpProxy({
31902     url: 'app.php/users'
31903 });
31904  * </code></pre>
31905  * <p>For finer grained control, the proxy may also be configured with an <code>api</code>:</p>
31906  * <pre><code>
31907 // Use the api specification
31908 var proxy = new Ext.data.HttpProxy({
31909     api: {
31910         read    : 'app.php/users/read',
31911         create  : 'app.php/users/create',
31912         update  : 'app.php/users/update',
31913         destroy : 'app.php/users/destroy'
31914     }
31915 });
31916  * </code></pre>
31917  * <p>Creating a Writer enabled store:</p>
31918  * <pre><code>
31919 var store = new Ext.data.Store({
31920     proxy: proxy,
31921     reader: reader,
31922     writer: writer
31923 });
31924  * </code></pre>
31925  * @constructor Create a new DataWriter
31926  * @param {Object} meta Metadata configuration options (implementation-specific)
31927  * @param {Object} recordType Either an Array of field definition objects as specified
31928  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
31929  * using {@link Ext.data.Record#create}.
31930  */
31931 Ext.data.DataWriter = function(config){
31932     /**
31933      * This DataWriter's configured metadata as passed to the constructor.
31934      * @type Mixed
31935      * @property meta
31936      */
31937     Ext.apply(this, config);
31938 };
31939
31940 Ext.data.DataWriter.prototype = {
31941
31942     /**
31943      * @cfg {Boolean} writeAllFields
31944      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
31945      * record -- not just those that changed.
31946      * <tt>false</tt> to have DataWriter only request modified fields from a record.
31947      */
31948     writeAllFields : false,
31949     /**
31950      * @cfg {Boolean} listful
31951      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
31952      * even when acting upon a single record.
31953      */
31954     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
31955
31956     /**
31957      * Writes data in preparation for server-write action.  Simply proxies to DataWriter#update, DataWriter#create
31958      * DataWriter#destroy.
31959      * @param {String} action [CREATE|UPDATE|DESTROY]
31960      * @param {Object} params The params-hash to write-to
31961      * @param {Record/Record[]} rs The recordset write.
31962      */
31963     write : function(action, params, rs) {
31964         this.render(action, rs, params, this[action](rs));
31965     },
31966
31967     /**
31968      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
31969      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
31970      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
31971      * @param {Record[]} rs Store recordset
31972      * @param {Object} params Http params to be sent to server.
31973      * @param {Object} data object populated according to DataReader meta-data.
31974      */
31975     render : Ext.emptyFn,
31976
31977     /**
31978      * update
31979      * @param {Object} p Params-hash to apply result to.
31980      * @param {Record/Record[]} rs Record(s) to write
31981      * @private
31982      */
31983     update : function(rs) {
31984         var params = {};
31985         if (Ext.isArray(rs)) {
31986             var data = [],
31987                 ids = [];
31988             Ext.each(rs, function(val){
31989                 ids.push(val.id);
31990                 data.push(this.updateRecord(val));
31991             }, this);
31992             params[this.meta.idProperty] = ids;
31993             params[this.meta.root] = data;
31994         }
31995         else if (rs instanceof Ext.data.Record) {
31996             params[this.meta.idProperty] = rs.id;
31997             params[this.meta.root] = this.updateRecord(rs);
31998         }
31999         return params;
32000     },
32001
32002     /**
32003      * @cfg {Function} saveRecord Abstract method that should be implemented in all subclasses
32004      * (e.g.: {@link Ext.data.JsonWriter#saveRecord JsonWriter.saveRecord}
32005      */
32006     updateRecord : Ext.emptyFn,
32007
32008     /**
32009      * create
32010      * @param {Object} p Params-hash to apply result to.
32011      * @param {Record/Record[]} rs Record(s) to write
32012      * @private
32013      */
32014     create : function(rs) {
32015         var params = {};
32016         if (Ext.isArray(rs)) {
32017             var data = [];
32018             Ext.each(rs, function(val){
32019                 data.push(this.createRecord(val));
32020             }, this);
32021             params[this.meta.root] = data;
32022         }
32023         else if (rs instanceof Ext.data.Record) {
32024             params[this.meta.root] = this.createRecord(rs);
32025         }
32026         return params;
32027     },
32028
32029     /**
32030      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
32031      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
32032      */
32033     createRecord : Ext.emptyFn,
32034
32035     /**
32036      * destroy
32037      * @param {Object} p Params-hash to apply result to.
32038      * @param {Record/Record[]} rs Record(s) to write
32039      * @private
32040      */
32041     destroy : function(rs) {
32042         var params = {};
32043         if (Ext.isArray(rs)) {
32044             var data = [],
32045                 ids = [];
32046             Ext.each(rs, function(val){
32047                 data.push(this.destroyRecord(val));
32048             }, this);
32049             params[this.meta.root] = data;
32050         } else if (rs instanceof Ext.data.Record) {
32051             params[this.meta.root] = this.destroyRecord(rs);
32052         }
32053         return params;
32054     },
32055
32056     /**
32057      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
32058      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
32059      */
32060     destroyRecord : Ext.emptyFn,
32061
32062     /**
32063      * Converts a Record to a hash
32064      * @param {Record}
32065      * @private
32066      */
32067     toHash : function(rec) {
32068         var map = rec.fields.map,
32069             data = {},
32070             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
32071             m;
32072         Ext.iterate(raw, function(prop, value){
32073             if((m = map[prop])){
32074                 data[m.mapping ? m.mapping : m.name] = value;
32075             }
32076         });
32077         data[this.meta.idProperty] = rec.id;
32078         return data;
32079     }
32080 };/**\r
32081  * @class Ext.data.DataProxy\r
32082  * @extends Ext.util.Observable\r
32083  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.\r
32084  * This class is intended to be extended and should not be created directly. For existing implementations,\r
32085  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and\r
32086  * {@link Ext.data.MemoryProxy}.</p>\r
32087  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}\r
32088  * (of the appropriate type which knows how to parse the data object) to provide a block of\r
32089  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>\r
32090  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the\r
32091  * config object to an {@link Ext.data.Connection}.</p>\r
32092  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the\r
32093  * <code>load</code> method (deprecated). See\r
32094  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or\r
32095  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>\r
32096  * <p><b><u>Example 1</u></b></p>\r
32097  * <pre><code>\r
32098 proxy: new Ext.data.ScriptTagProxy({\r
32099     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'\r
32100 }),\r
32101  * </code></pre>\r
32102  * <p><b><u>Example 2</u></b></p>\r
32103  * <pre><code>\r
32104 proxy : new Ext.data.HttpProxy({\r
32105     {@link Ext.data.Connection#method method}: 'GET',\r
32106     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,\r
32107     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}\r
32108     {@link #api}: {\r
32109         // all actions except the following will use above url\r
32110         create  : 'local/new.php',\r
32111         update  : 'local/update.php'\r
32112     }\r
32113 }),\r
32114  * </code></pre>\r
32115  */\r
32116 Ext.data.DataProxy = function(conn){\r
32117     // make sure we have a config object here to support ux proxies.\r
32118     // All proxies should now send config into superclass constructor.\r
32119     conn = conn || {};\r
32120 \r
32121     // This line caused a bug when people use custom Connection object having its own request method.\r
32122     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config\r
32123     //Ext.applyIf(this, conn);\r
32124 \r
32125     this.api     = conn.api;\r
32126     this.url     = conn.url;\r
32127     this.restful = conn.restful;\r
32128     this.listeners = conn.listeners;\r
32129 \r
32130     // deprecated\r
32131     this.prettyUrls = conn.prettyUrls;\r
32132 \r
32133     /**\r
32134      * @cfg {Object} api\r
32135      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".\r
32136      * Defaults to:<pre><code>\r
32137 api: {\r
32138     read    : undefined,\r
32139     create  : undefined,\r
32140     update  : undefined,\r
32141     destroy : undefined\r
32142 }\r
32143 </code></pre>\r
32144      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request\r
32145      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>\r
32146      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API\r
32147      * property should be modified before the action is requested using the corresponding before\r
32148      * action event.  For example to modify the URL associated with the load action:\r
32149      * <pre><code>\r
32150 // modify the url for the action\r
32151 myStore.on({\r
32152     beforeload: {\r
32153         fn: function (store, options) {\r
32154             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.\r
32155             store.proxy.setUrl('changed1.php');\r
32156 \r
32157             // set optional second parameter to true to make this URL change\r
32158             // permanent, applying this URL for all subsequent requests.\r
32159             store.proxy.setUrl('changed1.php', true);\r
32160 \r
32161             // manually set the <b>private</b> connection URL.\r
32162             // <b>Warning:</b>  Accessing the private URL property should be avoided.\r
32163             // Use the public method <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> instead, shown above.\r
32164             // It should be noted that changing the URL like this will affect\r
32165             // the URL for just this request.  Subsequent requests will use the\r
32166             // API or URL defined in your initial proxy configuration.\r
32167             store.proxy.conn.url = 'changed1.php';\r
32168 \r
32169             // proxy URL will be superseded by API (only if proxy created to use ajax):\r
32170             // It should be noted that proxy API changes are permanent and will\r
32171             // be used for all subsequent requests.\r
32172             store.proxy.api.load = 'changed2.php';\r
32173 \r
32174             // However, altering the proxy API should be done using the public\r
32175             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt> instead.\r
32176             store.proxy.setApi('load', 'changed2.php');\r
32177 \r
32178             // Or set the entire API with a config-object.\r
32179             // When using the config-object option, you must redefine the <b>entire</b>\r
32180             // API -- not just a specific action of it.\r
32181             store.proxy.setApi({\r
32182                 read    : 'changed_read.php',\r
32183                 create  : 'changed_create.php',\r
32184                 update  : 'changed_update.php',\r
32185                 destroy : 'changed_destroy.php'\r
32186             });\r
32187         }\r
32188     }\r
32189 });\r
32190      * </code></pre>\r
32191      * </p>\r
32192      */\r
32193     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.\r
32194     try {\r
32195         Ext.data.Api.prepare(this);\r
32196     } catch (e) {\r
32197         if (e instanceof Ext.data.Api.Error) {\r
32198             e.toConsole();\r
32199         }\r
32200     }\r
32201 \r
32202     this.addEvents(\r
32203         /**\r
32204          * @event exception\r
32205          * <p>Fires if an exception occurs in the Proxy during a remote request.\r
32206          * This event is relayed through a corresponding\r
32207          * {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},\r
32208          * so any Store instance may observe this event.\r
32209          * This event can be fired for one of two reasons:</p>\r
32210          * <div class="mdetail-params"><ul>\r
32211          * <li>remote-request <b>failed</b> : <div class="sub-desc">\r
32212          * The server did not return status === 200.\r
32213          * </div></li>\r
32214          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">\r
32215          * The remote-request succeeded but the reader could not read the response.\r
32216          * This means the server returned data, but the configured Reader threw an\r
32217          * error while reading the response.  In this case, this event will be\r
32218          * raised and the caught error will be passed along into this event.\r
32219          * </div></li>\r
32220          * </ul></div>\r
32221          * <br><p>This event fires with two different contexts based upon the 2nd\r
32222          * parameter <tt>type [remote|response]</tt>.  The first four parameters\r
32223          * are identical between the two contexts -- only the final two parameters\r
32224          * differ.</p>\r
32225          * @param {DataProxy} this The proxy that sent the request\r
32226          * @param {String} type\r
32227          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>\r
32228          * <div class="mdetail-params"><ul>\r
32229          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
32230          * <p>An <b>invalid</b> response from the server was returned: either 404,\r
32231          * 500 or the response meta-data does not match that defined in the DataReader\r
32232          * (e.g.: root, idProperty, successProperty).</p>\r
32233          * </div></li>\r
32234          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
32235          * <p>A <b>valid</b> response was returned from the server having\r
32236          * successProperty === false.  This response might contain an error-message\r
32237          * sent from the server.  For example, the user may have failed\r
32238          * authentication/authorization or a database validation error occurred.</p>\r
32239          * </div></li>\r
32240          * </ul></div>\r
32241          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.\r
32242          * @param {Object} options The options for the action that were specified in the {@link #request}.\r
32243          * @param {Object} response\r
32244          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
32245          * <div class="mdetail-params"><ul>\r
32246          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
32247          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>\r
32248          * </div></li>\r
32249          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
32250          * <p>The decoded response object sent from the server.</p>\r
32251          * </div></li>\r
32252          * </ul></div>\r
32253          * @param {Mixed} arg\r
32254          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
32255          * <div class="mdetail-params"><ul>\r
32256          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">\r
32257          * <p>The JavaScript Error object caught if the configured Reader could not read the data.\r
32258          * If the remote request returns success===false, this parameter will be null.</p>\r
32259          * </div></li>\r
32260          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">\r
32261          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action\r
32262          * (Ext.data.Api.actions.create|update|destroy).</p>\r
32263          * </div></li>\r
32264          * </ul></div>\r
32265          */\r
32266         'exception',\r
32267         /**\r
32268          * @event beforeload\r
32269          * Fires before a request to retrieve a data object.\r
32270          * @param {DataProxy} this The proxy for the request\r
32271          * @param {Object} params The params object passed to the {@link #request} function\r
32272          */\r
32273         'beforeload',\r
32274         /**\r
32275          * @event load\r
32276          * Fires before the load method's callback is called.\r
32277          * @param {DataProxy} this The proxy for the request\r
32278          * @param {Object} o The request transaction object\r
32279          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
32280          */\r
32281         'load',\r
32282         /**\r
32283          * @event loadexception\r
32284          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event\r
32285          * varies depending on the proxy, use the catch-all {@link #exception} event instead.\r
32286          * This event will fire in addition to the {@link #exception} event.</p>\r
32287          * @param {misc} misc See {@link #exception}.\r
32288          * @deprecated\r
32289          */\r
32290         'loadexception',\r
32291         /**\r
32292          * @event beforewrite\r
32293          * Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy\r
32294          * @param {DataProxy} this The proxy for the request\r
32295          * @param {String} action [Ext.data.Api.actions.create|update|destroy]\r
32296          * @param {Record/Array[Record]} rs The Record(s) to create|update|destroy.\r
32297          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.\r
32298          */\r
32299         'beforewrite',\r
32300         /**\r
32301          * @event write\r
32302          * Fires before the request-callback is called\r
32303          * @param {DataProxy} this The proxy that sent the request\r
32304          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]\r
32305          * @param {Object} data The data object extracted from the server-response\r
32306          * @param {Object} response The decoded response from server\r
32307          * @param {Record/Record{}} rs The records from Store\r
32308          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
32309          */\r
32310         'write'\r
32311     );\r
32312     Ext.data.DataProxy.superclass.constructor.call(this);\r
32313 };\r
32314 \r
32315 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {\r
32316     /**\r
32317      * @cfg {Boolean} restful\r
32318      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>\r
32319      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the\r
32320      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the\r
32321      * Store is RESTful, there is no need to set this option on the proxy.</p>\r
32322      * <br><p>RESTful implementations enable the serverside framework to automatically route\r
32323      * actions sent to one url based upon the HTTP method, for example:\r
32324      * <pre><code>\r
32325 store: new Ext.data.Store({\r
32326     restful: true,\r
32327     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users\r
32328     ...\r
32329 )}\r
32330      * </code></pre>\r
32331      * There is no <code>{@link #api}</code> specified in the configuration of the proxy,\r
32332      * all requests will be marshalled to a single RESTful url (/users) so the serverside\r
32333      * framework can inspect the HTTP Method and act accordingly:\r
32334      * <pre>\r
32335 <u>Method</u>   <u>url</u>        <u>action</u>\r
32336 POST     /users     create\r
32337 GET      /users     read\r
32338 PUT      /users/23  update\r
32339 DESTROY  /users/23  delete\r
32340      * </pre></p>\r
32341      */\r
32342     restful: false,\r
32343 \r
32344     /**\r
32345      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>\r
32346      * <p>If called with an object as the only parameter, the object should redefine the <b>entire</b> API, e.g.:</p><pre><code>\r
32347 proxy.setApi({\r
32348     read    : '/users/read',\r
32349     create  : '/users/create',\r
32350     update  : '/users/update',\r
32351     destroy : '/users/destroy'\r
32352 });\r
32353 </code></pre>\r
32354      * <p>If called with two parameters, the first parameter should be a string specifying the API action to\r
32355      * redefine and the second parameter should be the URL (or function if using DirectProxy) to call for that action, e.g.:</p><pre><code>\r
32356 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');\r
32357 </code></pre>\r
32358      * @param {String/Object} api An API specification object, or the name of an action.\r
32359      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.\r
32360      */\r
32361     setApi : function() {\r
32362         if (arguments.length == 1) {\r
32363             var valid = Ext.data.Api.isValid(arguments[0]);\r
32364             if (valid === true) {\r
32365                 this.api = arguments[0];\r
32366             }\r
32367             else {\r
32368                 throw new Ext.data.Api.Error('invalid', valid);\r
32369             }\r
32370         }\r
32371         else if (arguments.length == 2) {\r
32372             if (!Ext.data.Api.isAction(arguments[0])) {\r
32373                 throw new Ext.data.Api.Error('invalid', arguments[0]);\r
32374             }\r
32375             this.api[arguments[0]] = arguments[1];\r
32376         }\r
32377         Ext.data.Api.prepare(this);\r
32378     },\r
32379 \r
32380     /**\r
32381      * Returns true if the specified action is defined as a unique action in the api-config.\r
32382      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined\r
32383      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to\r
32384      * the corresponding code for CRUD action.\r
32385      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action\r
32386      * @return {Boolean}\r
32387      */\r
32388     isApiAction : function(action) {\r
32389         return (this.api[action]) ? true : false;\r
32390     },\r
32391 \r
32392     /**\r
32393      * All proxy actions are executed through this method.  Automatically fires the "before" + action event\r
32394      * @param {String} action Name of the action\r
32395      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'\r
32396      * @param {Object} params\r
32397      * @param {Ext.data.DataReader} reader\r
32398      * @param {Function} callback\r
32399      * @param {Object} scope Scope with which to call the callback (defaults to the Proxy object)\r
32400      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.\r
32401      */\r
32402     request : function(action, rs, params, reader, callback, scope, options) {\r
32403         if (!this.api[action] && !this.load) {\r
32404             throw new Ext.data.DataProxy.Error('action-undefined', action);\r
32405         }\r
32406         params = params || {};\r
32407         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {\r
32408             this.doRequest.apply(this, arguments);\r
32409         }\r
32410         else {\r
32411             callback.call(scope || this, null, options, false);\r
32412         }\r
32413     },\r
32414 \r
32415 \r
32416     /**\r
32417      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.\r
32418      * @deprecated\r
32419      * @param {Object} params\r
32420      * @param {Object} reader\r
32421      * @param {Object} callback\r
32422      * @param {Object} scope\r
32423      * @param {Object} arg\r
32424      */\r
32425     load : null,\r
32426 \r
32427     /**\r
32428      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses\r
32429      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},\r
32430      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).\r
32431      */\r
32432     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
32433         // default implementation of doRequest for backwards compatibility with 2.0 proxies.\r
32434         // If we're executing here, the action is probably "load".\r
32435         // Call with the pre-3.0 method signature.\r
32436         this.load(params, reader, callback, scope, options);\r
32437     },\r
32438 \r
32439     /**\r
32440      * buildUrl\r
32441      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,\r
32442      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an\r
32443      * instance of Ext.data.Record rather than an Array of them.\r
32444      * @param {String} action The api action being executed [read|create|update|destroy]\r
32445      * @param {Ext.data.Record/Array[Ext.data.Record]} The record or Array of Records being acted upon.\r
32446      * @return {String} url\r
32447      * @private\r
32448      */\r
32449     buildUrl : function(action, record) {\r
32450         record = record || null;\r
32451         var url = (this.api[action]) ? this.api[action].url : this.url;\r
32452         if (!url) {\r
32453             throw new Ext.data.Api.Error('invalid-url', action);\r
32454         }\r
32455 \r
32456         var format = null;\r
32457         var m = url.match(/(.*)(\.\w+)$/);  // <-- look for urls with "provides" suffix, e.g.: /users.json, /users.xml, etc\r
32458         if (m) {\r
32459             format = m[2];\r
32460             url = m[1];\r
32461         }\r
32462         // prettyUrls is deprectated in favor of restful-config\r
32463         if ((this.prettyUrls === true || this.restful === true) && record instanceof Ext.data.Record && !record.phantom) {\r
32464             url += '/' + record.id;\r
32465         }\r
32466         if (format) {   // <-- append the request format if exists (ie: /users/update/69[.json])\r
32467             url += format;\r
32468         }\r
32469         return url;\r
32470     },\r
32471 \r
32472     /**\r
32473      * Destroys the proxy by purging any event listeners and cancelling any active requests.\r
32474      */\r
32475     destroy: function(){\r
32476         this.purgeListeners();\r
32477     }\r
32478 });\r
32479 \r
32480 /**\r
32481  * @class Ext.data.DataProxy.Error\r
32482  * @extends Ext.Error\r
32483  * DataProxy Error extension.\r
32484  * constructor\r
32485  * @param {String} name\r
32486  * @param {Record/Array[Record]/Array}\r
32487  */\r
32488 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {\r
32489     constructor : function(message, arg) {\r
32490         this.arg = arg;\r
32491         Ext.Error.call(this, message);\r
32492     },\r
32493     name: 'Ext.data.DataProxy'\r
32494 });\r
32495 Ext.apply(Ext.data.DataProxy.Error.prototype, {\r
32496     lang: {\r
32497         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",\r
32498         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'\r
32499     }\r
32500 });\r
32501 /**\r
32502  * @class Ext.data.ScriptTagProxy\r
32503  * @extends Ext.data.DataProxy\r
32504  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain\r
32505  * other than the originating domain of the running page.<br>\r
32506  * <p>\r
32507  * <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\r
32508  * of the running page, you must use this class, rather than HttpProxy.</b><br>\r
32509  * <p>\r
32510  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript\r
32511  * source code because it is used as the source inside a &lt;script> tag.<br>\r
32512  * <p>\r
32513  * In order for the browser to process the returned data, the server must wrap the data object\r
32514  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.\r
32515  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy\r
32516  * depending on whether the callback name was passed:\r
32517  * <p>\r
32518  * <pre><code>\r
32519 boolean scriptTag = false;\r
32520 String cb = request.getParameter("callback");\r
32521 if (cb != null) {\r
32522     scriptTag = true;\r
32523     response.setContentType("text/javascript");\r
32524 } else {\r
32525     response.setContentType("application/x-json");\r
32526 }\r
32527 Writer out = response.getWriter();\r
32528 if (scriptTag) {\r
32529     out.write(cb + "(");\r
32530 }\r
32531 out.print(dataBlock.toJsonString());\r
32532 if (scriptTag) {\r
32533     out.write(");");\r
32534 }\r
32535 </code></pre>\r
32536  *\r
32537  * @constructor\r
32538  * @param {Object} config A configuration object.\r
32539  */\r
32540 Ext.data.ScriptTagProxy = function(config){\r
32541     Ext.apply(this, config);\r
32542 \r
32543     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);\r
32544 \r
32545     this.head = document.getElementsByTagName("head")[0];\r
32546 \r
32547     /**\r
32548      * @event loadexception\r
32549      * <b>Deprecated</b> in favor of 'exception' event.\r
32550      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:\r
32551      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit\r
32552      * specified by {@link #timeout}.  In this case, this event will be raised and the\r
32553      * fourth parameter (read error) will be null.</li>\r
32554      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned\r
32555      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be\r
32556      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>\r
32557      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly\r
32558      * on any Store instance.\r
32559      * @param {Object} this\r
32560      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load\r
32561      * call timed out, this parameter will be null.\r
32562      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
32563      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.\r
32564      * If the remote request returns success: false, this parameter will be null.\r
32565      */\r
32566 };\r
32567 \r
32568 Ext.data.ScriptTagProxy.TRANS_ID = 1000;\r
32569 \r
32570 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {\r
32571     /**\r
32572      * @cfg {String} url The URL from which to request the data object.\r
32573      */\r
32574     /**\r
32575      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.\r
32576      */\r
32577     timeout : 30000,\r
32578     /**\r
32579      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells\r
32580      * the server the name of the callback function set up by the load call to process the returned data object.\r
32581      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate\r
32582      * javascript output which calls this named function passing the data object as its only parameter.\r
32583      */\r
32584     callbackParam : "callback",\r
32585     /**\r
32586      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter\r
32587      * name to the request.\r
32588      */\r
32589     nocache : true,\r
32590 \r
32591     /**\r
32592      * HttpProxy implementation of DataProxy#doRequest\r
32593      * @param {String} action\r
32594      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null\r
32595      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
32596      * for the request to the remote server.\r
32597      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
32598      * object into a block of Ext.data.Records.\r
32599      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
32600      * The function must be passed <ul>\r
32601      * <li>The Record block object</li>\r
32602      * <li>The "arg" argument from the load function</li>\r
32603      * <li>A boolean success indicator</li>\r
32604      * </ul>\r
32605      * @param {Object} scope The scope in which to call the callback\r
32606      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
32607      */\r
32608     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
32609         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));\r
32610 \r
32611         var url = this.buildUrl(action, rs);\r
32612         if (!url) {\r
32613             throw new Ext.data.Api.Error('invalid-url', url);\r
32614         }\r
32615         url = Ext.urlAppend(url, p);\r
32616 \r
32617         if(this.nocache){\r
32618             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));\r
32619         }\r
32620         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;\r
32621         var trans = {\r
32622             id : transId,\r
32623             action: action,\r
32624             cb : "stcCallback"+transId,\r
32625             scriptId : "stcScript"+transId,\r
32626             params : params,\r
32627             arg : arg,\r
32628             url : url,\r
32629             callback : callback,\r
32630             scope : scope,\r
32631             reader : reader\r
32632         };\r
32633         window[trans.cb] = this.createCallback(action, rs, trans);\r
32634         url += String.format("&{0}={1}", this.callbackParam, trans.cb);\r
32635         if(this.autoAbort !== false){\r
32636             this.abort();\r
32637         }\r
32638 \r
32639         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);\r
32640 \r
32641         var script = document.createElement("script");\r
32642         script.setAttribute("src", url);\r
32643         script.setAttribute("type", "text/javascript");\r
32644         script.setAttribute("id", trans.scriptId);\r
32645         this.head.appendChild(script);\r
32646 \r
32647         this.trans = trans;\r
32648     },\r
32649 \r
32650     // @private createCallback\r
32651     createCallback : function(action, rs, trans) {\r
32652         var self = this;\r
32653         return function(res) {\r
32654             self.trans = false;\r
32655             self.destroyTrans(trans, true);\r
32656             if (action === Ext.data.Api.actions.read) {\r
32657                 self.onRead.call(self, action, trans, res);\r
32658             } else {\r
32659                 self.onWrite.call(self, action, trans, res, rs);\r
32660             }\r
32661         };\r
32662     },\r
32663     /**\r
32664      * Callback for read actions\r
32665      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
32666      * @param {Object} trans The request transaction object\r
32667      * @param {Object} res The server response\r
32668      * @private\r
32669      */\r
32670     onRead : function(action, trans, res) {\r
32671         var result;\r
32672         try {\r
32673             result = trans.reader.readRecords(res);\r
32674         }catch(e){\r
32675             // @deprecated: fire loadexception\r
32676             this.fireEvent("loadexception", this, trans, res, e);\r
32677 \r
32678             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
32679             trans.callback.call(trans.scope||window, null, trans.arg, false);\r
32680             return;\r
32681         }\r
32682         if (result.success === false) {\r
32683             // @deprecated: fire old loadexception for backwards-compat.\r
32684             this.fireEvent('loadexception', this, trans, res);\r
32685 \r
32686             this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
32687         } else {\r
32688             this.fireEvent("load", this, res, trans.arg);\r
32689         }\r
32690         trans.callback.call(trans.scope||window, result, trans.arg, result.success);\r
32691     },\r
32692     /**\r
32693      * Callback for write actions\r
32694      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
32695      * @param {Object} trans The request transaction object\r
32696      * @param {Object} res The server response\r
32697      * @private\r
32698      */\r
32699     onWrite : function(action, trans, res, rs) {\r
32700         var reader = trans.reader;\r
32701         try {\r
32702             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.\r
32703             reader.readResponse(action, res);\r
32704         } catch (e) {\r
32705             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
32706             trans.callback.call(trans.scope||window, null, res, false);\r
32707             return;\r
32708         }\r
32709         if(!res[reader.meta.successProperty] === true){\r
32710             this.fireEvent('exception', this, 'remote', action, trans, res, rs);\r
32711             trans.callback.call(trans.scope||window, null, res, false);\r
32712             return;\r
32713         }\r
32714         this.fireEvent("write", this, action, res[reader.meta.root], res, rs, trans.arg );\r
32715         trans.callback.call(trans.scope||window, res[reader.meta.root], res, true);\r
32716     },\r
32717 \r
32718     // private\r
32719     isLoading : function(){\r
32720         return this.trans ? true : false;\r
32721     },\r
32722 \r
32723     /**\r
32724      * Abort the current server request.\r
32725      */\r
32726     abort : function(){\r
32727         if(this.isLoading()){\r
32728             this.destroyTrans(this.trans);\r
32729         }\r
32730     },\r
32731 \r
32732     // private\r
32733     destroyTrans : function(trans, isLoaded){\r
32734         this.head.removeChild(document.getElementById(trans.scriptId));\r
32735         clearTimeout(trans.timeoutId);\r
32736         if(isLoaded){\r
32737             window[trans.cb] = undefined;\r
32738             try{\r
32739                 delete window[trans.cb];\r
32740             }catch(e){}\r
32741         }else{\r
32742             // if hasn't been loaded, wait for load to remove it to prevent script error\r
32743             window[trans.cb] = function(){\r
32744                 window[trans.cb] = undefined;\r
32745                 try{\r
32746                     delete window[trans.cb];\r
32747                 }catch(e){}\r
32748             };\r
32749         }\r
32750     },\r
32751 \r
32752     // private\r
32753     handleFailure : function(trans){\r
32754         this.trans = false;\r
32755         this.destroyTrans(trans, false);\r
32756         if (trans.action === Ext.data.Api.actions.read) {\r
32757             // @deprecated firing loadexception\r
32758             this.fireEvent("loadexception", this, null, trans.arg);\r
32759         }\r
32760 \r
32761         this.fireEvent('exception', this, 'response', trans.action, {\r
32762             response: null,\r
32763             options: trans.arg\r
32764         });\r
32765         trans.callback.call(trans.scope||window, null, trans.arg, false);\r
32766     },\r
32767 \r
32768     // inherit docs\r
32769     destroy: function(){\r
32770         this.abort();\r
32771         Ext.data.ScriptTagProxy.superclass.destroy.call(this);\r
32772     }\r
32773 });/**\r
32774  * @class Ext.data.HttpProxy\r
32775  * @extends Ext.data.DataProxy\r
32776  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same\r
32777  * domain of the originating page.</p>\r
32778  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other\r
32779  * than the domain from which the running page was served. For cross-domain requests, use a\r
32780  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>\r
32781  * <p>Be aware that to enable the browser to parse an XML document, the server must set\r
32782  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>\r
32783  * @constructor\r
32784  * @param {Object} conn\r
32785  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.\r
32786  * <p>Note that if this HttpProxy is being used by a (@link Ext.data.Store Store}, then the\r
32787  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>\r
32788  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,\r
32789  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be\r
32790  * used to pass parameters known at instantiation time.</p>\r
32791  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make\r
32792  * the request.</p>\r
32793  */\r
32794 Ext.data.HttpProxy = function(conn){\r
32795     Ext.data.HttpProxy.superclass.constructor.call(this, conn);\r
32796 \r
32797     /**\r
32798      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy\r
32799      * uses to make requests to the server. Properties of this object may be changed dynamically to\r
32800      * change the way data is requested.\r
32801      * @property\r
32802      */\r
32803     this.conn = conn;\r
32804 \r
32805     // nullify the connection url.  The url param has been copied to 'this' above.  The connection\r
32806     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the\r
32807     // connection url during beforeaction events (ie: beforeload, beforesave, etc).  The connection url will be nullified\r
32808     // after each request as well.  Url is always re-defined during doRequest.\r
32809     this.conn.url = null;\r
32810 \r
32811     this.useAjax = !conn || !conn.events;\r
32812 \r
32813     //private.  A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]\r
32814     var actions = Ext.data.Api.actions;\r
32815     this.activeRequest = {};\r
32816     for (var verb in actions) {\r
32817         this.activeRequest[actions[verb]] = undefined;\r
32818     }\r
32819 };\r
32820 \r
32821 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {\r
32822     /**\r
32823      * @cfg {Boolean} restful\r
32824      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's\r
32825      * {@link Ext.data.Record#id id} will be appended to the url (defaults to <tt>false</tt>).</p><br>\r
32826      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>\r
32827      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the\r
32828      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>\r
32829      * <p>Some MVC (e.g., Ruby on Rails, Merb and Django) support this style of segment based urls\r
32830      * where the segments in the URL follow the Model-View-Controller approach.</p><pre><code>\r
32831      * someSite.com/controller/action/id\r
32832      * </code></pre>\r
32833      * Where the segments in the url are typically:<div class="mdetail-params"><ul>\r
32834      * <li>The first segment : represents the controller class that should be invoked.</li>\r
32835      * <li>The second segment : represents the class function, or method, that should be called.</li>\r
32836      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>\r
32837      * </ul></div></p>\r
32838      * <p>For example:</p>\r
32839      * <pre><code>\r
32840 api: {\r
32841     load :    '/controller/load',\r
32842     create :  '/controller/new',  // Server MUST return idProperty of new record\r
32843     save :    '/controller/update',\r
32844     destroy : '/controller/destroy_action'\r
32845 }\r
32846 \r
32847 // Alternatively, one can use the object-form to specify each API-action\r
32848 api: {\r
32849     load: {url: 'read.php', method: 'GET'},\r
32850     create: 'create.php',\r
32851     destroy: 'destroy.php',\r
32852     save: 'update.php'\r
32853 }\r
32854      */\r
32855 \r
32856     /**\r
32857      * Return the {@link Ext.data.Connection} object being used by this Proxy.\r
32858      * @return {Connection} The Connection object. This object may be used to subscribe to events on\r
32859      * a finer-grained basis than the DataProxy events.\r
32860      */\r
32861     getConnection : function() {\r
32862         return this.useAjax ? Ext.Ajax : this.conn;\r
32863     },\r
32864 \r
32865     /**\r
32866      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl\r
32867      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for\r
32868      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined\r
32869      * in the initial proxy configuration.\r
32870      * @param {String} url\r
32871      * @param {Boolean} makePermanent (Optional) [false]\r
32872      *\r
32873      * (e.g.: beforeload, beforesave, etc).\r
32874      */\r
32875     setUrl : function(url, makePermanent) {\r
32876         this.conn.url = url;\r
32877         if (makePermanent === true) {\r
32878             this.url = url;\r
32879             Ext.data.Api.prepare(this);\r
32880         }\r
32881     },\r
32882 \r
32883     /**\r
32884      * HttpProxy implementation of DataProxy#doRequest\r
32885      * @param {String} action The crud action type (create, read, update, destroy)\r
32886      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
32887      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
32888      * for the request to the remote server.\r
32889      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
32890      * object into a block of Ext.data.Records.\r
32891      * @param {Function} callback\r
32892      * <div class="sub-desc"><p>A function to be called after the request.\r
32893      * The <tt>callback</tt> is passed the following arguments:<ul>\r
32894      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>\r
32895      * <li><tt>options</tt>: Options object from the action request</li>\r
32896      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>\r
32897      * @param {Object} scope The scope in which to call the callback\r
32898      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
32899      */\r
32900     doRequest : function(action, rs, params, reader, cb, scope, arg) {\r
32901         var  o = {\r
32902             method: (this.api[action]) ? this.api[action]['method'] : undefined,\r
32903             request: {\r
32904                 callback : cb,\r
32905                 scope : scope,\r
32906                 arg : arg\r
32907             },\r
32908             reader: reader,\r
32909             callback : this.createCallback(action, rs),\r
32910             scope: this\r
32911         };\r
32912         // Sample the request data:  If it's an object, then it hasn't been json-encoded yet.\r
32913         // Transmit data using jsonData config of Ext.Ajax.request\r
32914         if (typeof(params[reader.meta.root]) === 'object') {\r
32915             o.jsonData = params;\r
32916         } else {\r
32917             o.params = params || {};\r
32918         }\r
32919         // Set the connection url.  If this.conn.url is not null here,\r
32920         // the user may have overridden the url during a beforeaction event-handler.\r
32921         // this.conn.url is nullified after each request.\r
32922         if (this.conn.url === null) {\r
32923             this.conn.url = this.buildUrl(action, rs);\r
32924         }\r
32925         else if (this.restful === true && rs instanceof Ext.data.Record && !rs.phantom) {\r
32926             this.conn.url += '/' + rs.id;\r
32927         }\r
32928         if(this.useAjax){\r
32929 \r
32930             Ext.applyIf(o, this.conn);\r
32931 \r
32932             // If a currently running request is found for this action, abort it.\r
32933             if (this.activeRequest[action]) {\r
32934                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array\r
32935                 //Ext.Ajax.abort(this.activeRequest[action]);\r
32936             }\r
32937             this.activeRequest[action] = Ext.Ajax.request(o);\r
32938         }else{\r
32939             this.conn.request(o);\r
32940         }\r
32941         // request is sent, nullify the connection url in preparation for the next request\r
32942         this.conn.url = null;\r
32943     },\r
32944 \r
32945     /**\r
32946      * Returns a callback function for a request.  Note a special case is made for the\r
32947      * read action vs all the others.\r
32948      * @param {String} action [create|update|delete|load]\r
32949      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon\r
32950      * @private\r
32951      */\r
32952     createCallback : function(action, rs) {\r
32953         return function(o, success, response) {\r
32954             this.activeRequest[action] = undefined;\r
32955             if (!success) {\r
32956                 if (action === Ext.data.Api.actions.read) {\r
32957                     // @deprecated: fire loadexception for backwards compat.\r
32958                     this.fireEvent('loadexception', this, o, response);\r
32959                 }\r
32960                 this.fireEvent('exception', this, 'response', action, o, response);\r
32961                 o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
32962                 return;\r
32963             }\r
32964             if (action === Ext.data.Api.actions.read) {\r
32965                 this.onRead(action, o, response);\r
32966             } else {\r
32967                 this.onWrite(action, o, response, rs);\r
32968             }\r
32969         }\r
32970     },\r
32971 \r
32972     /**\r
32973      * Callback for read action\r
32974      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
32975      * @param {Object} o The request transaction object\r
32976      * @param {Object} res The server response\r
32977      * @private\r
32978      */\r
32979     onRead : function(action, o, response) {\r
32980         var result;\r
32981         try {\r
32982             result = o.reader.read(response);\r
32983         }catch(e){\r
32984             // @deprecated: fire old loadexception for backwards-compat.\r
32985             this.fireEvent('loadexception', this, o, response, e);\r
32986             this.fireEvent('exception', this, 'response', action, o, response, e);\r
32987             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
32988             return;\r
32989         }\r
32990         if (result.success === false) {\r
32991             // @deprecated: fire old loadexception for backwards-compat.\r
32992             this.fireEvent('loadexception', this, o, response);\r
32993 \r
32994             // Get DataReader read-back a response-object to pass along to exception event\r
32995             var res = o.reader.readResponse(action, response);\r
32996             this.fireEvent('exception', this, 'remote', action, o, res, null);\r
32997         }\r
32998         else {\r
32999             this.fireEvent('load', this, o, o.request.arg);\r
33000         }\r
33001         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);\r
33002     },\r
33003     /**\r
33004      * Callback for write actions\r
33005      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
33006      * @param {Object} trans The request transaction object\r
33007      * @param {Object} res The server response\r
33008      * @private\r
33009      */\r
33010     onWrite : function(action, o, response, rs) {\r
33011         var reader = o.reader;\r
33012         var res;\r
33013         try {\r
33014             res = reader.readResponse(action, response);\r
33015         } catch (e) {\r
33016             this.fireEvent('exception', this, 'response', action, o, response, e);\r
33017             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
33018             return;\r
33019         }\r
33020         if (res[reader.meta.successProperty] === false) {\r
33021             this.fireEvent('exception', this, 'remote', action, o, res, rs);\r
33022         } else {\r
33023             this.fireEvent('write', this, action, res[reader.meta.root], res, rs, o.request.arg);\r
33024         }\r
33025         o.request.callback.call(o.request.scope, res[reader.meta.root], res, res[reader.meta.successProperty]);\r
33026     },\r
33027 \r
33028     // inherit docs\r
33029     destroy: function(){\r
33030         if(!this.useAjax){\r
33031             this.conn.abort();\r
33032         }else if(this.activeRequest){\r
33033             var actions = Ext.data.Api.actions;\r
33034             for (var verb in actions) {\r
33035                 if(this.activeRequest[actions[verb]]){\r
33036                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);\r
33037                 }\r
33038             }\r
33039         }\r
33040         Ext.data.HttpProxy.superclass.destroy.call(this);\r
33041     }\r
33042 });/**\r
33043  * @class Ext.data.MemoryProxy\r
33044  * @extends Ext.data.DataProxy\r
33045  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor\r
33046  * to the Reader when its load method is called.\r
33047  * @constructor\r
33048  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.\r
33049  */\r
33050 Ext.data.MemoryProxy = function(data){\r
33051     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super\r
33052     var api = {};\r
33053     api[Ext.data.Api.actions.read] = true;\r
33054     Ext.data.MemoryProxy.superclass.constructor.call(this, {\r
33055         api: api\r
33056     });\r
33057     this.data = data;\r
33058 };\r
33059 \r
33060 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {\r
33061     /**\r
33062      * @event loadexception\r
33063      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed\r
33064      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.\r
33065      * @param {Object} this\r
33066      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
33067      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy\r
33068      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data\r
33069      */\r
33070 \r
33071        /**\r
33072      * MemoryProxy implementation of DataProxy#doRequest\r
33073      * @param {String} action\r
33074      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
33075      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
33076      * for the request to the remote server.\r
33077      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
33078      * object into a block of Ext.data.Records.\r
33079      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
33080      * The function must be passed <ul>\r
33081      * <li>The Record block object</li>\r
33082      * <li>The "arg" argument from the load function</li>\r
33083      * <li>A boolean success indicator</li>\r
33084      * </ul>\r
33085      * @param {Object} scope The scope in which to call the callback\r
33086      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
33087      */\r
33088     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
33089         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'\r
33090         params = params || {};\r
33091         var result;\r
33092         try {\r
33093             result = reader.readRecords(this.data);\r
33094         }catch(e){\r
33095             // @deprecated loadexception\r
33096             this.fireEvent("loadexception", this, null, arg, e);\r
33097 \r
33098             this.fireEvent('exception', this, 'response', action, arg, null, e);\r
33099             callback.call(scope, null, arg, false);\r
33100             return;\r
33101         }\r
33102         callback.call(scope, result, arg, true);\r
33103     }\r
33104 });/**
33105  * @class Ext.data.JsonWriter
33106  * @extends Ext.data.DataWriter
33107  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
33108  */
33109 Ext.data.JsonWriter = function(config) {
33110     Ext.data.JsonWriter.superclass.constructor.call(this, config);
33111     // careful to respect "returnJson", renamed to "encode"
33112     if (this.returnJson != undefined) {
33113         this.encode = this.returnJson;
33114     }
33115 }
33116 Ext.extend(Ext.data.JsonWriter, Ext.data.DataWriter, {
33117     /**
33118      * @cfg {Boolean} returnJson <b>Deprecated.  Use {@link Ext.data.JsonWriter#encode} instead.
33119      */
33120     returnJson : undefined,
33121     /**
33122      * @cfg {Boolean} encode <tt>true</tt> to {@link Ext.util.JSON#encode encode} the
33123      * {@link Ext.data.DataWriter#toHash hashed data}. Defaults to <tt>true</tt>.  When using
33124      * {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
33125      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
33126      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
33127      * instead of <b>params</b>.  When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
33128      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>
33129      */
33130     encode : true,
33131
33132     /**
33133      * Final action of a write event.  Apply the written data-object to params.
33134      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
33135      * @param {Record[]} rs
33136      * @param {Object} http params
33137      * @param {Object} data object populated according to DataReader meta-data "root" and "idProperty"
33138      */
33139     render : function(action, rs, params, data) {
33140         Ext.apply(params, data);
33141
33142         if (this.encode === true) { // <-- @deprecated returnJson
33143             if (Ext.isArray(rs) && data[this.meta.idProperty]) {
33144                 params[this.meta.idProperty] = Ext.encode(params[this.meta.idProperty]);
33145             }
33146             params[this.meta.root] = Ext.encode(params[this.meta.root]);
33147         }
33148     },
33149     /**
33150      * createRecord
33151      * @protected
33152      * @param {Ext.data.Record} rec
33153      */
33154     createRecord : function(rec) {
33155         return this.toHash(rec);
33156     },
33157     /**
33158      * updateRecord
33159      * @protected
33160      * @param {Ext.data.Record} rec
33161      */
33162     updateRecord : function(rec) {
33163         return this.toHash(rec);
33164
33165     },
33166     /**
33167      * destroyRecord
33168      * @protected
33169      * @param {Ext.data.Record} rec
33170      */
33171     destroyRecord : function(rec) {
33172         return rec.id;
33173     }
33174 });/**
33175  * @class Ext.data.JsonReader
33176  * @extends Ext.data.DataReader
33177  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from a JSON response
33178  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
33179  * <p>Example code:</p>
33180  * <pre><code>
33181 var Employee = Ext.data.Record.create([
33182     {name: 'firstname'},                  // map the Record's "firstname" field to the row object's key of the same name
33183     {name: 'job', mapping: 'occupation'}  // map the Record's "job" field to the row object's "occupation" key
33184 ]);
33185 var myReader = new Ext.data.JsonReader(
33186     {                             // The metadata property, with configuration options:
33187         totalProperty: "results", //   the property which contains the total dataset size (optional)
33188         root: "rows",             //   the property which contains an Array of record data objects
33189         idProperty: "id"          //   the property within each row object that provides an ID for the record (optional)
33190     },
33191     Employee  // {@link Ext.data.Record} constructor that provides mapping for JSON object
33192 );
33193 </code></pre>
33194  * <p>This would consume a JSON data object of the form:</p><pre><code>
33195 {
33196     results: 2,  // Reader's configured totalProperty
33197     rows: [      // Reader's configured root
33198         { id: 1, firstname: 'Bill', occupation: 'Gardener' },         // a row object
33199         { id: 2, firstname: 'Ben' , occupation: 'Horticulturalist' }  // another row object
33200     ]
33201 }
33202 </code></pre>
33203  * <p><b><u>Automatic configuration using metaData</u></b></p>
33204  * <p>It is possible to change a JsonReader's metadata at any time by including a <b><tt>metaData</tt></b>
33205  * property in the JSON data object. If the JSON data object has a <b><tt>metaData</tt></b> property, a
33206  * {@link Ext.data.Store Store} object using this Reader will reconfigure itself to use the newly provided
33207  * field definition and fire its {@link Ext.data.Store#metachange metachange} event. The metachange event
33208  * handler may interrogate the <b><tt>metaData</tt></b> property to perform any configuration required.
33209  * Note that reconfiguring a Store potentially invalidates objects which may refer to Fields or Records
33210  * which no longer exist.</p>
33211  * <p>The <b><tt>metaData</tt></b> property in the JSON data object may contain:</p>
33212  * <div class="mdetail-params"><ul>
33213  * <li>any of the configuration options for this class</li>
33214  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which the JsonReader will
33215  * use as an argument to the {@link Ext.data.Record#create data Record create method} in order to
33216  * configure the layout of the Records it will produce.</li>
33217  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property which the JsonReader will
33218  * use to set the {@link Ext.data.Store}'s {@link Ext.data.Store#sortInfo sortInfo} property</li>
33219  * <li>any user-defined properties needed</li>
33220  * </ul></div>
33221  * <p>To use this facility to send the same data as the example above (without having to code the creation
33222  * of the Record constructor), you would create the JsonReader like this:</p><pre><code>
33223 var myReader = new Ext.data.JsonReader();
33224 </code></pre>
33225  * <p>The first data packet from the server would configure the reader by containing a
33226  * <b><tt>metaData</tt></b> property <b>and</b> the data. For example, the JSON data object might take
33227  * the form:</p>
33228 <pre><code>
33229 {
33230     metaData: {
33231         idProperty: 'id',
33232         root: 'rows',
33233         totalProperty: 'results',
33234         fields: [
33235             {name: 'name'},
33236             {name: 'job', mapping: 'occupation'}
33237         ],
33238         sortInfo: {field: 'name', direction:'ASC'}, // used by store to set its sortInfo
33239         foo: 'bar' // custom property
33240     },
33241     results: 2,
33242     rows: [ // an Array
33243         { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
33244         { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' }
33245     ]
33246 }
33247 </code></pre>
33248  * @cfg {String} totalProperty [total] Name of the property from which to retrieve the total number of records
33249  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
33250  * paged from the remote server.  Defaults to <tt>total</tt>.
33251  * @cfg {String} successProperty [success] Name of the property from which to
33252  * retrieve the success attribute. Defaults to <tt>success</tt>.  See
33253  * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
33254  * for additional information.
33255  * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
33256  * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
33257  * An exception will be thrown if the root property is undefined. The data packet
33258  * value for this property should be an empty array to clear the data or show
33259  * no data.
33260  * @cfg {String} idProperty [id] Name of the property within a row object that contains a record identifier value.  Defaults to <tt>id</tt>
33261  * @constructor
33262  * Create a new JsonReader
33263  * @param {Object} meta Metadata configuration options.
33264  * @param {Array/Object} recordType
33265  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
33266  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
33267  * constructor created from {@link Ext.data.Record#create}.</p>
33268  */
33269 Ext.data.JsonReader = function(meta, recordType){
33270     meta = meta || {};
33271
33272     // default idProperty, successProperty & totalProperty -> "id", "success", "total"
33273     Ext.applyIf(meta, {
33274         idProperty: 'id',
33275         successProperty: 'success',
33276         totalProperty: 'total'
33277     });
33278
33279     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
33280 };
33281 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
33282     /**
33283      * This JsonReader's metadata as passed to the constructor, or as passed in
33284      * the last data packet's <b><tt>metaData</tt></b> property.
33285      * @type Mixed
33286      * @property meta
33287      */
33288     /**
33289      * This method is only used by a DataProxy which has retrieved data from a remote server.
33290      * @param {Object} response The XHR object which contains the JSON data in its responseText.
33291      * @return {Object} data A data block which is used by an Ext.data.Store object as
33292      * a cache of Ext.data.Records.
33293      */
33294     read : function(response){
33295         var json = response.responseText;
33296         var o = Ext.decode(json);
33297         if(!o) {
33298             throw {message: "JsonReader.read: Json object not found"};
33299         }
33300         return this.readRecords(o);
33301     },
33302
33303     // private function a store will implement
33304     onMetaChange : function(meta, recordType, o){
33305
33306     },
33307
33308     /**
33309      * @ignore
33310      */
33311     simpleAccess: function(obj, subsc) {
33312         return obj[subsc];
33313     },
33314
33315     /**
33316      * @ignore
33317      */
33318     getJsonAccessor: function(){
33319         var re = /[\[\.]/;
33320         return function(expr) {
33321             try {
33322                 return(re.test(expr)) ?
33323                 new Function("obj", "return obj." + expr) :
33324                 function(obj){
33325                     return obj[expr];
33326                 };
33327             } catch(e){}
33328             return Ext.emptyFn;
33329         };
33330     }(),
33331
33332     /**
33333      * Create a data block containing Ext.data.Records from a JSON object.
33334      * @param {Object} o An object which contains an Array of row objects in the property specified
33335      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
33336      * which contains the total size of the dataset.
33337      * @return {Object} data A data block which is used by an Ext.data.Store object as
33338      * a cache of Ext.data.Records.
33339      */
33340     readRecords : function(o){
33341         /**
33342          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
33343          * loaded or there is a load exception this property will be undefined.
33344          * @type Object
33345          */
33346         this.jsonData = o;
33347         if(o.metaData){
33348             delete this.ef;
33349             this.meta = o.metaData;
33350             this.recordType = Ext.data.Record.create(o.metaData.fields);
33351             this.onMetaChange(this.meta, this.recordType, o);
33352         }
33353         var s = this.meta, Record = this.recordType,
33354             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
33355
33356         // Generate extraction functions for the totalProperty, the root, the id, and for each field
33357         this.buildExtractors();
33358         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
33359         if(s.totalProperty){
33360             v = parseInt(this.getTotal(o), 10);
33361             if(!isNaN(v)){
33362                 totalRecords = v;
33363             }
33364         }
33365         if(s.successProperty){
33366             v = this.getSuccess(o);
33367             if(v === false || v === 'false'){
33368                 success = false;
33369             }
33370         }
33371
33372         var records = [];
33373         for(var i = 0; i < c; i++){
33374             var n = root[i];
33375             var record = new Record(this.extractValues(n, fi, fl), this.getId(n));
33376             record.json = n;
33377             records[i] = record;
33378         }
33379         return {
33380             success : success,
33381             records : records,
33382             totalRecords : totalRecords
33383         };
33384     },
33385
33386     // private
33387     buildExtractors : function() {
33388         if(this.ef){
33389             return;
33390         }
33391         var s = this.meta, Record = this.recordType,
33392             f = Record.prototype.fields, fi = f.items, fl = f.length;
33393
33394         if(s.totalProperty) {
33395             this.getTotal = this.getJsonAccessor(s.totalProperty);
33396         }
33397         if(s.successProperty) {
33398             this.getSuccess = this.getJsonAccessor(s.successProperty);
33399         }
33400         this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
33401         if (s.id || s.idProperty) {
33402             var g = this.getJsonAccessor(s.id || s.idProperty);
33403             this.getId = function(rec) {
33404                 var r = g(rec);
33405                 return (r === undefined || r === "") ? null : r;
33406             };
33407         } else {
33408             this.getId = function(){return null;};
33409         }
33410         var ef = [];
33411         for(var i = 0; i < fl; i++){
33412             f = fi[i];
33413             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
33414             ef.push(this.getJsonAccessor(map));
33415         }
33416         this.ef = ef;
33417     },
33418
33419     // private extractValues
33420     extractValues: function(data, items, len) {
33421         var f, values = {};
33422         for(var j = 0; j < len; j++){
33423             f = items[j];
33424             var v = this.ef[j](data);
33425             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
33426         }
33427         return values;
33428     },
33429
33430     /**
33431      * Decode a json response from server.
33432      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
33433      * @param {Object} response
33434      */
33435     readResponse : function(action, response) {
33436         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
33437         if(!o) {
33438             throw new Ext.data.JsonReader.Error('response');
33439         }
33440         if (Ext.isEmpty(o[this.meta.successProperty])) {
33441             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
33442         }
33443         // TODO, separate empty and undefined exceptions.
33444         if ((action === Ext.data.Api.actions.create || action === Ext.data.Api.actions.update)) {
33445             if (Ext.isEmpty(o[this.meta.root])) {
33446                 throw new Ext.data.JsonReader.Error('root-emtpy', this.meta.root);
33447             }
33448             else if (o[this.meta.root] === undefined) {
33449                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
33450             }
33451         }
33452         // make sure extraction functions are defined.
33453         this.ef = this.buildExtractors();
33454         return o;
33455     }
33456 });
33457
33458 /**
33459  * @class Ext.data.JsonReader.Error
33460  * Error class for JsonReader
33461  */
33462 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
33463     constructor : function(message, arg) {
33464         this.arg = arg;
33465         Ext.Error.call(this, message);
33466     },
33467     name : 'Ext.data.JsonReader'
33468 });
33469 Ext.apply(Ext.data.JsonReader.Error.prototype, {
33470     lang: {
33471         'response': "An error occurred while json-decoding your server response",
33472         '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.',
33473         'root-undefined-response': 'Could not locate your "root" property in your server response.  Please review your JsonReader config to ensure the config-property "root" matches the property your server-response.  See the JsonReader docs.',
33474         '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.',
33475         '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.',
33476         'root-emtpy': '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.'
33477     }
33478 });
33479 /**
33480  * @class Ext.data.ArrayReader
33481  * @extends Ext.data.JsonReader
33482  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
33483  * Each element of that Array represents a row of data fields. The
33484  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
33485  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
33486  * <p>Example code:</p>
33487  * <pre><code>
33488 var Employee = Ext.data.Record.create([
33489     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
33490     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
33491 ]);
33492 var myReader = new Ext.data.ArrayReader({
33493     {@link #idIndex}: 0
33494 }, Employee);
33495 </code></pre>
33496  * <p>This would consume an Array like this:</p>
33497  * <pre><code>
33498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
33499  * </code></pre>
33500  * @constructor
33501  * Create a new ArrayReader
33502  * @param {Object} meta Metadata configuration options.
33503  * @param {Array/Object} recordType
33504  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
33505  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
33506  * constructor created from {@link Ext.data.Record#create}.</p>
33507  */
33508 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
33509     /**
33510      * @cfg {String} successProperty
33511      * @hide
33512      */
33513     /**
33514      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
33515      * Deprecated. Use {@link #idIndex} instead.
33516      */
33517     /**
33518      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
33519      */
33520     /**
33521      * Create a data block containing Ext.data.Records from an Array.
33522      * @param {Object} o An Array of row objects which represents the dataset.
33523      * @return {Object} data A data block which is used by an Ext.data.Store object as
33524      * a cache of Ext.data.Records.
33525      */
33526     readRecords : function(o){
33527         this.arrayData = o;
33528         var s = this.meta,
33529             sid = s ? Ext.num(s.idIndex, s.id) : null,
33530             recordType = this.recordType, 
33531             fields = recordType.prototype.fields,
33532             records = [],
33533             v;
33534
33535         if(!this.getRoot) {
33536             this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p) {return p;};
33537             if(s.totalProperty) {
33538                 this.getTotal = this.getJsonAccessor(s.totalProperty);
33539             }
33540         }
33541
33542         var root = this.getRoot(o);
33543
33544         for(var i = 0; i < root.length; i++) {
33545             var n = root[i];
33546             var values = {};
33547             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
33548             for(var j = 0, jlen = fields.length; j < jlen; j++) {
33549                 var f = fields.items[j];
33550                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
33551                 v = n[k] !== undefined ? n[k] : f.defaultValue;
33552                 v = f.convert(v, n);
33553                 values[f.name] = v;
33554             }
33555             var record = new recordType(values, id);
33556             record.json = n;
33557             records[records.length] = record;
33558         }
33559
33560         var totalRecords = records.length;
33561
33562         if(s.totalProperty) {
33563             v = parseInt(this.getTotal(o), 10);
33564             if(!isNaN(v)) {
33565                 totalRecords = v;
33566             }
33567         }
33568
33569         return {
33570             records : records,
33571             totalRecords : totalRecords
33572         };
33573     }
33574 });/**
33575  * @class Ext.data.ArrayStore
33576  * @extends Ext.data.Store
33577  * <p>Formerly known as "SimpleStore".</p>
33578  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
33579  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
33580  * <p>A store configuration would be something like:<pre><code>
33581 var store = new Ext.data.ArrayStore({
33582     // store configs
33583     autoDestroy: true,
33584     storeId: 'myStore',
33585     // reader configs
33586     idIndex: 0,  
33587     fields: [
33588        'company',
33589        {name: 'price', type: 'float'},
33590        {name: 'change', type: 'float'},
33591        {name: 'pctChange', type: 'float'},
33592        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
33593     ]
33594 });
33595  * </code></pre></p>
33596  * <p>This store is configured to consume a returned object of the form:<pre><code>
33597 var myData = [
33598     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
33599     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
33600     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
33601     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
33602     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
33603 ];
33604  * </code></pre>
33605  * An object literal of this form could also be used as the {@link #data} config option.</p>
33606  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
33607  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
33608  * @constructor
33609  * @param {Object} config
33610  * @xtype arraystore
33611  */
33612 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
33613     /**
33614      * @cfg {Ext.data.DataReader} reader @hide
33615      */
33616     constructor: function(config){
33617         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
33618             reader: new Ext.data.ArrayReader(config)
33619         }));
33620     },
33621
33622     loadData : function(data, append){
33623         if(this.expandData === true){
33624             var r = [];
33625             for(var i = 0, len = data.length; i < len; i++){
33626                 r[r.length] = [data[i]];
33627             }
33628             data = r;
33629         }
33630         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
33631     }
33632 });
33633 Ext.reg('arraystore', Ext.data.ArrayStore);
33634
33635 // backwards compat
33636 Ext.data.SimpleStore = Ext.data.ArrayStore;
33637 Ext.reg('simplestore', Ext.data.SimpleStore);/**
33638  * @class Ext.data.JsonStore
33639  * @extends Ext.data.Store
33640  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
33641  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
33642  * <p>A store configuration would be something like:<pre><code>
33643 var store = new Ext.data.JsonStore({
33644     // store configs
33645     autoDestroy: true,
33646     url: 'get-images.php',
33647     storeId: 'myStore',
33648     // reader configs
33649     root: 'images',
33650     idProperty: 'name',  
33651     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
33652 });
33653  * </code></pre></p>
33654  * <p>This store is configured to consume a returned object of the form:<pre><code>
33655 {
33656     images: [
33657         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
33658         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
33659     ]
33660 }
33661  * </code></pre>
33662  * An object literal of this form could also be used as the {@link #data} config option.</p>
33663  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
33664  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
33665  * @constructor
33666  * @param {Object} config
33667  * @xtype jsonstore
33668  */
33669 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
33670     /**
33671      * @cfg {Ext.data.DataReader} reader @hide
33672      */
33673     constructor: function(config){
33674         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
33675             reader: new Ext.data.JsonReader(config)
33676         }));
33677     }
33678 });
33679 Ext.reg('jsonstore', Ext.data.JsonStore);/**
33680  * @class Ext.data.XmlWriter
33681  * @extends Ext.data.DataWriter
33682  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
33683  */
33684 Ext.data.XmlWriter = Ext.extend(Ext.data.DataWriter, {
33685     /**
33686      * Final action of a write event.  Apply the written data-object to params.
33687      * @param {String} action [Ext.data.Api.create|read|update|destroy]
33688      * @param {Record[]} rs
33689      * @param {Object} http params
33690      * @param {Object} data object populated according to DataReader meta-data "root" and "idProperty"
33691      */
33692     render : function(action, rs, params, data) {
33693         // no impl.
33694     },
33695     /**
33696      * createRecord
33697      * @param {Ext.data.Record} rec
33698      */
33699     createRecord : function(rec) {
33700         // no impl
33701     },
33702     /**
33703      * updateRecord
33704      * @param {Ext.data.Record} rec
33705      */
33706     updateRecord : function(rec) {
33707         // no impl.
33708
33709     },
33710     /**
33711      * destroyRecord
33712      * @param {Ext.data.Record} rec
33713      */
33714     destroyRecord : function(rec) {
33715         // no impl
33716     }
33717 });/**
33718  * @class Ext.data.XmlReader
33719  * @extends Ext.data.DataReader
33720  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
33721  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
33722  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
33723  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
33724  * <p>Example code:</p>
33725  * <pre><code>
33726 var Employee = Ext.data.Record.create([
33727    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
33728    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
33729 ]);
33730 var myReader = new Ext.data.XmlReader({
33731    totalRecords: "results", // The element which contains the total dataset size (optional)
33732    record: "row",           // The repeated element which contains row information
33733    id: "id"                 // The element within the row that provides an ID for the record (optional)
33734 }, Employee);
33735 </code></pre>
33736  * <p>
33737  * This would consume an XML file like this:
33738  * <pre><code>
33739 &lt;?xml version="1.0" encoding="UTF-8"?>
33740 &lt;dataset>
33741  &lt;results>2&lt;/results>
33742  &lt;row>
33743    &lt;id>1&lt;/id>
33744    &lt;name>Bill&lt;/name>
33745    &lt;occupation>Gardener&lt;/occupation>
33746  &lt;/row>
33747  &lt;row>
33748    &lt;id>2&lt;/id>
33749    &lt;name>Ben&lt;/name>
33750    &lt;occupation>Horticulturalist&lt;/occupation>
33751  &lt;/row>
33752 &lt;/dataset>
33753 </code></pre>
33754  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
33755  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
33756  * paged from the remote server.
33757  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
33758  * @cfg {String} success The DomQuery path to the success attribute used by forms.
33759  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
33760  * a record identifier value.
33761  * @constructor
33762  * Create a new XmlReader.
33763  * @param {Object} meta Metadata configuration options
33764  * @param {Object} recordType Either an Array of field definition objects as passed to
33765  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
33766  */
33767 Ext.data.XmlReader = function(meta, recordType){
33768     meta = meta || {};
33769     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
33770 };
33771 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
33772     /**
33773      * This method is only used by a DataProxy which has retrieved data from a remote server.
33774      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
33775      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
33776      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
33777      * a cache of Ext.data.Records.
33778      */
33779     read : function(response){
33780         var doc = response.responseXML;
33781         if(!doc) {
33782             throw {message: "XmlReader.read: XML Document not available"};
33783         }
33784         return this.readRecords(doc);
33785     },
33786
33787     /**
33788      * Create a data block containing Ext.data.Records from an XML document.
33789      * @param {Object} doc A parsed XML document.
33790      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
33791      * a cache of Ext.data.Records.
33792      */
33793     readRecords : function(doc){
33794         /**
33795          * After any data loads/reads, the raw XML Document is available for further custom processing.
33796          * @type XMLDocument
33797          */
33798         this.xmlData = doc;
33799         var root = doc.documentElement || doc;
33800         var q = Ext.DomQuery;
33801         var recordType = this.recordType, fields = recordType.prototype.fields;
33802         var sid = this.meta.idPath || this.meta.id;
33803         var totalRecords = 0, success = true;
33804         if(this.meta.totalRecords){
33805             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
33806         }
33807
33808         if(this.meta.success){
33809             var sv = q.selectValue(this.meta.success, root, true);
33810             success = sv !== false && sv !== 'false';
33811         }
33812         var records = [];
33813         var ns = q.select(this.meta.record, root);
33814         for(var i = 0, len = ns.length; i < len; i++) {
33815             var n = ns[i];
33816             var values = {};
33817             var id = sid ? q.selectValue(sid, n) : undefined;
33818             for(var j = 0, jlen = fields.length; j < jlen; j++){
33819                 var f = fields.items[j];
33820                 var v = q.selectValue(Ext.value(f.mapping, f.name, true), n, f.defaultValue);
33821                 v = f.convert(v, n);
33822                 values[f.name] = v;
33823             }
33824             var record = new recordType(values, id);
33825             record.node = n;
33826             records[records.length] = record;
33827         }
33828
33829         return {
33830             success : success,
33831             records : records,
33832             totalRecords : totalRecords || records.length
33833         };
33834     },
33835
33836     // TODO: implement readResponse for XmlReader
33837     readResponse : Ext.emptyFn
33838 });/**\r
33839  * @class Ext.data.XmlStore\r
33840  * @extends Ext.data.Store\r
33841  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.\r
33842  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>\r
33843  * <p>A store configuration would be something like:<pre><code>\r
33844 var store = new Ext.data.XmlStore({\r
33845     // store configs\r
33846     autoDestroy: true,\r
33847     storeId: 'myStore',\r
33848     url: 'sheldon.xml', // automatically configures a HttpProxy\r
33849     // reader configs\r
33850     record: 'Item', // records will have an "Item" tag\r
33851     idPath: 'ASIN',\r
33852     totalRecords: '@TotalResults'\r
33853     fields: [\r
33854         // set up the fields mapping into the xml doc\r
33855         // The first needs mapping, the others are very basic\r
33856         {name: 'Author', mapping: 'ItemAttributes > Author'},\r
33857         'Title', 'Manufacturer', 'ProductGroup'\r
33858     ]\r
33859 });\r
33860  * </code></pre></p>\r
33861  * <p>This store is configured to consume a returned object of the form:<pre><code>\r
33862 &#60?xml version="1.0" encoding="UTF-8"?>\r
33863 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">\r
33864     &#60Items>\r
33865         &#60Request>\r
33866             &#60IsValid>True&#60/IsValid>\r
33867             &#60ItemSearchRequest>\r
33868                 &#60Author>Sidney Sheldon&#60/Author>\r
33869                 &#60SearchIndex>Books&#60/SearchIndex>\r
33870             &#60/ItemSearchRequest>\r
33871         &#60/Request>\r
33872         &#60TotalResults>203&#60/TotalResults>\r
33873         &#60TotalPages>21&#60/TotalPages>\r
33874         &#60Item>\r
33875             &#60ASIN>0446355453&#60/ASIN>\r
33876             &#60DetailPageURL>\r
33877                 http://www.amazon.com/\r
33878             &#60/DetailPageURL>\r
33879             &#60ItemAttributes>\r
33880                 &#60Author>Sidney Sheldon&#60/Author>\r
33881                 &#60Manufacturer>Warner Books&#60/Manufacturer>\r
33882                 &#60ProductGroup>Book&#60/ProductGroup>\r
33883                 &#60Title>Master of the Game&#60/Title>\r
33884             &#60/ItemAttributes>\r
33885         &#60/Item>\r
33886     &#60/Items>\r
33887 &#60/ItemSearchResponse>\r
33888  * </code></pre>\r
33889  * An object literal of this form could also be used as the {@link #data} config option.</p>\r
33890  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of \r
33891  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>\r
33892  * @constructor\r
33893  * @param {Object} config\r
33894  * @xtype xmlstore\r
33895  */\r
33896 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {\r
33897     /**\r
33898      * @cfg {Ext.data.DataReader} reader @hide\r
33899      */\r
33900     constructor: function(config){\r
33901         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {\r
33902             reader: new Ext.data.XmlReader(config)\r
33903         }));\r
33904     }\r
33905 });\r
33906 Ext.reg('xmlstore', Ext.data.XmlStore);/**\r
33907  * @class Ext.data.GroupingStore\r
33908  * @extends Ext.data.Store\r
33909  * A specialized store implementation that provides for grouping records by one of the available fields. This\r
33910  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to proved the data model for\r
33911  * a grouped GridPanel.\r
33912  * @constructor\r
33913  * Creates a new GroupingStore.\r
33914  * @param {Object} config A config object containing the objects needed for the Store to access data,\r
33915  * and read the data into Records.\r
33916  * @xtype groupingstore\r
33917  */\r
33918 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {\r
33919     \r
33920     //inherit docs\r
33921     constructor: function(config){\r
33922         Ext.data.GroupingStore.superclass.constructor.call(this, config);\r
33923         this.applyGroupField();\r
33924     },\r
33925     \r
33926     /**\r
33927      * @cfg {String} groupField\r
33928      * The field name by which to sort the store's data (defaults to '').\r
33929      */\r
33930     /**\r
33931      * @cfg {Boolean} remoteGroup\r
33932      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the\r
33933      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a\r
33934      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.\r
33935      */\r
33936     remoteGroup : false,\r
33937     /**\r
33938      * @cfg {Boolean} groupOnSort\r
33939      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the\r
33940      * existing sort info (defaults to false).\r
33941      */\r
33942     groupOnSort:false,\r
33943 \r
33944         groupDir : 'ASC',\r
33945         \r
33946     /**\r
33947      * Clears any existing grouping and refreshes the data using the default sort.\r
33948      */\r
33949     clearGrouping : function(){\r
33950         this.groupField = false;\r
33951         if(this.remoteGroup){\r
33952             if(this.baseParams){\r
33953                 delete this.baseParams.groupBy;\r
33954             }\r
33955             var lo = this.lastOptions;\r
33956             if(lo && lo.params){\r
33957                 delete lo.params.groupBy;\r
33958             }\r
33959             this.reload();\r
33960         }else{\r
33961             this.applySort();\r
33962             this.fireEvent('datachanged', this);\r
33963         }\r
33964     },\r
33965 \r
33966     /**\r
33967      * Groups the data by the specified field.\r
33968      * @param {String} field The field name by which to sort the store's data\r
33969      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed\r
33970      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)\r
33971      */\r
33972     groupBy : function(field, forceRegroup, direction){\r
33973                 direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;\r
33974         if(this.groupField == field && this.groupDir == direction && !forceRegroup){\r
33975             return; // already grouped by this field\r
33976         }\r
33977         this.groupField = field;\r
33978                 this.groupDir = direction;\r
33979         this.applyGroupField();\r
33980         if(this.groupOnSort){\r
33981             this.sort(field, direction);\r
33982             return;\r
33983         }\r
33984         if(this.remoteGroup){\r
33985             this.reload();\r
33986         }else{\r
33987             var si = this.sortInfo || {};\r
33988             if(si.field != field || si.direction != direction){\r
33989                 this.applySort();\r
33990             }else{\r
33991                 this.sortData(field, direction);\r
33992             }\r
33993             this.fireEvent('datachanged', this);\r
33994         }\r
33995     },\r
33996     \r
33997     // private\r
33998     applyGroupField: function(){\r
33999         if(this.remoteGroup){\r
34000             if(!this.baseParams){\r
34001                 this.baseParams = {};\r
34002             }\r
34003             this.baseParams.groupBy = this.groupField;\r
34004             this.baseParams.groupDir = this.groupDir;\r
34005         }\r
34006     },\r
34007 \r
34008     // private\r
34009     applySort : function(){\r
34010         Ext.data.GroupingStore.superclass.applySort.call(this);\r
34011         if(!this.groupOnSort && !this.remoteGroup){\r
34012             var gs = this.getGroupState();\r
34013             if(gs && (gs != this.sortInfo.field || this.groupDir != this.sortInfo.direction)){\r
34014                 this.sortData(this.groupField, this.groupDir);\r
34015             }\r
34016         }\r
34017     },\r
34018 \r
34019     // private\r
34020     applyGrouping : function(alwaysFireChange){\r
34021         if(this.groupField !== false){\r
34022             this.groupBy(this.groupField, true, this.groupDir);\r
34023             return true;\r
34024         }else{\r
34025             if(alwaysFireChange === true){\r
34026                 this.fireEvent('datachanged', this);\r
34027             }\r
34028             return false;\r
34029         }\r
34030     },\r
34031 \r
34032     // private\r
34033     getGroupState : function(){\r
34034         return this.groupOnSort && this.groupField !== false ?\r
34035                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;\r
34036     }\r
34037 });\r
34038 Ext.reg('groupingstore', Ext.data.GroupingStore);/**\r
34039  * @class Ext.data.DirectProxy\r
34040  * @extends Ext.data.DataProxy\r
34041  */\r
34042 Ext.data.DirectProxy = function(config){\r
34043     Ext.apply(this, config);\r
34044     if(typeof this.paramOrder == 'string'){\r
34045         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
34046     }\r
34047     Ext.data.DirectProxy.superclass.constructor.call(this, config);\r
34048 };\r
34049 \r
34050 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {\r
34051     /**\r
34052      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed\r
34053      * server side.  Specify the params in the order in which they must be executed on the server-side\r
34054      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
34055      * comma, or pipe. For example,\r
34056      * any of the following would be acceptable:<pre><code>\r
34057 paramOrder: ['param1','param2','param3']\r
34058 paramOrder: 'param1 param2 param3'\r
34059 paramOrder: 'param1,param2,param3'\r
34060 paramOrder: 'param1|param2|param'\r
34061      </code></pre>\r
34062      */\r
34063     paramOrder: undefined,\r
34064 \r
34065     /**\r
34066      * @cfg {Boolean} paramsAsHash\r
34067      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a\r
34068      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
34069      */\r
34070     paramsAsHash: true,\r
34071 \r
34072     /**\r
34073      * @cfg {Function} directFn\r
34074      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter\r
34075      * for Store's which will not implement a full CRUD api.\r
34076      */\r
34077     directFn : undefined,\r
34078 \r
34079     // protected\r
34080     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
34081         var args = [];\r
34082         var directFn = this.api[action] || this.directFn;\r
34083 \r
34084         switch (action) {\r
34085             case Ext.data.Api.actions.create:\r
34086                 args.push(params[reader.meta.root]);            // <-- create(Hash)\r
34087                 break;\r
34088             case Ext.data.Api.actions.read:\r
34089                 if(this.paramOrder){\r
34090                     for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
34091                         args.push(params[this.paramOrder[i]]);\r
34092                     }\r
34093                 }else if(this.paramsAsHash){\r
34094                     args.push(params);\r
34095                 }\r
34096                 break;\r
34097             case Ext.data.Api.actions.update:\r
34098                 args.push(params[reader.meta.idProperty]);  // <-- save(Integer/Integer[], Hash/Hash[])\r
34099                 args.push(params[reader.meta.root]);\r
34100                 break;\r
34101             case Ext.data.Api.actions.destroy:\r
34102                 args.push(params[reader.meta.root]);        // <-- destroy(Int/Int[])\r
34103                 break;\r
34104         }\r
34105 \r
34106         var trans = {\r
34107             params : params || {},\r
34108             callback : callback,\r
34109             scope : scope,\r
34110             arg : options,\r
34111             reader: reader\r
34112         };\r
34113 \r
34114         args.push(this.createCallback(action, rs, trans), this);\r
34115         directFn.apply(window, args);\r
34116     },\r
34117 \r
34118     // private\r
34119     createCallback : function(action, rs, trans) {\r
34120         return function(result, res) {\r
34121             if (!res.status) {\r
34122                 // @deprecated fire loadexception\r
34123                 if (action === Ext.data.Api.actions.read) {\r
34124                     this.fireEvent("loadexception", this, trans, res, null);\r
34125                 }\r
34126                 this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
34127                 trans.callback.call(trans.scope, null, trans.arg, false);\r
34128                 return;\r
34129             }\r
34130             if (action === Ext.data.Api.actions.read) {\r
34131                 this.onRead(action, trans, result, res);\r
34132             } else {\r
34133                 this.onWrite(action, trans, result, res, rs);\r
34134             }\r
34135         };\r
34136     },\r
34137     /**\r
34138      * Callback for read actions\r
34139      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
34140      * @param {Object} trans The request transaction object\r
34141      * @param {Object} res The server response\r
34142      * @private\r
34143      */\r
34144     onRead : function(action, trans, result, res) {\r
34145         var records;\r
34146         try {\r
34147             records = trans.reader.readRecords(result);\r
34148         }\r
34149         catch (ex) {\r
34150             // @deprecated: Fire old loadexception for backwards-compat.\r
34151             this.fireEvent("loadexception", this, trans, res, ex);\r
34152 \r
34153             this.fireEvent('exception', this, 'response', action, trans, res, ex);\r
34154             trans.callback.call(trans.scope, null, trans.arg, false);\r
34155             return;\r
34156         }\r
34157         this.fireEvent("load", this, res, trans.arg);\r
34158         trans.callback.call(trans.scope, records, trans.arg, true);\r
34159     },\r
34160     /**\r
34161      * Callback for write actions\r
34162      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
34163      * @param {Object} trans The request transaction object\r
34164      * @param {Object} res The server response\r
34165      * @private\r
34166      */\r
34167     onWrite : function(action, trans, result, res, rs) {\r
34168         this.fireEvent("write", this, action, result, res, rs, trans.arg);\r
34169         trans.callback.call(trans.scope, result, res, true);\r
34170     }\r
34171 });\r
34172 \r
34173 /**\r
34174  * @class Ext.data.DirectStore\r
34175  * @extends Ext.data.Store\r
34176  * <p>Small helper class to create an {@link Ext.data.Store} configured with an\r
34177  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting\r
34178  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.\r
34179  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}\r
34180  * configured as needed.</p>\r
34181  *\r
34182  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>\r
34183  * <div><ul class="mdetail-params">\r
34184  * <li><b>{@link Ext.data.Store Store}</b></li>\r
34185  * <div class="sub-desc"><ul class="mdetail-params">\r
34186  *\r
34187  * </ul></div>\r
34188  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>\r
34189  * <div class="sub-desc"><ul class="mdetail-params">\r
34190  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>\r
34191  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>\r
34192  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>\r
34193  * </ul></div>\r
34194  *\r
34195  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>\r
34196  * <div class="sub-desc"><ul class="mdetail-params">\r
34197  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>\r
34198  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>\r
34199  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>\r
34200  * </ul></div>\r
34201  * </ul></div>\r
34202  *\r
34203  * @xtype directstore\r
34204  *\r
34205  * @constructor\r
34206  * @param {Object} config\r
34207  */\r
34208 Ext.data.DirectStore = function(c){\r
34209     // each transaction upon a singe record will generatie a distinct Direct transaction since Direct queues them into one Ajax request.\r
34210     c.batchTransactions = false;\r
34211 \r
34212     Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {\r
34213         proxy: (typeof(c.proxy) == 'undefined') ? new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')) : c.proxy,\r
34214         reader: (typeof(c.reader) == 'undefined' && typeof(c.fields) == 'object') ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader\r
34215     }));\r
34216 };\r
34217 Ext.extend(Ext.data.DirectStore, Ext.data.Store, {});\r
34218 Ext.reg('directstore', Ext.data.DirectStore);
34219 /**\r
34220  * @class Ext.Direct\r
34221  * @extends Ext.util.Observable\r
34222  * <p><b><u>Overview</u></b></p>\r
34223  * \r
34224  * <p>Ext.Direct aims to streamline communication between the client and server\r
34225  * by providing a single interface that reduces the amount of common code\r
34226  * typically required to validate data and handle returned data packets\r
34227  * (reading data, error conditions, etc).</p>\r
34228  *  \r
34229  * <p>The Ext.direct namespace includes several classes for a closer integration\r
34230  * with the server-side. The Ext.data namespace also includes classes for working\r
34231  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>\r
34232  * \r
34233  * <p><b><u>Specification</u></b></p>\r
34234  * \r
34235  * <p>For additional information consult the \r
34236  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>\r
34237  *   \r
34238  * <p><b><u>Providers</u></b></p>\r
34239  * \r
34240  * <p>Ext.Direct uses a provider architecture, where one or more providers are\r
34241  * used to transport data to and from the server. There are several providers\r
34242  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>\r
34243  * \r
34244  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>\r
34245  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>\r
34246  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side\r
34247  * on the client.</li>\r
34248  * </ul></div>\r
34249  * \r
34250  * <p>A provider does not need to be invoked directly, providers are added via\r
34251  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>\r
34252  * \r
34253  * <p><b><u>Router</u></b></p>\r
34254  * \r
34255  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client\r
34256  * to the appropriate server-side method. Because the Ext.Direct API is completely\r
34257  * platform-agnostic, you could completely swap out a Java based server solution\r
34258  * and replace it with one that uses C# without changing the client side JavaScript\r
34259  * at all.</p>\r
34260  * \r
34261  * <p><b><u>Server side events</u></b></p>\r
34262  * \r
34263  * <p>Custom events from the server may be handled by the client by adding\r
34264  * listeners, for example:</p>\r
34265  * <pre><code>\r
34266 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}\r
34267 \r
34268 // add a handler for a 'message' event sent by the server \r
34269 Ext.Direct.on('message', function(e){\r
34270     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));\r
34271             out.el.scrollTo('t', 100000, true);\r
34272 });\r
34273  * </code></pre>\r
34274  * @singleton\r
34275  */\r
34276 Ext.Direct = Ext.extend(Ext.util.Observable, {\r
34277     /**\r
34278      * Each event type implements a getData() method. The default event types are:\r
34279      * <div class="mdetail-params"><ul>\r
34280      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>\r
34281      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>\r
34282      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>\r
34283      * </ul></div>\r
34284      * @property eventTypes\r
34285      * @type Object\r
34286      */\r
34287 \r
34288     /**\r
34289      * Four types of possible exceptions which can occur:\r
34290      * <div class="mdetail-params"><ul>\r
34291      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>\r
34292      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>\r
34293      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>\r
34294      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>\r
34295      * </ul></div>\r
34296      * @property exceptions\r
34297      * @type Object\r
34298      */\r
34299     exceptions: {\r
34300         TRANSPORT: 'xhr',\r
34301         PARSE: 'parse',\r
34302         LOGIN: 'login',\r
34303         SERVER: 'exception'\r
34304     },\r
34305     \r
34306     // private\r
34307     constructor: function(){\r
34308         this.addEvents(\r
34309             /**\r
34310              * @event event\r
34311              * Fires after an event.\r
34312              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
34313              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
34314              */\r
34315             'event',\r
34316             /**\r
34317              * @event exception\r
34318              * Fires after an event exception.\r
34319              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
34320              */\r
34321             'exception'\r
34322         );\r
34323         this.transactions = {};\r
34324         this.providers = {};\r
34325     },\r
34326 \r
34327     /**\r
34328      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.\r
34329      * If the provider is not already connected, it will auto-connect.\r
34330      * <pre><code>\r
34331 var pollProv = new Ext.direct.PollingProvider({\r
34332     url: 'php/poll2.php'\r
34333 }); \r
34334 \r
34335 Ext.Direct.addProvider(\r
34336     {\r
34337         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider} \r
34338         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.\r
34339         "actions":{              // each property within the actions object represents a Class \r
34340             "TestAction":[       // array of methods within each server side Class   \r
34341             {\r
34342                 "name":"doEcho", // name of method\r
34343                 "len":1\r
34344             },{\r
34345                 "name":"multiply",\r
34346                 "len":1\r
34347             },{\r
34348                 "name":"doForm",\r
34349                 "formHandler":true, // handle form on server with Ext.Direct.Transaction \r
34350                 "len":1\r
34351             }]\r
34352         },\r
34353         "namespace":"myApplication",// namespace to create the Remoting Provider in\r
34354     },{\r
34355         type: 'polling', // create a {@link Ext.direct.PollingProvider} \r
34356         url:  'php/poll.php'\r
34357     },\r
34358     pollProv // reference to previously created instance\r
34359 );\r
34360      * </code></pre>\r
34361      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance\r
34362      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each\r
34363      * Provider description instructs Ext.Direct how to create client-side stub methods.\r
34364      */\r
34365     addProvider : function(provider){        \r
34366         var a = arguments;\r
34367         if(a.length > 1){\r
34368             for(var i = 0, len = a.length; i < len; i++){\r
34369                 this.addProvider(a[i]);\r
34370             }\r
34371             return;\r
34372         }\r
34373         \r
34374         // if provider has not already been instantiated\r
34375         if(!provider.events){\r
34376             provider = new Ext.Direct.PROVIDERS[provider.type](provider);\r
34377         }\r
34378         provider.id = provider.id || Ext.id();\r
34379         this.providers[provider.id] = provider;\r
34380 \r
34381         provider.on('data', this.onProviderData, this);\r
34382         provider.on('exception', this.onProviderException, this);\r
34383 \r
34384 \r
34385         if(!provider.isConnected()){\r
34386             provider.connect();\r
34387         }\r
34388 \r
34389         return provider;\r
34390     },\r
34391 \r
34392     /**\r
34393      * Retrieve a {@link Ext.direct.Provider provider} by the\r
34394      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is\r
34395      * {@link #addProvider added}.\r
34396      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider} \r
34397      */\r
34398     getProvider : function(id){\r
34399         return this.providers[id];\r
34400     },\r
34401 \r
34402     removeProvider : function(id){\r
34403         var provider = id.id ? id : this.providers[id.id];\r
34404         provider.un('data', this.onProviderData, this);\r
34405         provider.un('exception', this.onProviderException, this);\r
34406         delete this.providers[provider.id];\r
34407         return provider;\r
34408     },\r
34409 \r
34410     addTransaction: function(t){\r
34411         this.transactions[t.tid] = t;\r
34412         return t;\r
34413     },\r
34414 \r
34415     removeTransaction: function(t){\r
34416         delete this.transactions[t.tid || t];\r
34417         return t;\r
34418     },\r
34419 \r
34420     getTransaction: function(tid){\r
34421         return this.transactions[tid.tid || tid];\r
34422     },\r
34423 \r
34424     onProviderData : function(provider, e){\r
34425         if(Ext.isArray(e)){\r
34426             for(var i = 0, len = e.length; i < len; i++){\r
34427                 this.onProviderData(provider, e[i]);\r
34428             }\r
34429             return;\r
34430         }\r
34431         if(e.name && e.name != 'event' && e.name != 'exception'){\r
34432             this.fireEvent(e.name, e);\r
34433         }else if(e.type == 'exception'){\r
34434             this.fireEvent('exception', e);\r
34435         }\r
34436         this.fireEvent('event', e, provider);\r
34437     },\r
34438 \r
34439     createEvent : function(response, extraProps){\r
34440         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));\r
34441     }\r
34442 });\r
34443 // overwrite impl. with static instance\r
34444 Ext.Direct = new Ext.Direct();\r
34445 \r
34446 Ext.Direct.TID = 1;\r
34447 Ext.Direct.PROVIDERS = {};/**\r
34448  * @class Ext.Direct.Transaction\r
34449  * @extends Object\r
34450  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>\r
34451  * @constructor\r
34452  * @param {Object} config\r
34453  */\r
34454 Ext.Direct.Transaction = function(config){\r
34455     Ext.apply(this, config);\r
34456     this.tid = ++Ext.Direct.TID;\r
34457     this.retryCount = 0;\r
34458 };\r
34459 Ext.Direct.Transaction.prototype = {\r
34460     send: function(){\r
34461         this.provider.queueTransaction(this);\r
34462     },\r
34463 \r
34464     retry: function(){\r
34465         this.retryCount++;\r
34466         this.send();\r
34467     },\r
34468 \r
34469     getProvider: function(){\r
34470         return this.provider;\r
34471     }\r
34472 };Ext.Direct.Event = function(config){\r
34473     Ext.apply(this, config);\r
34474 }\r
34475 Ext.Direct.Event.prototype = {\r
34476     status: true,\r
34477     getData: function(){\r
34478         return this.data;\r
34479     }\r
34480 };\r
34481 \r
34482 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {\r
34483     type: 'rpc',\r
34484     getTransaction: function(){\r
34485         return this.transaction || Ext.Direct.getTransaction(this.tid);\r
34486     }\r
34487 });\r
34488 \r
34489 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {\r
34490     status: false,\r
34491     type: 'exception'\r
34492 });\r
34493 \r
34494 Ext.Direct.eventTypes = {\r
34495     'rpc':  Ext.Direct.RemotingEvent,\r
34496     'event':  Ext.Direct.Event,\r
34497     'exception':  Ext.Direct.ExceptionEvent\r
34498 };\r
34499 \r
34500 /**\r
34501  * @class Ext.direct.Provider\r
34502  * @extends Ext.util.Observable\r
34503  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>\r
34504  * \r
34505  * <p>For example ExtJs implements the following subclasses:</p>\r
34506  * <pre><code>\r
34507 Provider\r
34508 |\r
34509 +---{@link Ext.direct.JsonProvider JsonProvider} \r
34510     |\r
34511     +---{@link Ext.direct.PollingProvider PollingProvider}   \r
34512     |\r
34513     +---{@link Ext.direct.RemotingProvider RemotingProvider}   \r
34514  * </code></pre>\r
34515  * @abstract\r
34516  */\r
34517 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    \r
34518     /**\r
34519      * @cfg {String} id\r
34520      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).\r
34521      * You should assign an id if you need to be able to access the provider later and you do\r
34522      * not have an object reference available, for example:\r
34523      * <pre><code>\r
34524 Ext.Direct.addProvider(\r
34525     {\r
34526         type: 'polling',\r
34527         url:  'php/poll.php',\r
34528         id:   'poll-provider'\r
34529     }\r
34530 );\r
34531      \r
34532 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');\r
34533 p.disconnect();\r
34534      * </code></pre>\r
34535      */\r
34536         \r
34537     /**\r
34538      * @cfg {Number} priority\r
34539      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).\r
34540      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.\r
34541      */    \r
34542     priority: 1,\r
34543 \r
34544     /**\r
34545      * @cfg {String} type\r
34546      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified\r
34547      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a\r
34548      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>\r
34549      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>\r
34550      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>\r
34551      * </ul></div>\r
34552      */    \r
34553  \r
34554     // private\r
34555     constructor : function(config){\r
34556         Ext.apply(this, config);\r
34557         this.addEvents(\r
34558             /**\r
34559              * @event connect\r
34560              * Fires when the Provider connects to the server-side\r
34561              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
34562              */            \r
34563             'connect',\r
34564             /**\r
34565              * @event disconnect\r
34566              * Fires when the Provider disconnects from the server-side\r
34567              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
34568              */            \r
34569             'disconnect',\r
34570             /**\r
34571              * @event data\r
34572              * Fires when the Provider receives data from the server-side\r
34573              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
34574              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
34575              */            \r
34576             'data',\r
34577             /**\r
34578              * @event exception\r
34579              * Fires when the Provider receives an exception from the server-side\r
34580              */                        \r
34581             'exception'\r
34582         );\r
34583         Ext.direct.Provider.superclass.constructor.call(this, config);\r
34584     },\r
34585 \r
34586     /**\r
34587      * Returns whether or not the server-side is currently connected.\r
34588      * Abstract method for subclasses to implement.\r
34589      */\r
34590     isConnected: function(){\r
34591         return false;\r
34592     },\r
34593 \r
34594     /**\r
34595      * Abstract methods for subclasses to implement.\r
34596      */\r
34597     connect: Ext.emptyFn,\r
34598     \r
34599     /**\r
34600      * Abstract methods for subclasses to implement.\r
34601      */\r
34602     disconnect: Ext.emptyFn\r
34603 });\r
34604 /**\r
34605  * @class Ext.direct.JsonProvider\r
34606  * @extends Ext.direct.Provider\r
34607  */\r
34608 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {\r
34609     parseResponse: function(xhr){\r
34610         if(!Ext.isEmpty(xhr.responseText)){\r
34611             if(typeof xhr.responseText == 'object'){\r
34612                 return xhr.responseText;\r
34613             }\r
34614             return Ext.decode(xhr.responseText);\r
34615         }\r
34616         return null;\r
34617     },\r
34618 \r
34619     getEvents: function(xhr){\r
34620         var data = null;\r
34621         try{\r
34622             data = this.parseResponse(xhr);\r
34623         }catch(e){\r
34624             var event = new Ext.Direct.ExceptionEvent({\r
34625                 data: e,\r
34626                 xhr: xhr,\r
34627                 code: Ext.Direct.exceptions.PARSE,\r
34628                 message: 'Error parsing json response: \n\n ' + data\r
34629             })\r
34630             return [event];\r
34631         }\r
34632         var events = [];\r
34633         if(Ext.isArray(data)){\r
34634             for(var i = 0, len = data.length; i < len; i++){\r
34635                 events.push(Ext.Direct.createEvent(data[i]));\r
34636             }\r
34637         }else{\r
34638             events.push(Ext.Direct.createEvent(data));\r
34639         }\r
34640         return events;\r
34641     }\r
34642 });/**\r
34643  * @class Ext.direct.PollingProvider\r
34644  * @extends Ext.direct.JsonProvider\r
34645  *\r
34646  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.\r
34647  * The initial request for data originates from the client, and then is responded to by the\r
34648  * server.</p>\r
34649  * \r
34650  * <p>All configurations for the PollingProvider should be generated by the server-side\r
34651  * API portion of the Ext.Direct stack.</p>\r
34652  *\r
34653  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply\r
34654  * specifying <tt>type = 'polling'</tt>.  For example:</p>\r
34655  * <pre><code>\r
34656 var pollA = new Ext.direct.PollingProvider({\r
34657     type:'polling',\r
34658     url: 'php/pollA.php',\r
34659 });\r
34660 Ext.Direct.addProvider(pollA);\r
34661 pollA.disconnect();\r
34662 \r
34663 Ext.Direct.addProvider(\r
34664     {\r
34665         type:'polling',\r
34666         url: 'php/pollB.php',\r
34667         id: 'pollB-provider'\r
34668     }\r
34669 );\r
34670 var pollB = Ext.Direct.getProvider('pollB-provider');\r
34671  * </code></pre>\r
34672  */\r
34673 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {\r
34674     /**\r
34675      * @cfg {Number} priority\r
34676      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.\r
34677      */\r
34678     // override default priority\r
34679     priority: 3,\r
34680     \r
34681     /**\r
34682      * @cfg {Number} interval\r
34683      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every\r
34684      * 3 seconds).\r
34685      */\r
34686     interval: 3000,\r
34687 \r
34688     /**\r
34689      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters\r
34690      * on every polling request\r
34691      */\r
34692     \r
34693     /**\r
34694      * @cfg {String/Function} url\r
34695      * The url which the PollingProvider should contact with each request. This can also be\r
34696      * an imported Ext.Direct method which will accept the baseParams as its only argument.\r
34697      */\r
34698 \r
34699     // private\r
34700     constructor : function(config){\r
34701         Ext.direct.PollingProvider.superclass.constructor.call(this, config);\r
34702         this.addEvents(\r
34703             /**\r
34704              * @event beforepoll\r
34705              * Fired immediately before a poll takes place, an event handler can return false\r
34706              * in order to cancel the poll.\r
34707              * @param {Ext.direct.PollingProvider}\r
34708              */\r
34709             'beforepoll',            \r
34710             /**\r
34711              * @event poll\r
34712              * This event has not yet been implemented.\r
34713              * @param {Ext.direct.PollingProvider}\r
34714              */\r
34715             'poll'\r
34716         );\r
34717     },\r
34718 \r
34719     // inherited\r
34720     isConnected: function(){\r
34721         return !!this.pollTask;\r
34722     },\r
34723 \r
34724     /**\r
34725      * Connect to the server-side and begin the polling process. To handle each\r
34726      * response subscribe to the data event.\r
34727      */\r
34728     connect: function(){\r
34729         if(this.url && !this.pollTask){\r
34730             this.pollTask = Ext.TaskMgr.start({\r
34731                 run: function(){\r
34732                     if(this.fireEvent('beforepoll', this) !== false){\r
34733                         if(typeof this.url == 'function'){\r
34734                             this.url(this.baseParams);\r
34735                         }else{\r
34736                             Ext.Ajax.request({\r
34737                                 url: this.url,\r
34738                                 callback: this.onData,\r
34739                                 scope: this,\r
34740                                 params: this.baseParams\r
34741                             });\r
34742                         }\r
34743                     }\r
34744                 },\r
34745                 interval: this.interval,\r
34746                 scope: this\r
34747             });\r
34748             this.fireEvent('connect', this);\r
34749         }else if(!this.url){\r
34750             throw 'Error initializing PollingProvider, no url configured.';\r
34751         }\r
34752     },\r
34753 \r
34754     /**\r
34755      * Disconnect from the server-side and stop the polling process. The disconnect\r
34756      * event will be fired on a successful disconnect.\r
34757      */\r
34758     disconnect: function(){\r
34759         if(this.pollTask){\r
34760             Ext.TaskMgr.stop(this.pollTask);\r
34761             delete this.pollTask;\r
34762             this.fireEvent('disconnect', this);\r
34763         }\r
34764     },\r
34765 \r
34766     // private\r
34767     onData: function(opt, success, xhr){\r
34768         if(success){\r
34769             var events = this.getEvents(xhr);\r
34770             for(var i = 0, len = events.length; i < len; i++){\r
34771                 var e = events[i];\r
34772                 this.fireEvent('data', this, e);\r
34773             }\r
34774         }else{\r
34775             var e = new Ext.Direct.ExceptionEvent({\r
34776                 data: e,\r
34777                 code: Ext.Direct.exceptions.TRANSPORT,\r
34778                 message: 'Unable to connect to the server.',\r
34779                 xhr: xhr\r
34780             });\r
34781             this.fireEvent('data', this, e);\r
34782         }\r
34783     }\r
34784 });\r
34785 \r
34786 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**\r
34787  * @class Ext.direct.RemotingProvider\r
34788  * @extends Ext.direct.JsonProvider\r
34789  * \r
34790  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to\r
34791  * server side methods on the client (a remote procedure call (RPC) type of\r
34792  * connection where the client can initiate a procedure on the server).</p>\r
34793  * \r
34794  * <p>This allows for code to be organized in a fashion that is maintainable,\r
34795  * while providing a clear path between client and server, something that is\r
34796  * not always apparent when using URLs.</p>\r
34797  * \r
34798  * <p>To accomplish this the server-side needs to describe what classes and methods\r
34799  * are available on the client-side. This configuration will typically be\r
34800  * outputted by the server-side Ext.Direct stack when the API description is built.</p>\r
34801  */\r
34802 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       \r
34803     /**\r
34804      * @cfg {Object} actions\r
34805      * Object literal defining the server side actions and methods. For example, if\r
34806      * the Provider is configured with:\r
34807      * <pre><code>\r
34808 "actions":{ // each property within the 'actions' object represents a server side Class \r
34809     "TestAction":[ // array of methods within each server side Class to be   \r
34810     {              // stubbed out on client\r
34811         "name":"doEcho", \r
34812         "len":1            \r
34813     },{\r
34814         "name":"multiply",// name of method\r
34815         "len":2           // The number of parameters that will be used to create an\r
34816                           // array of data to send to the server side function.\r
34817                           // Ensure the server sends back a Number, not a String. \r
34818     },{\r
34819         "name":"doForm",\r
34820         "formHandler":true, // direct the client to use specialized form handling method \r
34821         "len":1\r
34822     }]\r
34823 }\r
34824      * </code></pre>\r
34825      * <p>Note that a Store is not required, a server method can be called at any time.\r
34826      * In the following example a <b>client side</b> handler is used to call the\r
34827      * server side method "multiply" in the server-side "TestAction" Class:</p>\r
34828      * <pre><code>\r
34829 TestAction.multiply(\r
34830     2, 4, // pass two arguments to server, so specify len=2\r
34831     // callback function after the server is called\r
34832     // result: the result returned by the server\r
34833     //      e: Ext.Direct.RemotingEvent object\r
34834     function(result, e){\r
34835         var t = e.getTransaction();\r
34836         var action = t.action; // server side Class called\r
34837         var method = t.method; // server side method called\r
34838         if(e.status){\r
34839             var answer = Ext.encode(result); // 8\r
34840     \r
34841         }else{\r
34842             var msg = e.message; // failure message\r
34843         }\r
34844     }\r
34845 );\r
34846      * </code></pre>\r
34847      * In the example above, the server side "multiply" function will be passed two\r
34848      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be\r
34849      * available as the <tt>result</tt> in the example above. \r
34850      */\r
34851     \r
34852     /**\r
34853      * @cfg {String/Object} namespace\r
34854      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).\r
34855      * Explicitly specify the namespace Object, or specify a String to have a\r
34856      * {@link Ext#namespace namespace created} implicitly.\r
34857      */\r
34858     \r
34859     /**\r
34860      * @cfg {String} url\r
34861      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. \r
34862      */\r
34863     \r
34864     /**\r
34865      * @cfg {String} enableUrlEncode\r
34866      * Specify which param will hold the arguments for the method.\r
34867      * Defaults to <tt>'data'</tt>.\r
34868      */\r
34869     \r
34870     /**\r
34871      * @cfg {Number/Boolean} enableBuffer\r
34872      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method\r
34873      * calls. If a number is specified this is the amount of time in milliseconds\r
34874      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>\r
34875      * <br><p>Calls which are received within the specified timeframe will be\r
34876      * concatenated together and sent in a single request, optimizing the\r
34877      * application by reducing the amount of round trips that have to be made\r
34878      * to the server.</p>\r
34879      */\r
34880     enableBuffer: 10,\r
34881     \r
34882     /**\r
34883      * @cfg {Number} maxRetries\r
34884      * Number of times to re-attempt delivery on failure of a call.\r
34885      */\r
34886     maxRetries: 1,\r
34887 \r
34888     constructor : function(config){\r
34889         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);\r
34890         this.addEvents(\r
34891             /**\r
34892              * @event beforecall\r
34893              * Fires immediately before the client-side sends off the RPC call.\r
34894              * By returning false from an event handler you can prevent the call from\r
34895              * executing.\r
34896              * @param {Ext.direct.RemotingProvider} provider\r
34897              * @param {Ext.Direct.Transaction} transaction\r
34898              */            \r
34899             'beforecall',\r
34900             /**\r
34901              * @event call\r
34902              * Fires immediately after the request to the server-side is sent. This does\r
34903              * NOT fire after the response has come back from the call.\r
34904              * @param {Ext.direct.RemotingProvider} provider\r
34905              * @param {Ext.Direct.Transaction} transaction\r
34906              */            \r
34907             'call'\r
34908         );\r
34909         this.namespace = (typeof this.namespace === 'string') ? Ext.ns(this.namespace) : this.namespace || window;\r
34910         this.transactions = {};\r
34911         this.callBuffer = [];\r
34912     },\r
34913 \r
34914     // private\r
34915     initAPI : function(){\r
34916         var o = this.actions;\r
34917         for(var c in o){\r
34918             var cls = this.namespace[c] || (this.namespace[c] = {});\r
34919             var ms = o[c];\r
34920             for(var i = 0, len = ms.length; i < len; i++){\r
34921                 var m = ms[i];\r
34922                 cls[m.name] = this.createMethod(c, m);\r
34923             }\r
34924         }\r
34925     },\r
34926 \r
34927     // inherited\r
34928     isConnected: function(){\r
34929         return !!this.connected;\r
34930     },\r
34931 \r
34932     connect: function(){\r
34933         if(this.url){\r
34934             this.initAPI();\r
34935             this.connected = true;\r
34936             this.fireEvent('connect', this);\r
34937         }else if(!this.url){\r
34938             throw 'Error initializing RemotingProvider, no url configured.';\r
34939         }\r
34940     },\r
34941 \r
34942     disconnect: function(){\r
34943         if(this.connected){\r
34944             this.connected = false;\r
34945             this.fireEvent('disconnect', this);\r
34946         }\r
34947     },\r
34948 \r
34949     onData: function(opt, success, xhr){\r
34950         if(success){\r
34951             var events = this.getEvents(xhr);\r
34952             for(var i = 0, len = events.length; i < len; i++){\r
34953                 var e = events[i];\r
34954                 var t = this.getTransaction(e);\r
34955                 this.fireEvent('data', this, e);\r
34956                 if(t){\r
34957                     this.doCallback(t, e, true);\r
34958                     Ext.Direct.removeTransaction(t);\r
34959                 }\r
34960             }\r
34961         }else{\r
34962             var ts = [].concat(opt.ts);\r
34963             for(var i = 0, len = ts.length; i < len; i++){\r
34964                 var t = this.getTransaction(ts[i]);\r
34965                 if(t && t.retryCount < this.maxRetries){\r
34966                     t.retry();\r
34967                 }else{\r
34968                     var e = new Ext.Direct.ExceptionEvent({\r
34969                         data: e,\r
34970                         transaction: t,\r
34971                         code: Ext.Direct.exceptions.TRANSPORT,\r
34972                         message: 'Unable to connect to the server.',\r
34973                         xhr: xhr\r
34974                     });\r
34975                     this.fireEvent('data', this, e);\r
34976                     if(t){\r
34977                         this.doCallback(t, e, false);\r
34978                         Ext.Direct.removeTransaction(t);\r
34979                     }\r
34980                 }\r
34981             }\r
34982         }\r
34983     },\r
34984 \r
34985     getCallData: function(t){\r
34986         return {\r
34987             action: t.action,\r
34988             method: t.method,\r
34989             data: t.data,\r
34990             type: 'rpc',\r
34991             tid: t.tid\r
34992         };\r
34993     },\r
34994 \r
34995     doSend : function(data){\r
34996         var o = {\r
34997             url: this.url,\r
34998             callback: this.onData,\r
34999             scope: this,\r
35000             ts: data\r
35001         };\r
35002 \r
35003         // send only needed data\r
35004         var callData;\r
35005         if(Ext.isArray(data)){\r
35006             callData = [];\r
35007             for(var i = 0, len = data.length; i < len; i++){\r
35008                 callData.push(this.getCallData(data[i]));\r
35009             }\r
35010         }else{\r
35011             callData = this.getCallData(data);\r
35012         }\r
35013 \r
35014         if(this.enableUrlEncode){\r
35015             var params = {};\r
35016             params[typeof this.enableUrlEncode == 'string' ? this.enableUrlEncode : 'data'] = Ext.encode(callData);\r
35017             o.params = params;\r
35018         }else{\r
35019             o.jsonData = callData;\r
35020         }\r
35021         Ext.Ajax.request(o);\r
35022     },\r
35023 \r
35024     combineAndSend : function(){\r
35025         var len = this.callBuffer.length;\r
35026         if(len > 0){\r
35027             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);\r
35028             this.callBuffer = [];\r
35029         }\r
35030     },\r
35031 \r
35032     queueTransaction: function(t){\r
35033         if(t.form){\r
35034             this.processForm(t);\r
35035             return;\r
35036         }\r
35037         this.callBuffer.push(t);\r
35038         if(this.enableBuffer){\r
35039             if(!this.callTask){\r
35040                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);\r
35041             }\r
35042             this.callTask.delay(typeof this.enableBuffer == 'number' ? this.enableBuffer : 10);\r
35043         }else{\r
35044             this.combineAndSend();\r
35045         }\r
35046     },\r
35047 \r
35048     doCall : function(c, m, args){\r
35049         var data = null, hs = args[m.len], scope = args[m.len+1];\r
35050 \r
35051         if(m.len !== 0){\r
35052             data = args.slice(0, m.len);\r
35053         }\r
35054 \r
35055         var t = new Ext.Direct.Transaction({\r
35056             provider: this,\r
35057             args: args,\r
35058             action: c,\r
35059             method: m.name,\r
35060             data: data,\r
35061             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs\r
35062         });\r
35063 \r
35064         if(this.fireEvent('beforecall', this, t) !== false){\r
35065             Ext.Direct.addTransaction(t);\r
35066             this.queueTransaction(t);\r
35067             this.fireEvent('call', this, t);\r
35068         }\r
35069     },\r
35070 \r
35071     doForm : function(c, m, form, callback, scope){\r
35072         var t = new Ext.Direct.Transaction({\r
35073             provider: this,\r
35074             action: c,\r
35075             method: m.name,\r
35076             args:[form, callback, scope],\r
35077             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,\r
35078             isForm: true\r
35079         });\r
35080 \r
35081         if(this.fireEvent('beforecall', this, t) !== false){\r
35082             Ext.Direct.addTransaction(t);\r
35083             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',\r
35084                 params = {\r
35085                     extTID: t.tid,\r
35086                     extAction: c,\r
35087                     extMethod: m.name,\r
35088                     extType: 'rpc',\r
35089                     extUpload: String(isUpload)\r
35090                 };\r
35091             \r
35092             // change made from typeof callback check to callback.params\r
35093             // to support addl param passing in DirectSubmit EAC 6/2\r
35094             Ext.apply(t, {\r
35095                 form: Ext.getDom(form),\r
35096                 isUpload: isUpload,\r
35097                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params\r
35098             });\r
35099             this.fireEvent('call', this, t);\r
35100             this.processForm(t);\r
35101         }\r
35102     },\r
35103     \r
35104     processForm: function(t){\r
35105         Ext.Ajax.request({\r
35106             url: this.url,\r
35107             params: t.params,\r
35108             callback: this.onData,\r
35109             scope: this,\r
35110             form: t.form,\r
35111             isUpload: t.isUpload,\r
35112             ts: t\r
35113         });\r
35114     },\r
35115 \r
35116     createMethod : function(c, m){\r
35117         var f;\r
35118         if(!m.formHandler){\r
35119             f = function(){\r
35120                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));\r
35121             }.createDelegate(this);\r
35122         }else{\r
35123             f = function(form, callback, scope){\r
35124                 this.doForm(c, m, form, callback, scope);\r
35125             }.createDelegate(this);\r
35126         }\r
35127         f.directCfg = {\r
35128             action: c,\r
35129             method: m\r
35130         };\r
35131         return f;\r
35132     },\r
35133 \r
35134     getTransaction: function(opt){\r
35135         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;\r
35136     },\r
35137 \r
35138     doCallback: function(t, e){\r
35139         var fn = e.status ? 'success' : 'failure';\r
35140         if(t && t.cb){\r
35141             var hs = t.cb;\r
35142             var result = e.result || e.data;\r
35143             if(Ext.isFunction(hs)){\r
35144                 hs(result, e);\r
35145             } else{\r
35146                 Ext.callback(hs[fn], hs.scope, [result, e]);\r
35147                 Ext.callback(hs.callback, hs.scope, [result, e]);\r
35148             }\r
35149         }\r
35150     }\r
35151 });\r
35152 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/**\r
35153  * @class Ext.Resizable\r
35154  * @extends Ext.util.Observable\r
35155  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element \r
35156  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap\r
35157  * 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\r
35158  * the element will be wrapped for you automatically.</p>\r
35159  * <p>Here is the list of valid resize handles:</p>\r
35160  * <pre>\r
35161 Value   Description\r
35162 ------  -------------------\r
35163  'n'     north\r
35164  's'     south\r
35165  'e'     east\r
35166  'w'     west\r
35167  'nw'    northwest\r
35168  'sw'    southwest\r
35169  'se'    southeast\r
35170  'ne'    northeast\r
35171  'all'   all\r
35172 </pre>\r
35173  * <p>Here's an example showing the creation of a typical Resizable:</p>\r
35174  * <pre><code>\r
35175 var resizer = new Ext.Resizable('element-id', {\r
35176     handles: 'all',\r
35177     minWidth: 200,\r
35178     minHeight: 100,\r
35179     maxWidth: 500,\r
35180     maxHeight: 400,\r
35181     pinned: true\r
35182 });\r
35183 resizer.on('resize', myHandler);\r
35184 </code></pre>\r
35185  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>\r
35186  * resizer.east.setDisplayed(false);</p>\r
35187  * @constructor\r
35188  * Create a new resizable component\r
35189  * @param {Mixed} el The id or element to resize\r
35190  * @param {Object} config configuration options\r
35191   */\r
35192 Ext.Resizable = function(el, config){\r
35193     this.el = Ext.get(el);\r
35194     \r
35195     if(config && config.wrap){\r
35196         config.resizeChild = this.el;\r
35197         this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});\r
35198         this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';\r
35199         this.el.setStyle('overflow', 'hidden');\r
35200         this.el.setPositioning(config.resizeChild.getPositioning());\r
35201         config.resizeChild.clearPositioning();\r
35202         if(!config.width || !config.height){\r
35203             var csize = config.resizeChild.getSize();\r
35204             this.el.setSize(csize.width, csize.height);\r
35205         }\r
35206         if(config.pinned && !config.adjustments){\r
35207             config.adjustments = 'auto';\r
35208         }\r
35209     }\r
35210 \r
35211     /**\r
35212      * The proxy Element that is resized in place of the real Element during the resize operation.\r
35213      * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.\r
35214      * Read only.\r
35215      * @type Ext.Element.\r
35216      * @property proxy\r
35217      */\r
35218     this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());\r
35219     this.proxy.unselectable();\r
35220     this.proxy.enableDisplayMode('block');\r
35221 \r
35222     Ext.apply(this, config);\r
35223     \r
35224     if(this.pinned){\r
35225         this.disableTrackOver = true;\r
35226         this.el.addClass('x-resizable-pinned');\r
35227     }\r
35228     // if the element isn't positioned, make it relative\r
35229     var position = this.el.getStyle('position');\r
35230     if(position != 'absolute' && position != 'fixed'){\r
35231         this.el.setStyle('position', 'relative');\r
35232     }\r
35233     if(!this.handles){ // no handles passed, must be legacy style\r
35234         this.handles = 's,e,se';\r
35235         if(this.multiDirectional){\r
35236             this.handles += ',n,w';\r
35237         }\r
35238     }\r
35239     if(this.handles == 'all'){\r
35240         this.handles = 'n s e w ne nw se sw';\r
35241     }\r
35242     var hs = this.handles.split(/\s*?[,;]\s*?| /);\r
35243     var ps = Ext.Resizable.positions;\r
35244     for(var i = 0, len = hs.length; i < len; i++){\r
35245         if(hs[i] && ps[hs[i]]){\r
35246             var pos = ps[hs[i]];\r
35247             this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);\r
35248         }\r
35249     }\r
35250     // legacy\r
35251     this.corner = this.southeast;\r
35252     \r
35253     if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){\r
35254         this.updateBox = true;\r
35255     }   \r
35256    \r
35257     this.activeHandle = null;\r
35258     \r
35259     if(this.resizeChild){\r
35260         if(typeof this.resizeChild == 'boolean'){\r
35261             this.resizeChild = Ext.get(this.el.dom.firstChild, true);\r
35262         }else{\r
35263             this.resizeChild = Ext.get(this.resizeChild, true);\r
35264         }\r
35265     }\r
35266     \r
35267     if(this.adjustments == 'auto'){\r
35268         var rc = this.resizeChild;\r
35269         var hw = this.west, he = this.east, hn = this.north, hs = this.south;\r
35270         if(rc && (hw || hn)){\r
35271             rc.position('relative');\r
35272             rc.setLeft(hw ? hw.el.getWidth() : 0);\r
35273             rc.setTop(hn ? hn.el.getHeight() : 0);\r
35274         }\r
35275         this.adjustments = [\r
35276             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),\r
35277             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1 \r
35278         ];\r
35279     }\r
35280     \r
35281     if(this.draggable){\r
35282         this.dd = this.dynamic ? \r
35283             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});\r
35284         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);\r
35285     }\r
35286     \r
35287     this.addEvents(\r
35288         /**\r
35289          * @event beforeresize\r
35290          * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.\r
35291          * @param {Ext.Resizable} this\r
35292          * @param {Ext.EventObject} e The mousedown event\r
35293          */\r
35294         'beforeresize',\r
35295         /**\r
35296          * @event resize\r
35297          * Fired after a resize.\r
35298          * @param {Ext.Resizable} this\r
35299          * @param {Number} width The new width\r
35300          * @param {Number} height The new height\r
35301          * @param {Ext.EventObject} e The mouseup event\r
35302          */\r
35303         'resize'\r
35304     );\r
35305     \r
35306     if(this.width !== null && this.height !== null){\r
35307         this.resizeTo(this.width, this.height);\r
35308     }else{\r
35309         this.updateChildSize();\r
35310     }\r
35311     if(Ext.isIE){\r
35312         this.el.dom.style.zoom = 1;\r
35313     }\r
35314     Ext.Resizable.superclass.constructor.call(this);\r
35315 };\r
35316 \r
35317 Ext.extend(Ext.Resizable, Ext.util.Observable, {\r
35318 \r
35319     /**\r
35320      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the\r
35321      * resize operation's new size (defaults to <tt>[0, 0]</tt>)\r
35322      */\r
35323     adjustments : [0, 0],\r
35324     /**\r
35325      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)\r
35326      */\r
35327     animate : false,\r
35328     /**\r
35329      * @cfg {Mixed} constrainTo Constrain the resize to a particular element\r
35330      */\r
35331     /**\r
35332      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)\r
35333      */\r
35334     disableTrackOver : false,\r
35335     /**\r
35336      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)\r
35337      */\r
35338     draggable: false,\r
35339     /**\r
35340      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)\r
35341      */\r
35342     duration : 0.35,\r
35343     /**\r
35344      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)\r
35345      */\r
35346     dynamic : false,\r
35347     /**\r
35348      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)\r
35349      */\r
35350     easing : 'easeOutStrong',\r
35351     /**\r
35352      * @cfg {Boolean} enabled False to disable resizing (defaults to true)\r
35353      */\r
35354     enabled : true,\r
35355     /**\r
35356      * @property enabled Writable. False if resizing is disabled.\r
35357      * @type Boolean \r
35358      */\r
35359     /**\r
35360      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).\r
35361      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.\r
35362      */\r
35363     handles : false,\r
35364     /**\r
35365      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.\r
35366      */\r
35367     multiDirectional : false,\r
35368     /**\r
35369      * @cfg {Number} height The height of the element in pixels (defaults to null)\r
35370      */\r
35371     height : null,\r
35372     /**\r
35373      * @cfg {Number} width The width of the element in pixels (defaults to null)\r
35374      */\r
35375     width : null,\r
35376     /**\r
35377      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels\r
35378      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
35379      */\r
35380     heightIncrement : 0,\r
35381     /**\r
35382      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels\r
35383      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
35384      */\r
35385     widthIncrement : 0,\r
35386     /**\r
35387      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)\r
35388      */\r
35389     minHeight : 5,\r
35390     /**\r
35391      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)\r
35392      */\r
35393     minWidth : 5,\r
35394     /**\r
35395      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)\r
35396      */\r
35397     maxHeight : 10000,\r
35398     /**\r
35399      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)\r
35400      */\r
35401     maxWidth : 10000,\r
35402     /**\r
35403      * @cfg {Number} minX The minimum x for the element (defaults to 0)\r
35404      */\r
35405     minX: 0,\r
35406     /**\r
35407      * @cfg {Number} minY The minimum x for the element (defaults to 0)\r
35408      */\r
35409     minY: 0,\r
35410     /**\r
35411      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the\r
35412      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)\r
35413      */\r
35414     pinned : false,\r
35415     /**\r
35416      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height\r
35417      * and width during resize (defaults to false)\r
35418      */\r
35419     preserveRatio : false,\r
35420     /**\r
35421      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false) \r
35422      */ \r
35423     resizeChild : false,\r
35424     /**\r
35425      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)\r
35426      */\r
35427     transparent: false,\r
35428     /**\r
35429      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region\r
35430      */\r
35431     /**\r
35432      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)\r
35433      * in favor of the handles config option (defaults to false)\r
35434      */\r
35435 \r
35436     \r
35437     /**\r
35438      * Perform a manual resize and fires the 'resize' event.\r
35439      * @param {Number} width\r
35440      * @param {Number} height\r
35441      */\r
35442     resizeTo : function(width, height){\r
35443         this.el.setSize(width, height);\r
35444         this.updateChildSize();\r
35445         this.fireEvent('resize', this, width, height, null);\r
35446     },\r
35447 \r
35448     // private\r
35449     startSizing : function(e, handle){\r
35450         this.fireEvent('beforeresize', this, e);\r
35451         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler\r
35452 \r
35453             if(!this.overlay){\r
35454                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());\r
35455                 this.overlay.unselectable();\r
35456                 this.overlay.enableDisplayMode('block');\r
35457                 this.overlay.on({\r
35458                     scope: this,\r
35459                     mousemove: this.onMouseMove,\r
35460                     mouseup: this.onMouseUp\r
35461                 });\r
35462             }\r
35463             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));\r
35464 \r
35465             this.resizing = true;\r
35466             this.startBox = this.el.getBox();\r
35467             this.startPoint = e.getXY();\r
35468             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],\r
35469                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];\r
35470 \r
35471             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
35472             this.overlay.show();\r
35473 \r
35474             if(this.constrainTo) {\r
35475                 var ct = Ext.get(this.constrainTo);\r
35476                 this.resizeRegion = ct.getRegion().adjust(\r
35477                     ct.getFrameWidth('t'),\r
35478                     ct.getFrameWidth('l'),\r
35479                     -ct.getFrameWidth('b'),\r
35480                     -ct.getFrameWidth('r')\r
35481                 );\r
35482             }\r
35483 \r
35484             this.proxy.setStyle('visibility', 'hidden'); // workaround display none\r
35485             this.proxy.show();\r
35486             this.proxy.setBox(this.startBox);\r
35487             if(!this.dynamic){\r
35488                 this.proxy.setStyle('visibility', 'visible');\r
35489             }\r
35490         }\r
35491     },\r
35492 \r
35493     // private\r
35494     onMouseDown : function(handle, e){\r
35495         if(this.enabled){\r
35496             e.stopEvent();\r
35497             this.activeHandle = handle;\r
35498             this.startSizing(e, handle);\r
35499         }          \r
35500     },\r
35501 \r
35502     // private\r
35503     onMouseUp : function(e){\r
35504         this.activeHandle = null;\r
35505         var size = this.resizeElement();\r
35506         this.resizing = false;\r
35507         this.handleOut();\r
35508         this.overlay.hide();\r
35509         this.proxy.hide();\r
35510         this.fireEvent('resize', this, size.width, size.height, e);\r
35511     },\r
35512 \r
35513     // private\r
35514     updateChildSize : function(){\r
35515         if(this.resizeChild){\r
35516             var el = this.el;\r
35517             var child = this.resizeChild;\r
35518             var adj = this.adjustments;\r
35519             if(el.dom.offsetWidth){\r
35520                 var b = el.getSize(true);\r
35521                 child.setSize(b.width+adj[0], b.height+adj[1]);\r
35522             }\r
35523             // Second call here for IE\r
35524             // The first call enables instant resizing and\r
35525             // the second call corrects scroll bars if they\r
35526             // exist\r
35527             if(Ext.isIE){\r
35528                 setTimeout(function(){\r
35529                     if(el.dom.offsetWidth){\r
35530                         var b = el.getSize(true);\r
35531                         child.setSize(b.width+adj[0], b.height+adj[1]);\r
35532                     }\r
35533                 }, 10);\r
35534             }\r
35535         }\r
35536     },\r
35537 \r
35538     // private\r
35539     snap : function(value, inc, min){\r
35540         if(!inc || !value){\r
35541             return value;\r
35542         }\r
35543         var newValue = value;\r
35544         var m = value % inc;\r
35545         if(m > 0){\r
35546             if(m > (inc/2)){\r
35547                 newValue = value + (inc-m);\r
35548             }else{\r
35549                 newValue = value - m;\r
35550             }\r
35551         }\r
35552         return Math.max(min, newValue);\r
35553     },\r
35554 \r
35555     /**\r
35556      * <p>Performs resizing of the associated Element. This method is called internally by this\r
35557      * class, and should not be called by user code.</p>\r
35558      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI\r
35559      * component such as a Panel, this method may be overridden by specifying an implementation\r
35560      * as a config option to provide appropriate behaviour at the end of the resize operation on\r
35561      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>\r
35562      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}\r
35563      * Element. Example:\r
35564 <pre><code>\r
35565 new Ext.Panel({\r
35566     title: 'Resize me',\r
35567     x: 100,\r
35568     y: 100,\r
35569     renderTo: Ext.getBody(),\r
35570     floating: true,\r
35571     frame: true,\r
35572     width: 400,\r
35573     height: 200,\r
35574     listeners: {\r
35575         render: function(p) {\r
35576             new Ext.Resizable(p.getEl(), {\r
35577                 handles: 'all',\r
35578                 pinned: true,\r
35579                 transparent: true,\r
35580                 resizeElement: function() {\r
35581                     var box = this.proxy.getBox();\r
35582                     p.updateBox(box);\r
35583                     if (p.layout) {\r
35584                         p.doLayout();\r
35585                     }\r
35586                     return box;\r
35587                 }\r
35588            });\r
35589        }\r
35590     }\r
35591 }).show();\r
35592 </code></pre>\r
35593      */\r
35594     resizeElement : function(){\r
35595         var box = this.proxy.getBox();\r
35596         if(this.updateBox){\r
35597             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);\r
35598         }else{\r
35599             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);\r
35600         }\r
35601         this.updateChildSize();\r
35602         if(!this.dynamic){\r
35603             this.proxy.hide();\r
35604         }\r
35605         return box;\r
35606     },\r
35607 \r
35608     // private\r
35609     constrain : function(v, diff, m, mx){\r
35610         if(v - diff < m){\r
35611             diff = v - m;    \r
35612         }else if(v - diff > mx){\r
35613             diff = v - mx; \r
35614         }\r
35615         return diff;                \r
35616     },\r
35617 \r
35618     // private\r
35619     onMouseMove : function(e){\r
35620         if(this.enabled && this.activeHandle){\r
35621             try{// try catch so if something goes wrong the user doesn't get hung\r
35622 \r
35623             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {\r
35624                 return;\r
35625             }\r
35626 \r
35627             //var curXY = this.startPoint;\r
35628             var curSize = this.curSize || this.startBox,\r
35629                 x = this.startBox.x, y = this.startBox.y,\r
35630                 ox = x, \r
35631                 oy = y,\r
35632                 w = curSize.width, \r
35633                 h = curSize.height,\r
35634                 ow = w, \r
35635                 oh = h,\r
35636                 mw = this.minWidth, \r
35637                 mh = this.minHeight,\r
35638                 mxw = this.maxWidth, \r
35639                 mxh = this.maxHeight,\r
35640                 wi = this.widthIncrement,\r
35641                 hi = this.heightIncrement,\r
35642                 eventXY = e.getXY(),\r
35643                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),\r
35644                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),\r
35645                 pos = this.activeHandle.position,\r
35646                 tw,\r
35647                 th;\r
35648             \r
35649             switch(pos){\r
35650                 case 'east':\r
35651                     w += diffX; \r
35652                     w = Math.min(Math.max(mw, w), mxw);\r
35653                     break;\r
35654                 case 'south':\r
35655                     h += diffY;\r
35656                     h = Math.min(Math.max(mh, h), mxh);\r
35657                     break;\r
35658                 case 'southeast':\r
35659                     w += diffX; \r
35660                     h += diffY;\r
35661                     w = Math.min(Math.max(mw, w), mxw);\r
35662                     h = Math.min(Math.max(mh, h), mxh);\r
35663                     break;\r
35664                 case 'north':\r
35665                     diffY = this.constrain(h, diffY, mh, mxh);\r
35666                     y += diffY;\r
35667                     h -= diffY;\r
35668                     break;\r
35669                 case 'west':\r
35670                     diffX = this.constrain(w, diffX, mw, mxw);\r
35671                     x += diffX;\r
35672                     w -= diffX;\r
35673                     break;\r
35674                 case 'northeast':\r
35675                     w += diffX; \r
35676                     w = Math.min(Math.max(mw, w), mxw);\r
35677                     diffY = this.constrain(h, diffY, mh, mxh);\r
35678                     y += diffY;\r
35679                     h -= diffY;\r
35680                     break;\r
35681                 case 'northwest':\r
35682                     diffX = this.constrain(w, diffX, mw, mxw);\r
35683                     diffY = this.constrain(h, diffY, mh, mxh);\r
35684                     y += diffY;\r
35685                     h -= diffY;\r
35686                     x += diffX;\r
35687                     w -= diffX;\r
35688                     break;\r
35689                case 'southwest':\r
35690                     diffX = this.constrain(w, diffX, mw, mxw);\r
35691                     h += diffY;\r
35692                     h = Math.min(Math.max(mh, h), mxh);\r
35693                     x += diffX;\r
35694                     w -= diffX;\r
35695                     break;\r
35696             }\r
35697             \r
35698             var sw = this.snap(w, wi, mw);\r
35699             var sh = this.snap(h, hi, mh);\r
35700             if(sw != w || sh != h){\r
35701                 switch(pos){\r
35702                     case 'northeast':\r
35703                         y -= sh - h;\r
35704                     break;\r
35705                     case 'north':\r
35706                         y -= sh - h;\r
35707                         break;\r
35708                     case 'southwest':\r
35709                         x -= sw - w;\r
35710                     break;\r
35711                     case 'west':\r
35712                         x -= sw - w;\r
35713                         break;\r
35714                     case 'northwest':\r
35715                         x -= sw - w;\r
35716                         y -= sh - h;\r
35717                     break;\r
35718                 }\r
35719                 w = sw;\r
35720                 h = sh;\r
35721             }\r
35722             \r
35723             if(this.preserveRatio){\r
35724                 switch(pos){\r
35725                     case 'southeast':\r
35726                     case 'east':\r
35727                         h = oh * (w/ow);\r
35728                         h = Math.min(Math.max(mh, h), mxh);\r
35729                         w = ow * (h/oh);\r
35730                        break;\r
35731                     case 'south':\r
35732                         w = ow * (h/oh);\r
35733                         w = Math.min(Math.max(mw, w), mxw);\r
35734                         h = oh * (w/ow);\r
35735                         break;\r
35736                     case 'northeast':\r
35737                         w = ow * (h/oh);\r
35738                         w = Math.min(Math.max(mw, w), mxw);\r
35739                         h = oh * (w/ow);\r
35740                     break;\r
35741                     case 'north':\r
35742                         tw = w;\r
35743                         w = ow * (h/oh);\r
35744                         w = Math.min(Math.max(mw, w), mxw);\r
35745                         h = oh * (w/ow);\r
35746                         x += (tw - w) / 2;\r
35747                         break;\r
35748                     case 'southwest':\r
35749                         h = oh * (w/ow);\r
35750                         h = Math.min(Math.max(mh, h), mxh);\r
35751                         tw = w;\r
35752                         w = ow * (h/oh);\r
35753                         x += tw - w;\r
35754                         break;\r
35755                     case 'west':\r
35756                         th = h;\r
35757                         h = oh * (w/ow);\r
35758                         h = Math.min(Math.max(mh, h), mxh);\r
35759                         y += (th - h) / 2;\r
35760                         tw = w;\r
35761                         w = ow * (h/oh);\r
35762                         x += tw - w;\r
35763                        break;\r
35764                     case 'northwest':\r
35765                         tw = w;\r
35766                         th = h;\r
35767                         h = oh * (w/ow);\r
35768                         h = Math.min(Math.max(mh, h), mxh);\r
35769                         w = ow * (h/oh);\r
35770                         y += th - h;\r
35771                         x += tw - w;\r
35772                         break;\r
35773                         \r
35774                 }\r
35775             }\r
35776             this.proxy.setBounds(x, y, w, h);\r
35777             if(this.dynamic){\r
35778                 this.resizeElement();\r
35779             }\r
35780             }catch(ex){}\r
35781         }\r
35782     },\r
35783 \r
35784     // private\r
35785     handleOver : function(){\r
35786         if(this.enabled){\r
35787             this.el.addClass('x-resizable-over');\r
35788         }\r
35789     },\r
35790 \r
35791     // private\r
35792     handleOut : function(){\r
35793         if(!this.resizing){\r
35794             this.el.removeClass('x-resizable-over');\r
35795         }\r
35796     },\r
35797     \r
35798     /**\r
35799      * Returns the element this component is bound to.\r
35800      * @return {Ext.Element}\r
35801      */\r
35802     getEl : function(){\r
35803         return this.el;\r
35804     },\r
35805     \r
35806     /**\r
35807      * Returns the resizeChild element (or null).\r
35808      * @return {Ext.Element}\r
35809      */\r
35810     getResizeChild : function(){\r
35811         return this.resizeChild;\r
35812     },\r
35813     \r
35814     /**\r
35815      * Destroys this resizable. If the element was wrapped and \r
35816      * removeEl is not true then the element remains.\r
35817      * @param {Boolean} removeEl (optional) true to remove the element from the DOM\r
35818      */\r
35819     destroy : function(removeEl){\r
35820         Ext.destroy(this.dd, this.overlay, this.proxy);\r
35821         this.overlay = null;\r
35822         this.proxy = null;\r
35823         \r
35824         var ps = Ext.Resizable.positions;\r
35825         for(var k in ps){\r
35826             if(typeof ps[k] != 'function' && this[ps[k]]){\r
35827                 this[ps[k]].destroy();\r
35828             }\r
35829         }\r
35830         if(removeEl){\r
35831             this.el.update('');\r
35832             Ext.destroy(this.el);\r
35833             this.el = null;\r
35834         }\r
35835         this.purgeListeners();\r
35836     },\r
35837 \r
35838     syncHandleHeight : function(){\r
35839         var h = this.el.getHeight(true);\r
35840         if(this.west){\r
35841             this.west.el.setHeight(h);\r
35842         }\r
35843         if(this.east){\r
35844             this.east.el.setHeight(h);\r
35845         }\r
35846     }\r
35847 });\r
35848 \r
35849 // private\r
35850 // hash to map config positions to true positions\r
35851 Ext.Resizable.positions = {\r
35852     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'\r
35853 };\r
35854 \r
35855 // private\r
35856 Ext.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){\r
35857     if(!this.tpl){\r
35858         // only initialize the template if resizable is used\r
35859         var tpl = Ext.DomHelper.createTemplate(\r
35860             {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}\r
35861         );\r
35862         tpl.compile();\r
35863         Ext.Resizable.Handle.prototype.tpl = tpl;\r
35864     }\r
35865     this.position = pos;\r
35866     this.rz = rz;\r
35867     this.el = this.tpl.append(rz.el.dom, [this.position], true);\r
35868     this.el.unselectable();\r
35869     if(transparent){\r
35870         this.el.setOpacity(0);\r
35871     }\r
35872     this.el.on('mousedown', this.onMouseDown, this);\r
35873     if(!disableTrackOver){\r
35874         this.el.on({\r
35875             scope: this,\r
35876             mouseover: this.onMouseOver,\r
35877             mouseout: this.onMouseOut\r
35878         });\r
35879     }\r
35880 };\r
35881 \r
35882 // private\r
35883 Ext.Resizable.Handle.prototype = {\r
35884     // private\r
35885     afterResize : function(rz){\r
35886         // do nothing    \r
35887     },\r
35888     // private\r
35889     onMouseDown : function(e){\r
35890         this.rz.onMouseDown(this, e);\r
35891     },\r
35892     // private\r
35893     onMouseOver : function(e){\r
35894         this.rz.handleOver(this, e);\r
35895     },\r
35896     // private\r
35897     onMouseOut : function(e){\r
35898         this.rz.handleOut(this, e);\r
35899     },\r
35900     // private\r
35901     destroy : function(){\r
35902         Ext.destroy(this.el);\r
35903         this.el = null;\r
35904     }\r
35905 };\r
35906 /**
35907  * @class Ext.Window
35908  * @extends Ext.Panel
35909  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
35910  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
35911  * restored to their prior size, and can be {@link #minimize}d.</p>
35912  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide 
35913  * grouping, activation, to front, to back and other application-specific behavior.</p>
35914  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
35915  * specify {@link Ext.Component#renderTo renderTo}.</p>
35916  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
35917  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
35918  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
35919  * @constructor
35920  * @param {Object} config The config object
35921  * @xtype window
35922  */
35923 Ext.Window = Ext.extend(Ext.Panel, {
35924     /**
35925      * @cfg {Number} x
35926      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
35927      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
35928      */
35929     /**
35930      * @cfg {Number} y
35931      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
35932      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
35933      */
35934     /**
35935      * @cfg {Boolean} modal
35936      * True to make the window modal and mask everything behind it when displayed, false to display it without
35937      * restricting access to other UI elements (defaults to false).
35938      */
35939     /**
35940      * @cfg {String/Element} animateTarget
35941      * Id or element from which the window should animate while opening (defaults to null with no animation).
35942      */
35943     /**
35944      * @cfg {String} resizeHandles
35945      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
35946      */
35947     /**
35948      * @cfg {Ext.WindowGroup} manager
35949      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
35950      */
35951     /**
35952     * @cfg {String/Number/Button} defaultButton
35953     * The id / index of a button or a button instance to focus when this window received the focus.
35954     */
35955     /**
35956     * @cfg {Function} onEsc
35957     * Allows override of the built-in processing for the escape key. Default action
35958     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
35959     * To prevent the Window closing when the escape key is pressed, specify this as
35960     * Ext.emptyFn (See {@link Ext#emptyFn}).
35961     */
35962     /**
35963      * @cfg {Boolean} collapsed
35964      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if 
35965      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window 
35966      * will always be expanded when shown.
35967      */
35968     /**
35969      * @cfg {Boolean} maximized
35970      * True to initially display the window in a maximized state. (Defaults to false).
35971      */
35972     
35973     /**
35974     * @cfg {String} baseCls
35975     * The base CSS class to apply to this panel's element (defaults to 'x-window').
35976     */
35977     baseCls : 'x-window',
35978     /**
35979      * @cfg {Boolean} resizable
35980      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
35981      */
35982     resizable : true,
35983     /**
35984      * @cfg {Boolean} draggable
35985      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
35986      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
35987      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
35988      */
35989     draggable : true,
35990     /**
35991      * @cfg {Boolean} closable
35992      * <p>True to display the 'close' tool button and allow the user to close the window, false to
35993      * hide the button and disallow closing the window (defaults to true).</p>
35994      * <p>By default, when close is requested by either clicking the close button in the header
35995      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
35996      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
35997      * it may not be reused.</p>
35998      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
35999      * {@link #closeAction} to 'hide'.
36000      */
36001     closable : true,
36002     /**
36003      * @cfg {String} closeAction
36004      * <p>The action to take when the close header tool is clicked:
36005      * <div class="mdetail-params"><ul>
36006      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
36007      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
36008      * it and all descendant Components. The window will <b>not</b> be available to be
36009      * redisplayed via the {@link #show} method.
36010      * </div></li>
36011      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
36012      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
36013      * The window will be available to be redisplayed via the {@link #show} method.
36014      * </div></li>
36015      * </ul></div>
36016      * <p><b>Note:</b> This setting does not affect the {@link #close} method
36017      * which will always {@link Ext.Component#destroy destroy} the window. To
36018      * programatically <i>hide</i> a window, call {@link #hide}.</p>
36019      */
36020     closeAction : 'close',
36021     /**
36022      * @cfg {Boolean} constrain
36023      * True to constrain the window within its containing element, false to allow it to fall outside of its
36024      * containing element. By default the window will be rendered to document.body.  To render and constrain the 
36025      * window within another element specify {@link #renderTo}.
36026      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
36027      */
36028     constrain : false,
36029     /**
36030      * @cfg {Boolean} constrainHeader
36031      * True to constrain the window header within its containing element (allowing the window body to fall outside 
36032      * of its containing element) or false to allow the header to fall outside its containing element (defaults to 
36033      * false). Optionally the entire window can be constrained using {@link #constrain}.
36034      */
36035     constrainHeader : false,
36036     /**
36037      * @cfg {Boolean} plain
36038      * True to render the window body with a transparent background so that it will blend into the framing
36039      * elements, false to add a lighter background color to visually highlight the body element and separate it
36040      * more distinctly from the surrounding frame (defaults to false).
36041      */
36042     plain : false,
36043     /**
36044      * @cfg {Boolean} minimizable
36045      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
36046      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
36047      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
36048      * custom minimize behavior implemented for this option to be useful.
36049      */
36050     minimizable : false,
36051     /**
36052      * @cfg {Boolean} maximizable
36053      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
36054      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
36055      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
36056      * restore the window to its previous size.
36057      */
36058     maximizable : false,
36059     /**
36060      * @cfg {Number} minHeight
36061      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
36062      */
36063     minHeight : 100,
36064     /**
36065      * @cfg {Number} minWidth
36066      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
36067      */
36068     minWidth : 200,
36069     /**
36070      * @cfg {Boolean} expandOnShow
36071      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
36072      * {@link #collapsed}) when displayed (defaults to true).
36073      */
36074     expandOnShow : true,
36075
36076     // inherited docs, same default
36077     collapsible : false,
36078
36079     /**
36080      * @cfg {Boolean} initHidden
36081      * True to hide the window until show() is explicitly called (defaults to true).
36082      */
36083     initHidden : true,
36084     /**
36085     * @cfg {Boolean} monitorResize @hide
36086     * This is automatically managed based on the value of constrain and constrainToHeader
36087     */
36088     monitorResize : true,
36089
36090     // The following configs are set to provide the basic functionality of a window.
36091     // Changing them would require additional code to handle correctly and should
36092     // usually only be done in subclasses that can provide custom behavior.  Changing them
36093     // may have unexpected or undesirable results.
36094     /** @cfg {String} elements @hide */
36095     elements : 'header,body',
36096     /** @cfg {Boolean} frame @hide */
36097     frame : true,
36098     /** @cfg {Boolean} floating @hide */
36099     floating : true,
36100
36101     // private
36102     initComponent : function(){
36103         Ext.Window.superclass.initComponent.call(this);
36104         this.addEvents(
36105             /**
36106              * @event activate
36107              * Fires after the window has been visually activated via {@link setActive}.
36108              * @param {Ext.Window} this
36109              */
36110             /**
36111              * @event deactivate
36112              * Fires after the window has been visually deactivated via {@link setActive}.
36113              * @param {Ext.Window} this
36114              */
36115             /**
36116              * @event resize
36117              * Fires after the window has been resized.
36118              * @param {Ext.Window} this
36119              * @param {Number} width The window's new width
36120              * @param {Number} height The window's new height
36121              */
36122             'resize',
36123             /**
36124              * @event maximize
36125              * Fires after the window has been maximized.
36126              * @param {Ext.Window} this
36127              */
36128             'maximize',
36129             /**
36130              * @event minimize
36131              * Fires after the window has been minimized.
36132              * @param {Ext.Window} this
36133              */
36134             'minimize',
36135             /**
36136              * @event restore
36137              * Fires after the window has been restored to its original size after being maximized.
36138              * @param {Ext.Window} this
36139              */
36140             'restore'
36141         );
36142         if(this.initHidden === false){
36143             this.show();
36144         }else{
36145             this.hidden = true;
36146         }
36147     },
36148
36149     // private
36150     getState : function(){
36151         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
36152     },
36153
36154     // private
36155     onRender : function(ct, position){
36156         Ext.Window.superclass.onRender.call(this, ct, position);
36157
36158         if(this.plain){
36159             this.el.addClass('x-window-plain');
36160         }
36161
36162         // this element allows the Window to be focused for keyboard events
36163         this.focusEl = this.el.createChild({
36164                     tag: 'a', href:'#', cls:'x-dlg-focus',
36165                     tabIndex:'-1', html: '&#160;'});
36166         this.focusEl.swallowEvent('click', true);
36167
36168         this.proxy = this.el.createProxy('x-window-proxy');
36169         this.proxy.enableDisplayMode('block');
36170
36171         if(this.modal){
36172             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
36173             this.mask.enableDisplayMode('block');
36174             this.mask.hide();
36175             this.mon(this.mask, 'click', this.focus, this);
36176         }
36177         this.initTools();
36178     },
36179
36180     // private
36181     initEvents : function(){
36182         Ext.Window.superclass.initEvents.call(this);
36183         if(this.animateTarget){
36184             this.setAnimateTarget(this.animateTarget);
36185         }
36186
36187         if(this.resizable){
36188             this.resizer = new Ext.Resizable(this.el, {
36189                 minWidth: this.minWidth,
36190                 minHeight:this.minHeight,
36191                 handles: this.resizeHandles || 'all',
36192                 pinned: true,
36193                 resizeElement : this.resizerAction
36194             });
36195             this.resizer.window = this;
36196             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
36197         }
36198
36199         if(this.draggable){
36200             this.header.addClass('x-window-draggable');
36201         }
36202         this.mon(this.el, 'mousedown', this.toFront, this);
36203         this.manager = this.manager || Ext.WindowMgr;
36204         this.manager.register(this);
36205         if(this.maximized){
36206             this.maximized = false;
36207             this.maximize();
36208         }
36209         if(this.closable){
36210             var km = this.getKeyMap();
36211             km.on(27, this.onEsc, this);
36212             km.disable();
36213         }
36214     },
36215
36216     initDraggable : function(){
36217         /**
36218          * If this Window is configured {@link #draggable}, this property will contain
36219          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.
36220          * @type Ext.dd.DD
36221          * @property dd
36222          */
36223         this.dd = new Ext.Window.DD(this);
36224     },
36225
36226    // private
36227     onEsc : function(){
36228         this[this.closeAction]();
36229     },
36230
36231     // private
36232     beforeDestroy : function(){
36233         if (this.rendered){
36234             this.hide();
36235           if(this.doAnchor){
36236                 Ext.EventManager.removeResizeListener(this.doAnchor, this);
36237               Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
36238             }
36239             Ext.destroy(
36240                 this.focusEl,
36241                 this.resizer,
36242                 this.dd,
36243                 this.proxy,
36244                 this.mask
36245             );
36246         }
36247         Ext.Window.superclass.beforeDestroy.call(this);
36248     },
36249
36250     // private
36251     onDestroy : function(){
36252         if(this.manager){
36253             this.manager.unregister(this);
36254         }
36255         Ext.Window.superclass.onDestroy.call(this);
36256     },
36257
36258     // private
36259     initTools : function(){
36260         if(this.minimizable){
36261             this.addTool({
36262                 id: 'minimize',
36263                 handler: this.minimize.createDelegate(this, [])
36264             });
36265         }
36266         if(this.maximizable){
36267             this.addTool({
36268                 id: 'maximize',
36269                 handler: this.maximize.createDelegate(this, [])
36270             });
36271             this.addTool({
36272                 id: 'restore',
36273                 handler: this.restore.createDelegate(this, []),
36274                 hidden:true
36275             });
36276             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
36277         }
36278         if(this.closable){
36279             this.addTool({
36280                 id: 'close',
36281                 handler: this[this.closeAction].createDelegate(this, [])
36282             });
36283         }
36284     },
36285
36286     // private
36287     resizerAction : function(){
36288         var box = this.proxy.getBox();
36289         this.proxy.hide();
36290         this.window.handleResize(box);
36291         return box;
36292     },
36293
36294     // private
36295     beforeResize : function(){
36296         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
36297         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
36298         this.resizeBox = this.el.getBox();
36299     },
36300
36301     // private
36302     updateHandles : function(){
36303         if(Ext.isIE && this.resizer){
36304             this.resizer.syncHandleHeight();
36305             this.el.repaint();
36306         }
36307     },
36308
36309     // private
36310     handleResize : function(box){
36311         var rz = this.resizeBox;
36312         if(rz.x != box.x || rz.y != box.y){
36313             this.updateBox(box);
36314         }else{
36315             this.setSize(box);
36316         }
36317         this.focus();
36318         this.updateHandles();
36319         this.saveState();
36320         this.doLayout();
36321         this.fireEvent('resize', this, box.width, box.height);
36322     },
36323
36324     /**
36325      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
36326      * window itself will receive focus.
36327      */
36328     focus : function(){
36329         var f = this.focusEl, db = this.defaultButton, t = typeof db;
36330         if(t != 'undefined'){
36331             if(t == 'number' && this.fbar){
36332                 f = this.fbar.items.get(db);
36333             }else if(t == 'string'){
36334                 f = Ext.getCmp(db);
36335             }else{
36336                 f = db;
36337             }
36338         }
36339         f = f || this.focusEl;
36340         f.focus.defer(10, f);
36341     },
36342
36343     /**
36344      * Sets the target element from which the window should animate while opening.
36345      * @param {String/Element} el The target element or id
36346      */
36347     setAnimateTarget : function(el){
36348         el = Ext.get(el);
36349         this.animateTarget = el;
36350     },
36351
36352     // private
36353     beforeShow : function(){
36354         delete this.el.lastXY;
36355         delete this.el.lastLT;
36356         if(this.x === undefined || this.y === undefined){
36357             var xy = this.el.getAlignToXY(this.container, 'c-c');
36358             var pos = this.el.translatePoints(xy[0], xy[1]);
36359             this.x = this.x === undefined? pos.left : this.x;
36360             this.y = this.y === undefined? pos.top : this.y;
36361         }
36362         this.el.setLeftTop(this.x, this.y);
36363
36364         if(this.expandOnShow){
36365             this.expand(false);
36366         }
36367
36368         if(this.modal){
36369             Ext.getBody().addClass('x-body-masked');
36370             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
36371             this.mask.show();
36372         }
36373     },
36374
36375     /**
36376      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
36377      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
36378      * animate while opening (defaults to null with no animation)
36379      * @param {Function} callback (optional) A callback function to call after the window is displayed
36380      * @param {Object} scope (optional) The scope in which to execute the callback
36381      * @return {Ext.Window} this
36382      */
36383     show : function(animateTarget, cb, scope){
36384         if(!this.rendered){
36385             this.render(Ext.getBody());
36386         }
36387         if(this.hidden === false){
36388             this.toFront();
36389             return this;
36390         }
36391         if(this.fireEvent('beforeshow', this) === false){
36392             return this;
36393         }
36394         if(cb){
36395             this.on('show', cb, scope, {single:true});
36396         }
36397         this.hidden = false;
36398         if(animateTarget !== undefined){
36399             this.setAnimateTarget(animateTarget);
36400         }
36401         this.beforeShow();
36402         if(this.animateTarget){
36403             this.animShow();
36404         }else{
36405             this.afterShow();
36406         }
36407         return this;
36408     },
36409
36410     // private
36411     afterShow : function(isAnim){
36412         this.proxy.hide();
36413         this.el.setStyle('display', 'block');
36414         this.el.show();
36415         if(this.maximized){
36416             this.fitContainer();
36417         }
36418         if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
36419             this.cascade(this.setAutoScroll);
36420         }
36421
36422         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
36423             Ext.EventManager.onWindowResize(this.onWindowResize, this);
36424         }
36425         this.doConstrain();
36426         this.doLayout();
36427         if(this.keyMap){
36428             this.keyMap.enable();
36429         }
36430         this.toFront();
36431         this.updateHandles();
36432         if(isAnim && (Ext.isIE || Ext.isWebKit)){
36433             var sz = this.getSize();
36434             this.onResize(sz.width, sz.height);
36435         }
36436         this.fireEvent('show', this);
36437     },
36438
36439     // private
36440     animShow : function(){
36441         this.proxy.show();
36442         this.proxy.setBox(this.animateTarget.getBox());
36443         this.proxy.setOpacity(0);
36444         var b = this.getBox(false);
36445         b.callback = this.afterShow.createDelegate(this, [true], false);
36446         b.scope = this;
36447         b.duration = 0.25;
36448         b.easing = 'easeNone';
36449         b.opacity = 0.5;
36450         b.block = true;
36451         this.el.setStyle('display', 'none');
36452         this.proxy.shift(b);
36453     },
36454
36455     /**
36456      * Hides the window, setting it to invisible and applying negative offsets.
36457      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
36458      * animate while hiding (defaults to null with no animation)
36459      * @param {Function} callback (optional) A callback function to call after the window is hidden
36460      * @param {Object} scope (optional) The scope in which to execute the callback
36461      * @return {Ext.Window} this
36462      */
36463     hide : function(animateTarget, cb, scope){
36464         if(this.hidden || this.fireEvent('beforehide', this) === false){
36465             return this;
36466         }
36467         if(cb){
36468             this.on('hide', cb, scope, {single:true});
36469         }
36470         this.hidden = true;
36471         if(animateTarget !== undefined){
36472             this.setAnimateTarget(animateTarget);
36473         }
36474         if(this.modal){
36475             this.mask.hide();
36476             Ext.getBody().removeClass('x-body-masked');
36477         }
36478         if(this.animateTarget){
36479             this.animHide();
36480         }else{
36481             this.el.hide();
36482             this.afterHide();
36483         }
36484         return this;
36485     },
36486
36487     // private
36488     afterHide : function(){
36489         this.proxy.hide();
36490         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
36491             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
36492         }
36493         if(this.keyMap){
36494             this.keyMap.disable();
36495         }
36496         this.fireEvent('hide', this);
36497     },
36498
36499     // private
36500     animHide : function(){
36501         this.proxy.setOpacity(0.5);
36502         this.proxy.show();
36503         var tb = this.getBox(false);
36504         this.proxy.setBox(tb);
36505         this.el.hide();
36506         var b = this.animateTarget.getBox();
36507         b.callback = this.afterHide;
36508         b.scope = this;
36509         b.duration = 0.25;
36510         b.easing = 'easeNone';
36511         b.block = true;
36512         b.opacity = 0;
36513         this.proxy.shift(b);
36514     },
36515
36516     // private
36517     onWindowResize : function(){
36518         if(this.maximized){
36519             this.fitContainer();
36520         }
36521         if(this.modal){
36522             this.mask.setSize('100%', '100%');
36523             var force = this.mask.dom.offsetHeight;
36524             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
36525         }
36526         this.doConstrain();
36527     },
36528
36529     // private
36530     doConstrain : function(){
36531         if(this.constrain || this.constrainHeader){
36532             var offsets;
36533             if(this.constrain){
36534                 offsets = {
36535                     right:this.el.shadowOffset,
36536                     left:this.el.shadowOffset,
36537                     bottom:this.el.shadowOffset
36538                 };
36539             }else {
36540                 var s = this.getSize();
36541                 offsets = {
36542                     right:-(s.width - 100),
36543                     bottom:-(s.height - 25)
36544                 };
36545             }
36546
36547             var xy = this.el.getConstrainToXY(this.container, true, offsets);
36548             if(xy){
36549                 this.setPosition(xy[0], xy[1]);
36550             }
36551         }
36552     },
36553
36554     // private - used for dragging
36555     ghost : function(cls){
36556         var ghost = this.createGhost(cls);
36557         var box = this.getBox(true);
36558         ghost.setLeftTop(box.x, box.y);
36559         ghost.setWidth(box.width);
36560         this.el.hide();
36561         this.activeGhost = ghost;
36562         return ghost;
36563     },
36564
36565     // private
36566     unghost : function(show, matchPosition){
36567         if(!this.activeGhost) {
36568             return;
36569         }
36570         if(show !== false){
36571             this.el.show();
36572             this.focus();
36573             if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
36574                 this.cascade(this.setAutoScroll);
36575             }
36576         }
36577         if(matchPosition !== false){
36578             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
36579         }
36580         this.activeGhost.hide();
36581         this.activeGhost.remove();
36582         delete this.activeGhost;
36583     },
36584
36585     /**
36586      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
36587      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
36588      * either the minimize event can be handled or this method can be overridden.
36589      * @return {Ext.Window} this
36590      */
36591     minimize : function(){
36592         this.fireEvent('minimize', this);
36593         return this;
36594     },
36595
36596     /**
36597      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
36598      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
36599      * event is fired before the close happens and will cancel the close action if it returns false.<p>
36600      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
36601      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
36602      * To hide the Window without destroying it, call {@link #hide}.</p>
36603      */
36604     close : function(){
36605         if(this.fireEvent('beforeclose', this) !== false){
36606             this.hide(null, function(){
36607                 this.fireEvent('close', this);
36608                 this.destroy();
36609             }, this);
36610         }
36611     },
36612
36613     /**
36614      * Fits the window within its current container and automatically replaces
36615      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
36616      * Also see {@link #toggleMaximize}.
36617      * @return {Ext.Window} this
36618      */
36619     maximize : function(){
36620         if(!this.maximized){
36621             this.expand(false);
36622             this.restoreSize = this.getSize();
36623             this.restorePos = this.getPosition(true);
36624             if (this.maximizable){
36625                 this.tools.maximize.hide();
36626                 this.tools.restore.show();
36627             }
36628             this.maximized = true;
36629             this.el.disableShadow();
36630
36631             if(this.dd){
36632                 this.dd.lock();
36633             }
36634             if(this.collapsible){
36635                 this.tools.toggle.hide();
36636             }
36637             this.el.addClass('x-window-maximized');
36638             this.container.addClass('x-window-maximized-ct');
36639
36640             this.setPosition(0, 0);
36641             this.fitContainer();
36642             this.fireEvent('maximize', this);
36643         }
36644         return this;
36645     },
36646
36647     /**
36648      * Restores a {@link #maximizable maximized}  window back to its original
36649      * size and position prior to being maximized and also replaces
36650      * the 'restore' tool button with the 'maximize' tool button.
36651      * Also see {@link #toggleMaximize}.
36652      * @return {Ext.Window} this
36653      */
36654     restore : function(){
36655         if(this.maximized){
36656             this.el.removeClass('x-window-maximized');
36657             this.tools.restore.hide();
36658             this.tools.maximize.show();
36659             this.setPosition(this.restorePos[0], this.restorePos[1]);
36660             this.setSize(this.restoreSize.width, this.restoreSize.height);
36661             delete this.restorePos;
36662             delete this.restoreSize;
36663             this.maximized = false;
36664             this.el.enableShadow(true);
36665
36666             if(this.dd){
36667                 this.dd.unlock();
36668             }
36669             if(this.collapsible){
36670                 this.tools.toggle.show();
36671             }
36672             this.container.removeClass('x-window-maximized-ct');
36673
36674             this.doConstrain();
36675             this.fireEvent('restore', this);
36676         }
36677         return this;
36678     },
36679
36680     /**
36681      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
36682      * state of the window.
36683      * @return {Ext.Window} this
36684      */
36685     toggleMaximize : function(){
36686         return this[this.maximized ? 'restore' : 'maximize']();
36687     },
36688
36689     // private
36690     fitContainer : function(){
36691         var vs = this.container.getViewSize();
36692         this.setSize(vs.width, vs.height);
36693     },
36694
36695     // private
36696     // z-index is managed by the WindowManager and may be overwritten at any time
36697     setZIndex : function(index){
36698         if(this.modal){
36699             this.mask.setStyle('z-index', index);
36700         }
36701         this.el.setZIndex(++index);
36702         index += 5;
36703
36704         if(this.resizer){
36705             this.resizer.proxy.setStyle('z-index', ++index);
36706         }
36707
36708         this.lastZIndex = index;
36709     },
36710
36711     /**
36712      * Aligns the window to the specified element
36713      * @param {Mixed} element The element to align to.
36714      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details).
36715      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36716      * @return {Ext.Window} this
36717      */
36718     alignTo : function(element, position, offsets){
36719         var xy = this.el.getAlignToXY(element, position, offsets);
36720         this.setPagePosition(xy[0], xy[1]);
36721         return this;
36722     },
36723
36724     /**
36725      * Anchors this window to another element and realigns it when the window is resized or scrolled.
36726      * @param {Mixed} element The element to align to.
36727      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
36728      * @param {Array} offsets (optional) Offset the positioning by [x, y]
36729      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
36730      * is a number, it is used as the buffer delay (defaults to 50ms).
36731      * @return {Ext.Window} this
36732      */
36733     anchorTo : function(el, alignment, offsets, monitorScroll){
36734       if(this.doAnchor){
36735           Ext.EventManager.removeResizeListener(this.doAnchor, this);
36736           Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
36737       }
36738       this.doAnchor = function(){
36739           this.alignTo(el, alignment, offsets);
36740       };
36741       Ext.EventManager.onWindowResize(this.doAnchor, this);
36742       
36743       var tm = typeof monitorScroll;
36744       if(tm != 'undefined'){
36745           Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
36746               {buffer: tm == 'number' ? monitorScroll : 50});
36747       }
36748       this.doAnchor();
36749       return this;
36750     },
36751
36752     /**
36753      * Brings this window to the front of any other visible windows
36754      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
36755      * @return {Ext.Window} this
36756      */
36757     toFront : function(e){
36758         if(this.manager.bringToFront(this)){
36759             if(!e || !e.getTarget().focus){
36760                 this.focus();
36761             }
36762         }
36763         return this;
36764     },
36765
36766     /**
36767      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
36768      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred.
36769      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
36770      */
36771     setActive : function(active){
36772         if(active){
36773             if(!this.maximized){
36774                 this.el.enableShadow(true);
36775             }
36776             this.fireEvent('activate', this);
36777         }else{
36778             this.el.disableShadow();
36779             this.fireEvent('deactivate', this);
36780         }
36781     },
36782
36783     /**
36784      * Sends this window to the back of (lower z-index than) any other visible windows
36785      * @return {Ext.Window} this
36786      */
36787     toBack : function(){
36788         this.manager.sendToBack(this);
36789         return this;
36790     },
36791
36792     /**
36793      * Centers this window in the viewport
36794      * @return {Ext.Window} this
36795      */
36796     center : function(){
36797         var xy = this.el.getAlignToXY(this.container, 'c-c');
36798         this.setPagePosition(xy[0], xy[1]);
36799         return this;
36800     }
36801
36802     /**
36803      * @cfg {Boolean} autoWidth @hide
36804      **/
36805 });
36806 Ext.reg('window', Ext.Window);
36807
36808 // private - custom Window DD implementation
36809 Ext.Window.DD = function(win){
36810     this.win = win;
36811     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
36812     this.setHandleElId(win.header.id);
36813     this.scroll = false;
36814 };
36815
36816 Ext.extend(Ext.Window.DD, Ext.dd.DD, {
36817     moveOnly:true,
36818     headerOffsets:[100, 25],
36819     startDrag : function(){
36820         var w = this.win;
36821         this.proxy = w.ghost();
36822         if(w.constrain !== false){
36823             var so = w.el.shadowOffset;
36824             this.constrainTo(w.container, {right: so, left: so, bottom: so});
36825         }else if(w.constrainHeader !== false){
36826             var s = this.proxy.getSize();
36827             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
36828         }
36829     },
36830     b4Drag : Ext.emptyFn,
36831
36832     onDrag : function(e){
36833         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
36834     },
36835
36836     endDrag : function(e){
36837         this.win.unghost();
36838         this.win.saveState();
36839     }
36840 });
36841 /**
36842  * @class Ext.WindowGroup
36843  * An object that represents a group of {@link Ext.Window} instances and provides z-order management
36844  * and window activation behavior.
36845  * @constructor
36846  */
36847 Ext.WindowGroup = function(){
36848     var list = {};
36849     var accessList = [];
36850     var front = null;
36851
36852     // private
36853     var sortWindows = function(d1, d2){
36854         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
36855     };
36856
36857     // private
36858     var orderWindows = function(){
36859         var a = accessList, len = a.length;
36860         if(len > 0){
36861             a.sort(sortWindows);
36862             var seed = a[0].manager.zseed;
36863             for(var i = 0; i < len; i++){
36864                 var win = a[i];
36865                 if(win && !win.hidden){
36866                     win.setZIndex(seed + (i*10));
36867                 }
36868             }
36869         }
36870         activateLast();
36871     };
36872
36873     // private
36874     var setActiveWin = function(win){
36875         if(win != front){
36876             if(front){
36877                 front.setActive(false);
36878             }
36879             front = win;
36880             if(win){
36881                 win.setActive(true);
36882             }
36883         }
36884     };
36885
36886     // private
36887     var activateLast = function(){
36888         for(var i = accessList.length-1; i >=0; --i) {
36889             if(!accessList[i].hidden){
36890                 setActiveWin(accessList[i]);
36891                 return;
36892             }
36893         }
36894         // none to activate
36895         setActiveWin(null);
36896     };
36897
36898     return {
36899         /**
36900          * The starting z-index for windows (defaults to 9000)
36901          * @type Number The z-index value
36902          */
36903         zseed : 9000,
36904
36905         // private
36906         register : function(win){
36907             list[win.id] = win;
36908             accessList.push(win);
36909             win.on('hide', activateLast);
36910         },
36911
36912         // private
36913         unregister : function(win){
36914             delete list[win.id];
36915             win.un('hide', activateLast);
36916             accessList.remove(win);
36917         },
36918
36919         /**
36920          * Gets a registered window by id.
36921          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
36922          * @return {Ext.Window}
36923          */
36924         get : function(id){
36925             return typeof id == "object" ? id : list[id];
36926         },
36927
36928         /**
36929          * Brings the specified window to the front of any other active windows.
36930          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
36931          * @return {Boolean} True if the dialog was brought to the front, else false
36932          * if it was already in front
36933          */
36934         bringToFront : function(win){
36935             win = this.get(win);
36936             if(win != front){
36937                 win._lastAccess = new Date().getTime();
36938                 orderWindows();
36939                 return true;
36940             }
36941             return false;
36942         },
36943
36944         /**
36945          * Sends the specified window to the back of other active windows.
36946          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
36947          * @return {Ext.Window} The window
36948          */
36949         sendToBack : function(win){
36950             win = this.get(win);
36951             win._lastAccess = -(new Date().getTime());
36952             orderWindows();
36953             return win;
36954         },
36955
36956         /**
36957          * Hides all windows in the group.
36958          */
36959         hideAll : function(){
36960             for(var id in list){
36961                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
36962                     list[id].hide();
36963                 }
36964             }
36965         },
36966
36967         /**
36968          * Gets the currently-active window in the group.
36969          * @return {Ext.Window} The active window
36970          */
36971         getActive : function(){
36972             return front;
36973         },
36974
36975         /**
36976          * Returns zero or more windows in the group using the custom search function passed to this method.
36977          * The function should accept a single {@link Ext.Window} reference as its only argument and should
36978          * return true if the window matches the search criteria, otherwise it should return false.
36979          * @param {Function} fn The search function
36980          * @param {Object} scope (optional) The scope in which to execute the function (defaults to the window
36981          * that gets passed to the function if not specified)
36982          * @return {Array} An array of zero or more matching windows
36983          */
36984         getBy : function(fn, scope){
36985             var r = [];
36986             for(var i = accessList.length-1; i >=0; --i) {
36987                 var win = accessList[i];
36988                 if(fn.call(scope||win, win) !== false){
36989                     r.push(win);
36990                 }
36991             }
36992             return r;
36993         },
36994
36995         /**
36996          * Executes the specified function once for every window in the group, passing each
36997          * window as the only parameter. Returning false from the function will stop the iteration.
36998          * @param {Function} fn The function to execute for each item
36999          * @param {Object} scope (optional) The scope in which to execute the function
37000          */
37001         each : function(fn, scope){
37002             for(var id in list){
37003                 if(list[id] && typeof list[id] != "function"){
37004                     if(fn.call(scope || list[id], list[id]) === false){
37005                         return;
37006                     }
37007                 }
37008             }
37009         }
37010     };
37011 };
37012
37013
37014 /**
37015  * @class Ext.WindowMgr
37016  * @extends Ext.WindowGroup
37017  * The default global window group that is available automatically.  To have more than one group of windows
37018  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
37019  * @singleton
37020  */
37021 Ext.WindowMgr = new Ext.WindowGroup();/**
37022  * @class Ext.MessageBox
37023  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>
37024  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt
37025  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code
37026  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function
37027  * (see the <code>function</code> parameter for {@link #show} for more details).</p>
37028  * <p>Example usage:</p>
37029  *<pre><code>
37030 // Basic alert:
37031 Ext.Msg.alert('Status', 'Changes saved successfully.');
37032
37033 // Prompt for user data and process the result using a callback:
37034 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
37035     if (btn == 'ok'){
37036         // process text value and close...
37037     }
37038 });
37039
37040 // Show a dialog using config options:
37041 Ext.Msg.show({
37042    title:'Save Changes?',
37043    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
37044    buttons: Ext.Msg.YESNOCANCEL,
37045    fn: processResult,
37046    animEl: 'elId',
37047    icon: Ext.MessageBox.QUESTION
37048 });
37049 </code></pre>
37050  * @singleton
37051  */
37052 Ext.MessageBox = function(){
37053     var dlg, opt, mask, waitTimer;
37054     var bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl;
37055     var buttons, activeTextEl, bwidth, iconCls = '';
37056
37057     // private
37058     var handleButton = function(button){
37059         if(dlg.isVisible()){
37060             dlg.hide();
37061             handleHide();
37062             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
37063         }
37064     };
37065
37066     // private
37067     var handleHide = function(){
37068         if(opt && opt.cls){
37069             dlg.el.removeClass(opt.cls);
37070         }
37071         progressBar.reset();
37072     };
37073
37074     // private
37075     var handleEsc = function(d, k, e){
37076         if(opt && opt.closable !== false){
37077             dlg.hide();
37078             handleHide();
37079         }
37080         if(e){
37081             e.stopEvent();
37082         }
37083     };
37084
37085     // private
37086     var updateButtons = function(b){
37087         var width = 0;
37088         if(!b){
37089             buttons["ok"].hide();
37090             buttons["cancel"].hide();
37091             buttons["yes"].hide();
37092             buttons["no"].hide();
37093             return width;
37094         }
37095         dlg.footer.dom.style.display = '';
37096         for(var k in buttons){
37097             if(typeof buttons[k] != "function"){
37098                 if(b[k]){
37099                     buttons[k].show();
37100                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Ext.MessageBox.buttonText[k]);
37101                     width += buttons[k].el.getWidth()+15;
37102                 }else{
37103                     buttons[k].hide();
37104                 }
37105             }
37106         }
37107         return width;
37108     };
37109
37110     return {
37111         /**
37112          * Returns a reference to the underlying {@link Ext.Window} element
37113          * @return {Ext.Window} The window
37114          */
37115         getDialog : function(titleText){
37116            if(!dlg){
37117                 dlg = new Ext.Window({
37118                     autoCreate : true,
37119                     title:titleText,
37120                     resizable:false,
37121                     constrain:true,
37122                     constrainHeader:true,
37123                     minimizable : false,
37124                     maximizable : false,
37125                     stateful: false,
37126                     modal: true,
37127                     shim:true,
37128                     buttonAlign:"center",
37129                     width:400,
37130                     height:100,
37131                     minHeight: 80,
37132                     plain:true,
37133                     footer:true,
37134                     closable:true,
37135                     close : function(){
37136                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
37137                             handleButton("no");
37138                         }else{
37139                             handleButton("cancel");
37140                         }
37141                     }
37142                 });
37143                 buttons = {};
37144                 var bt = this.buttonText;
37145                 //TODO: refactor this block into a buttons config to pass into the Window constructor
37146                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
37147                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
37148                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
37149                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
37150                 buttons["ok"].hideMode = buttons["yes"].hideMode = buttons["no"].hideMode = buttons["cancel"].hideMode = 'offsets';
37151                 dlg.render(document.body);
37152                 dlg.getEl().addClass('x-window-dlg');
37153                 mask = dlg.mask;
37154                 bodyEl = dlg.body.createChild({
37155                     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>'
37156                 });
37157                 iconEl = Ext.get(bodyEl.dom.firstChild);
37158                 var contentEl = bodyEl.dom.childNodes[1];
37159                 msgEl = Ext.get(contentEl.firstChild);
37160                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);
37161                 textboxEl.enableDisplayMode();
37162                 textboxEl.addKeyListener([10,13], function(){
37163                     if(dlg.isVisible() && opt && opt.buttons){
37164                         if(opt.buttons.ok){
37165                             handleButton("ok");
37166                         }else if(opt.buttons.yes){
37167                             handleButton("yes");
37168                         }
37169                     }
37170                 });
37171                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);
37172                 textareaEl.enableDisplayMode();
37173                 progressBar = new Ext.ProgressBar({
37174                     renderTo:bodyEl
37175                 });
37176                bodyEl.createChild({cls:'x-clear'});
37177             }
37178             return dlg;
37179         },
37180
37181         /**
37182          * Updates the message box body text
37183          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
37184          * the XHTML-compliant non-breaking space character '&amp;#160;')
37185          * @return {Ext.MessageBox} this
37186          */
37187         updateText : function(text){
37188             if(!dlg.isVisible() && !opt.width){
37189                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows
37190             }
37191             msgEl.update(text || '&#160;');
37192
37193             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0;
37194             var mw = msgEl.getWidth() + msgEl.getMargins('lr');
37195             var fw = dlg.getFrameWidth('lr');
37196             var bw = dlg.body.getFrameWidth('lr');
37197             if (Ext.isIE && iw > 0){
37198                 //3 pixels get subtracted in the icon CSS for an IE margin issue,
37199                 //so we have to add it back here for the overall width to be consistent
37200                 iw += 3;
37201             }
37202             var w = Math.max(Math.min(opt.width || iw+mw+fw+bw, this.maxWidth),
37203                         Math.max(opt.minWidth || this.minWidth, bwidth || 0));
37204
37205             if(opt.prompt === true){
37206                 activeTextEl.setWidth(w-iw-fw-bw);
37207             }
37208             if(opt.progress === true || opt.wait === true){
37209                 progressBar.setSize(w-iw-fw-bw);
37210             }
37211             if(Ext.isIE && w == bwidth){
37212                 w += 4; //Add offset when the content width is smaller than the buttons.    
37213             }
37214             dlg.setSize(w, 'auto').center();
37215             return this;
37216         },
37217
37218         /**
37219          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
37220          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},
37221          * or by calling {@link Ext.MessageBox#show} with progress: true.
37222          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)
37223          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')
37224          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined
37225          * so that any existing body text will not get overwritten by default unless a new value is passed in)
37226          * @return {Ext.MessageBox} this
37227          */
37228         updateProgress : function(value, progressText, msg){
37229             progressBar.updateProgress(value, progressText);
37230             if(msg){
37231                 this.updateText(msg);
37232             }
37233             return this;
37234         },
37235
37236         /**
37237          * Returns true if the message box is currently displayed
37238          * @return {Boolean} True if the message box is visible, else false
37239          */
37240         isVisible : function(){
37241             return dlg && dlg.isVisible();
37242         },
37243
37244         /**
37245          * Hides the message box if it is displayed
37246          * @return {Ext.MessageBox} this
37247          */
37248         hide : function(){
37249             var proxy = dlg ? dlg.activeGhost : null;
37250             if(this.isVisible() || proxy){
37251                 dlg.hide();
37252                 handleHide();
37253                 if (proxy){
37254                     // unghost is a private function, but i saw no better solution
37255                     // to fix the locking problem when dragging while it closes
37256                     dlg.unghost(false, false);
37257                 } 
37258             }
37259             return this;
37260         },
37261
37262         /**
37263          * Displays a new message box, or reinitializes an existing message box, based on the config options
37264          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
37265          * although those calls are basic shortcuts and do not support all of the config options allowed here.
37266          * @param {Object} config The following config options are supported: <ul>
37267          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it
37268          * opens and closes (defaults to undefined)</div></li>
37269          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',
37270          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>
37271          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that
37272          * progress and wait dialogs will ignore this property and always hide the close button as they can only
37273          * be closed programmatically.</div></li>
37274          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
37275          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
37276          * if displayed (defaults to 75)</div></li>
37277          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either
37278          * by clicking on the configured buttons, or on the dialog close button, or by pressing
37279          * the return button to enter input.
37280          * <p>Progress and wait dialogs will ignore this option since they do not respond to user
37281          * actions and can only be closed programmatically, so any required function should be called
37282          * by the same code after it closes the dialog. Parameters passed:<ul>
37283          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>
37284          * <li><tt>ok</tt></li>
37285          * <li><tt>yes</tt></li>
37286          * <li><tt>no</tt></li>
37287          * <li><tt>cancel</tt></li>
37288          * </ul></div></div></li>
37289          * <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>
37290          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>
37291          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
37292          * </ul></p></div></li>
37293          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>
37294          * <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
37295          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
37296          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to
37297          * add an optional header icon (defaults to '')</div></li>
37298          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>
37299          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>
37300          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
37301          * displayed (defaults to true)</div></li>
37302          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
37303          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>
37304          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">
37305          * True to prompt the user to enter multi-line text (defaults to false)</div></li>
37306          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
37307          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>
37308          * <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>
37309          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>
37310          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
37311          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
37312          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
37313          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>
37314          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>
37315          * </ul>
37316          * Example usage:
37317          * <pre><code>
37318 Ext.Msg.show({
37319    title: 'Address',
37320    msg: 'Please enter your address:',
37321    width: 300,
37322    buttons: Ext.MessageBox.OKCANCEL,
37323    multiline: true,
37324    fn: saveAddress,
37325    animEl: 'addAddressBtn',
37326    icon: Ext.MessageBox.INFO
37327 });
37328 </code></pre>
37329          * @return {Ext.MessageBox} this
37330          */
37331         show : function(options){
37332             if(this.isVisible()){
37333                 this.hide();
37334             }
37335             opt = options;
37336             var d = this.getDialog(opt.title || "&#160;");
37337
37338             d.setTitle(opt.title || "&#160;");
37339             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);
37340             d.tools.close.setDisplayed(allowClose);
37341             activeTextEl = textboxEl;
37342             opt.prompt = opt.prompt || (opt.multiline ? true : false);
37343             if(opt.prompt){
37344                 if(opt.multiline){
37345                     textboxEl.hide();
37346                     textareaEl.show();
37347                     textareaEl.setHeight(typeof opt.multiline == "number" ?
37348                         opt.multiline : this.defaultTextHeight);
37349                     activeTextEl = textareaEl;
37350                 }else{
37351                     textboxEl.show();
37352                     textareaEl.hide();
37353                 }
37354             }else{
37355                 textboxEl.hide();
37356                 textareaEl.hide();
37357             }
37358             activeTextEl.dom.value = opt.value || "";
37359             if(opt.prompt){
37360                 d.focusEl = activeTextEl;
37361             }else{
37362                 var bs = opt.buttons;
37363                 var db = null;
37364                 if(bs && bs.ok){
37365                     db = buttons["ok"];
37366                 }else if(bs && bs.yes){
37367                     db = buttons["yes"];
37368                 }
37369                 if (db){
37370                     d.focusEl = db;
37371                 }
37372             }
37373             if(opt.iconCls){
37374               d.setIconClass(opt.iconCls);
37375             }
37376             this.setIcon(opt.icon);
37377             if(opt.cls){
37378                 d.el.addClass(opt.cls);
37379             }
37380             d.proxyDrag = opt.proxyDrag === true;
37381             d.modal = opt.modal !== false;
37382             d.mask = opt.modal !== false ? mask : false;
37383             
37384             d.on('show', function(){
37385                 //workaround for window internally enabling keymap in afterShow
37386                 d.keyMap.setDisabled(allowClose !== true);
37387                 d.doLayout();
37388                 this.setIcon(opt.icon);
37389                 bwidth = updateButtons(opt.buttons);
37390                 progressBar.setVisible(opt.progress === true || opt.wait === true);
37391                 this.updateProgress(0, opt.progressText);
37392                 this.updateText(opt.msg);
37393                 if(opt.wait === true){
37394                     progressBar.wait(opt.waitConfig);
37395                 }
37396
37397             }, this, {single:true});
37398             if(!d.isVisible()){
37399                 // force it to the end of the z-index stack so it gets a cursor in FF
37400                 document.body.appendChild(dlg.el.dom);
37401                 d.setAnimateTarget(opt.animEl);
37402                 d.show(opt.animEl);
37403             }
37404             return this;
37405         },
37406
37407         /**
37408          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default
37409          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
37410          * to clear any existing icon.  The following built-in icon classes are supported, but you can also pass
37411          * in a custom class name:
37412          * <pre>
37413 Ext.MessageBox.INFO
37414 Ext.MessageBox.WARNING
37415 Ext.MessageBox.QUESTION
37416 Ext.MessageBox.ERROR
37417          *</pre>
37418          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
37419          * @return {Ext.MessageBox} this
37420          */
37421         setIcon : function(icon){
37422             if(icon && icon != ''){
37423                 iconEl.removeClass('x-hidden');
37424                 iconEl.replaceClass(iconCls, icon);
37425                 bodyEl.addClass('x-dlg-icon');
37426                 iconCls = icon;
37427             }else{
37428                 iconEl.replaceClass(iconCls, 'x-hidden');
37429                 bodyEl.removeClass('x-dlg-icon');
37430                 iconCls = '';
37431             }
37432             return this;
37433         },
37434
37435         /**
37436          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
37437          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}
37438          * and closing the message box when the process is complete.
37439          * @param {String} title The title bar text
37440          * @param {String} msg The message box body text
37441          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')
37442          * @return {Ext.MessageBox} this
37443          */
37444         progress : function(title, msg, progressText){
37445             this.show({
37446                 title : title,
37447                 msg : msg,
37448                 buttons: false,
37449                 progress:true,
37450                 closable:false,
37451                 minWidth: this.minProgressWidth,
37452                 progressText: progressText
37453             });
37454             return this;
37455         },
37456
37457         /**
37458          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
37459          * interaction while waiting for a long-running process to complete that does not have defined intervals.
37460          * You are responsible for closing the message box when the process is complete.
37461          * @param {String} msg The message box body text
37462          * @param {String} title (optional) The title bar text
37463          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object
37464          * @return {Ext.MessageBox} this
37465          */
37466         wait : function(msg, title, config){
37467             this.show({
37468                 title : title,
37469                 msg : msg,
37470                 buttons: false,
37471                 closable:false,
37472                 wait:true,
37473                 modal:true,
37474                 minWidth: this.minProgressWidth,
37475                 waitConfig: config
37476             });
37477             return this;
37478         },
37479
37480         /**
37481          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
37482          * If a callback function is passed it will be called after the user clicks the button, and the
37483          * id of the button that was clicked will be passed as the only parameter to the callback
37484          * (could also be the top-right close button).
37485          * @param {String} title The title bar text
37486          * @param {String} msg The message box body text
37487          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37488          * @param {Object} scope (optional) The scope of the callback function
37489          * @return {Ext.MessageBox} this
37490          */
37491         alert : function(title, msg, fn, scope){
37492             this.show({
37493                 title : title,
37494                 msg : msg,
37495                 buttons: this.OK,
37496                 fn: fn,
37497                 scope : scope
37498             });
37499             return this;
37500         },
37501
37502         /**
37503          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
37504          * If a callback function is passed it will be called after the user clicks either button,
37505          * and the id of the button that was clicked will be passed as the only parameter to the callback
37506          * (could also be the top-right close button).
37507          * @param {String} title The title bar text
37508          * @param {String} msg The message box body text
37509          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37510          * @param {Object} scope (optional) The scope of the callback function
37511          * @return {Ext.MessageBox} this
37512          */
37513         confirm : function(title, msg, fn, scope){
37514             this.show({
37515                 title : title,
37516                 msg : msg,
37517                 buttons: this.YESNO,
37518                 fn: fn,
37519                 scope : scope,
37520                 icon: this.QUESTION
37521             });
37522             return this;
37523         },
37524
37525         /**
37526          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
37527          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user
37528          * clicks either button, and the id of the button that was clicked (could also be the top-right
37529          * close button) and the text that was entered will be passed as the two parameters to the callback.
37530          * @param {String} title The title bar text
37531          * @param {String} msg The message box body text
37532          * @param {Function} fn (optional) The callback function invoked after the message box is closed
37533          * @param {Object} scope (optional) The scope of the callback function
37534          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
37535          * property, or the height in pixels to create the textbox (defaults to false / single-line)
37536          * @param {String} value (optional) Default value of the text input element (defaults to '')
37537          * @return {Ext.MessageBox} this
37538          */
37539         prompt : function(title, msg, fn, scope, multiline, value){
37540             this.show({
37541                 title : title,
37542                 msg : msg,
37543                 buttons: this.OKCANCEL,
37544                 fn: fn,
37545                 minWidth:250,
37546                 scope : scope,
37547                 prompt:true,
37548                 multiline: multiline,
37549                 value: value
37550             });
37551             return this;
37552         },
37553
37554         /**
37555          * Button config that displays a single OK button
37556          * @type Object
37557          */
37558         OK : {ok:true},
37559         /**
37560          * Button config that displays a single Cancel button
37561          * @type Object
37562          */
37563         CANCEL : {cancel:true},
37564         /**
37565          * Button config that displays OK and Cancel buttons
37566          * @type Object
37567          */
37568         OKCANCEL : {ok:true, cancel:true},
37569         /**
37570          * Button config that displays Yes and No buttons
37571          * @type Object
37572          */
37573         YESNO : {yes:true, no:true},
37574         /**
37575          * Button config that displays Yes, No and Cancel buttons
37576          * @type Object
37577          */
37578         YESNOCANCEL : {yes:true, no:true, cancel:true},
37579         /**
37580          * The CSS class that provides the INFO icon image
37581          * @type String
37582          */
37583         INFO : 'ext-mb-info',
37584         /**
37585          * The CSS class that provides the WARNING icon image
37586          * @type String
37587          */
37588         WARNING : 'ext-mb-warning',
37589         /**
37590          * The CSS class that provides the QUESTION icon image
37591          * @type String
37592          */
37593         QUESTION : 'ext-mb-question',
37594         /**
37595          * The CSS class that provides the ERROR icon image
37596          * @type String
37597          */
37598         ERROR : 'ext-mb-error',
37599
37600         /**
37601          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
37602          * @type Number
37603          */
37604         defaultTextHeight : 75,
37605         /**
37606          * The maximum width in pixels of the message box (defaults to 600)
37607          * @type Number
37608          */
37609         maxWidth : 600,
37610         /**
37611          * The minimum width in pixels of the message box (defaults to 110)
37612          * @type Number
37613          */
37614         minWidth : 110,
37615         /**
37616          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
37617          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
37618          * @type Number
37619          */
37620         minProgressWidth : 250,
37621         /**
37622          * An object containing the default button text strings that can be overriden for localized language support.
37623          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific
37624          * resource file for handling language support across the framework.
37625          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french
37626          * @type Object
37627          */
37628         buttonText : {
37629             ok : "OK",
37630             cancel : "Cancel",
37631             yes : "Yes",
37632             no : "No"
37633         }
37634     };
37635 }();
37636
37637 /**
37638  * Shorthand for {@link Ext.MessageBox}
37639  */
37640 Ext.Msg = Ext.MessageBox;/**\r
37641  * @class Ext.dd.PanelProxy\r
37642  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally\r
37643  * for the Panel's drag drop implementation, and should never need to be created directly.\r
37644  * @constructor\r
37645  * @param panel The {@link Ext.Panel} to proxy for\r
37646  * @param config Configuration options\r
37647  */\r
37648 Ext.dd.PanelProxy = function(panel, config){\r
37649     this.panel = panel;\r
37650     this.id = this.panel.id +'-ddproxy';\r
37651     Ext.apply(this, config);\r
37652 };\r
37653 \r
37654 Ext.dd.PanelProxy.prototype = {\r
37655     /**\r
37656      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,\r
37657      * false to drag with no proxy (defaults to true).\r
37658      */\r
37659     insertProxy : true,\r
37660 \r
37661     // private overrides\r
37662     setStatus : Ext.emptyFn,\r
37663     reset : Ext.emptyFn,\r
37664     update : Ext.emptyFn,\r
37665     stop : Ext.emptyFn,\r
37666     sync: Ext.emptyFn,\r
37667 \r
37668     /**\r
37669      * Gets the proxy's element\r
37670      * @return {Element} The proxy's element\r
37671      */\r
37672     getEl : function(){\r
37673         return this.ghost;\r
37674     },\r
37675 \r
37676     /**\r
37677      * Gets the proxy's ghost element\r
37678      * @return {Element} The proxy's ghost element\r
37679      */\r
37680     getGhost : function(){\r
37681         return this.ghost;\r
37682     },\r
37683 \r
37684     /**\r
37685      * Gets the proxy's element\r
37686      * @return {Element} The proxy's element\r
37687      */\r
37688     getProxy : function(){\r
37689         return this.proxy;\r
37690     },\r
37691 \r
37692     /**\r
37693      * Hides the proxy\r
37694      */\r
37695     hide : function(){\r
37696         if(this.ghost){\r
37697             if(this.proxy){\r
37698                 this.proxy.remove();\r
37699                 delete this.proxy;\r
37700             }\r
37701             this.panel.el.dom.style.display = '';\r
37702             this.ghost.remove();\r
37703             delete this.ghost;\r
37704         }\r
37705     },\r
37706 \r
37707     /**\r
37708      * Shows the proxy\r
37709      */\r
37710     show : function(){\r
37711         if(!this.ghost){\r
37712             this.ghost = this.panel.createGhost(undefined, undefined, Ext.getBody());\r
37713             this.ghost.setXY(this.panel.el.getXY())\r
37714             if(this.insertProxy){\r
37715                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});\r
37716                 this.proxy.setSize(this.panel.getSize());\r
37717             }\r
37718             this.panel.el.dom.style.display = 'none';\r
37719         }\r
37720     },\r
37721 \r
37722     // private\r
37723     repair : function(xy, callback, scope){\r
37724         this.hide();\r
37725         if(typeof callback == "function"){\r
37726             callback.call(scope || this);\r
37727         }\r
37728     },\r
37729 \r
37730     /**\r
37731      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel\r
37732      * to keep the proxy sync'd to the Panel's location.\r
37733      * @param {HTMLElement} parentNode The proxy's parent DOM node\r
37734      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults\r
37735      * to the parent's last child if not specified)\r
37736      */\r
37737     moveProxy : function(parentNode, before){\r
37738         if(this.proxy){\r
37739             parentNode.insertBefore(this.proxy.dom, before);\r
37740         }\r
37741     }\r
37742 };\r
37743 \r
37744 // private - DD implementation for Panels\r
37745 Ext.Panel.DD = function(panel, cfg){\r
37746     this.panel = panel;\r
37747     this.dragData = {panel: panel};\r
37748     this.proxy = new Ext.dd.PanelProxy(panel, cfg);\r
37749     Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);\r
37750     var h = panel.header;\r
37751     if(h){\r
37752         this.setHandleElId(h.id);\r
37753     }\r
37754     (h ? h : this.panel.body).setStyle('cursor', 'move');\r
37755     this.scroll = false;\r
37756 };\r
37757 \r
37758 Ext.extend(Ext.Panel.DD, Ext.dd.DragSource, {\r
37759     showFrame: Ext.emptyFn,\r
37760     startDrag: Ext.emptyFn,\r
37761     b4StartDrag: function(x, y) {\r
37762         this.proxy.show();\r
37763     },\r
37764     b4MouseDown: function(e) {\r
37765         var x = e.getPageX();\r
37766         var y = e.getPageY();\r
37767         this.autoOffset(x, y);\r
37768     },\r
37769     onInitDrag : function(x, y){\r
37770         this.onStartDrag(x, y);\r
37771         return true;\r
37772     },\r
37773     createFrame : Ext.emptyFn,\r
37774     getDragEl : function(e){\r
37775         return this.proxy.ghost.dom;\r
37776     },\r
37777     endDrag : function(e){\r
37778         this.proxy.hide();\r
37779         this.panel.saveState();\r
37780     },\r
37781 \r
37782     autoOffset : function(x, y) {\r
37783         x -= this.startPageX;\r
37784         y -= this.startPageY;\r
37785         this.setDelta(x, y);\r
37786     }\r
37787 });/**
37788  * @class Ext.state.Provider
37789  * Abstract base class for state provider implementations. This class provides methods
37790  * for encoding and decoding <b>typed</b> variables including dates and defines the
37791  * Provider interface.
37792  */
37793 Ext.state.Provider = function(){
37794     /**
37795      * @event statechange
37796      * Fires when a state change occurs.
37797      * @param {Provider} this This state provider
37798      * @param {String} key The state key which was changed
37799      * @param {String} value The encoded value for the state
37800      */
37801     this.addEvents("statechange");
37802     this.state = {};
37803     Ext.state.Provider.superclass.constructor.call(this);
37804 };
37805 Ext.extend(Ext.state.Provider, Ext.util.Observable, {
37806     /**
37807      * Returns the current value for a key
37808      * @param {String} name The key name
37809      * @param {Mixed} defaultValue A default value to return if the key's value is not found
37810      * @return {Mixed} The state data
37811      */
37812     get : function(name, defaultValue){
37813         return typeof this.state[name] == "undefined" ?
37814             defaultValue : this.state[name];
37815     },
37816
37817     /**
37818      * Clears a value from the state
37819      * @param {String} name The key name
37820      */
37821     clear : function(name){
37822         delete this.state[name];
37823         this.fireEvent("statechange", this, name, null);
37824     },
37825
37826     /**
37827      * Sets the value for a key
37828      * @param {String} name The key name
37829      * @param {Mixed} value The value to set
37830      */
37831     set : function(name, value){
37832         this.state[name] = value;
37833         this.fireEvent("statechange", this, name, value);
37834     },
37835
37836     /**
37837      * Decodes a string previously encoded with {@link #encodeValue}.
37838      * @param {String} value The value to decode
37839      * @return {Mixed} The decoded value
37840      */
37841     decodeValue : function(cookie){
37842         var re = /^(a|n|d|b|s|o)\:(.*)$/;
37843         var matches = re.exec(unescape(cookie));
37844         if(!matches || !matches[1]) return; // non state cookie
37845         var type = matches[1];
37846         var v = matches[2];
37847         switch(type){
37848             case "n":
37849                 return parseFloat(v);
37850             case "d":
37851                 return new Date(Date.parse(v));
37852             case "b":
37853                 return (v == "1");
37854             case "a":
37855                 var all = [];
37856                 var values = v.split("^");
37857                 for(var i = 0, len = values.length; i < len; i++){
37858                     all.push(this.decodeValue(values[i]));
37859                 }
37860                 return all;
37861            case "o":
37862                 var all = {};
37863                 var values = v.split("^");
37864                 for(var i = 0, len = values.length; i < len; i++){
37865                     var kv = values[i].split("=");
37866                     all[kv[0]] = this.decodeValue(kv[1]);
37867                 }
37868                 return all;
37869            default:
37870                 return v;
37871         }
37872     },
37873
37874     /**
37875      * Encodes a value including type information.  Decode with {@link #decodeValue}.
37876      * @param {Mixed} value The value to encode
37877      * @return {String} The encoded value
37878      */
37879     encodeValue : function(v){
37880         var enc;
37881         if(typeof v == "number"){
37882             enc = "n:" + v;
37883         }else if(typeof v == "boolean"){
37884             enc = "b:" + (v ? "1" : "0");
37885         }else if(Ext.isDate(v)){
37886             enc = "d:" + v.toGMTString();
37887         }else if(Ext.isArray(v)){
37888             var flat = "";
37889             for(var i = 0, len = v.length; i < len; i++){
37890                 flat += this.encodeValue(v[i]);
37891                 if(i != len-1) flat += "^";
37892             }
37893             enc = "a:" + flat;
37894         }else if(typeof v == "object"){
37895             var flat = "";
37896             for(var key in v){
37897                 if(typeof v[key] != "function" && v[key] !== undefined){
37898                     flat += key + "=" + this.encodeValue(v[key]) + "^";
37899                 }
37900             }
37901             enc = "o:" + flat.substring(0, flat.length-1);
37902         }else{
37903             enc = "s:" + v;
37904         }
37905         return escape(enc);
37906     }
37907 });
37908 /**\r
37909  * @class Ext.state.Manager\r
37910  * This is the global state manager. By default all components that are "state aware" check this class\r
37911  * for state information if you don't pass them a custom state provider. In order for this class\r
37912  * to be useful, it must be initialized with a provider when your application initializes. Example usage:\r
37913  <pre><code>\r
37914 // in your initialization function\r
37915 init : function(){\r
37916    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());\r
37917    var win = new Window(...);\r
37918    win.restoreState();\r
37919 }\r
37920  </code></pre>\r
37921  * @singleton\r
37922  */\r
37923 Ext.state.Manager = function(){\r
37924     var provider = new Ext.state.Provider();\r
37925 \r
37926     return {\r
37927         /**\r
37928          * Configures the default state provider for your application\r
37929          * @param {Provider} stateProvider The state provider to set\r
37930          */\r
37931         setProvider : function(stateProvider){\r
37932             provider = stateProvider;\r
37933         },\r
37934 \r
37935         /**\r
37936          * Returns the current value for a key\r
37937          * @param {String} name The key name\r
37938          * @param {Mixed} defaultValue The default value to return if the key lookup does not match\r
37939          * @return {Mixed} The state data\r
37940          */\r
37941         get : function(key, defaultValue){\r
37942             return provider.get(key, defaultValue);\r
37943         },\r
37944 \r
37945         /**\r
37946          * Sets the value for a key\r
37947          * @param {String} name The key name\r
37948          * @param {Mixed} value The state data\r
37949          */\r
37950          set : function(key, value){\r
37951             provider.set(key, value);\r
37952         },\r
37953 \r
37954         /**\r
37955          * Clears a value from the state\r
37956          * @param {String} name The key name\r
37957          */\r
37958         clear : function(key){\r
37959             provider.clear(key);\r
37960         },\r
37961 \r
37962         /**\r
37963          * Gets the currently configured state provider\r
37964          * @return {Provider} The state provider\r
37965          */\r
37966         getProvider : function(){\r
37967             return provider;\r
37968         }\r
37969     };\r
37970 }();\r
37971 /**\r
37972  * @class Ext.state.CookieProvider\r
37973  * @extends Ext.state.Provider\r
37974  * The default Provider implementation which saves state via cookies.\r
37975  * <br />Usage:\r
37976  <pre><code>\r
37977    var cp = new Ext.state.CookieProvider({\r
37978        path: "/cgi-bin/",\r
37979        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days\r
37980        domain: "extjs.com"\r
37981    });\r
37982    Ext.state.Manager.setProvider(cp);\r
37983  </code></pre>\r
37984  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)\r
37985  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)\r
37986  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than\r
37987  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include\r
37988  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same\r
37989  * domain the page is running on including the 'www' like 'www.extjs.com')\r
37990  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)\r
37991  * @constructor\r
37992  * Create a new CookieProvider\r
37993  * @param {Object} config The configuration object\r
37994  */\r
37995 Ext.state.CookieProvider = function(config){\r
37996     Ext.state.CookieProvider.superclass.constructor.call(this);\r
37997     this.path = "/";\r
37998     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days\r
37999     this.domain = null;\r
38000     this.secure = false;\r
38001     Ext.apply(this, config);\r
38002     this.state = this.readCookies();\r
38003 };\r
38004 \r
38005 Ext.extend(Ext.state.CookieProvider, Ext.state.Provider, {\r
38006     // private\r
38007     set : function(name, value){\r
38008         if(typeof value == "undefined" || value === null){\r
38009             this.clear(name);\r
38010             return;\r
38011         }\r
38012         this.setCookie(name, value);\r
38013         Ext.state.CookieProvider.superclass.set.call(this, name, value);\r
38014     },\r
38015 \r
38016     // private\r
38017     clear : function(name){\r
38018         this.clearCookie(name);\r
38019         Ext.state.CookieProvider.superclass.clear.call(this, name);\r
38020     },\r
38021 \r
38022     // private\r
38023     readCookies : function(){\r
38024         var cookies = {};\r
38025         var c = document.cookie + ";";\r
38026         var re = /\s?(.*?)=(.*?);/g;\r
38027         var matches;\r
38028         while((matches = re.exec(c)) != null){\r
38029             var name = matches[1];\r
38030             var value = matches[2];\r
38031             if(name && name.substring(0,3) == "ys-"){\r
38032                 cookies[name.substr(3)] = this.decodeValue(value);\r
38033             }\r
38034         }\r
38035         return cookies;\r
38036     },\r
38037 \r
38038     // private\r
38039     setCookie : function(name, value){\r
38040         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +\r
38041            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +\r
38042            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
38043            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
38044            ((this.secure == true) ? "; secure" : "");\r
38045     },\r
38046 \r
38047     // private\r
38048     clearCookie : function(name){\r
38049         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +\r
38050            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
38051            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
38052            ((this.secure == true) ? "; secure" : "");\r
38053     }\r
38054 });/**
38055  * @class Ext.DataView
38056  * @extends Ext.BoxComponent
38057  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
38058  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
38059  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
38060  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
38061  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
38062  * config must be provided for the DataView to determine what nodes it will be working with.</b>
38063  *
38064  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
38065  * <pre><code>
38066 var store = new Ext.data.JsonStore({
38067     url: 'get-images.php',
38068     root: 'images',
38069     fields: [
38070         'name', 'url',
38071         {name:'size', type: 'float'},
38072         {name:'lastmod', type:'date', dateFormat:'timestamp'}
38073     ]
38074 });
38075 store.load();
38076
38077 var tpl = new Ext.XTemplate(
38078     '&lt;tpl for="."&gt;',
38079         '&lt;div class="thumb-wrap" id="{name}"&gt;',
38080         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
38081         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
38082     '&lt;/tpl&gt;',
38083     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
38084 );
38085
38086 var panel = new Ext.Panel({
38087     id:'images-view',
38088     frame:true,
38089     width:535,
38090     autoHeight:true,
38091     collapsible:true,
38092     layout:'fit',
38093     title:'Simple DataView',
38094
38095     items: new Ext.DataView({
38096         store: store,
38097         tpl: tpl,
38098         autoHeight:true,
38099         multiSelect: true,
38100         overClass:'x-view-over',
38101         itemSelector:'div.thumb-wrap',
38102         emptyText: 'No images to display'
38103     })
38104 });
38105 panel.render(document.body);
38106 </code></pre>
38107  * @constructor
38108  * Create a new DataView
38109  * @param {Object} config The config object
38110  * @xtype dataview
38111  */
38112 Ext.DataView = Ext.extend(Ext.BoxComponent, {
38113     /**
38114      * @cfg {String/Array} tpl
38115      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
38116      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
38117      */
38118     /**
38119      * @cfg {Ext.data.Store} store
38120      * The {@link Ext.data.Store} to bind this DataView to.
38121      */
38122     /**
38123      * @cfg {String} itemSelector
38124      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
38125      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
38126      * working with.
38127      */
38128     /**
38129      * @cfg {Boolean} multiSelect
38130      * True to allow selection of more than one item at a time, false to allow selection of only a single item
38131      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
38132      */
38133     /**
38134      * @cfg {Boolean} singleSelect
38135      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
38136      * Note that if {@link #multiSelect} = true, this value will be ignored.
38137      */
38138     /**
38139      * @cfg {Boolean} simpleSelect
38140      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
38141      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
38142      */
38143     /**
38144      * @cfg {String} overClass
38145      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
38146      */
38147     /**
38148      * @cfg {String} loadingText
38149      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
38150      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
38151      * contents will continue to display normally until the new data is loaded and the contents are replaced.
38152      */
38153     /**
38154      * @cfg {String} selectedClass
38155      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
38156      */
38157     selectedClass : "x-view-selected",
38158     /**
38159      * @cfg {String} emptyText
38160      * The text to display in the view when there is no data to display (defaults to '').
38161      */
38162     emptyText : "",
38163
38164     /**
38165      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
38166      */
38167     deferEmptyText: true,
38168     /**
38169      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
38170      */
38171     trackOver: false,
38172
38173     //private
38174     last: false,
38175
38176     // private
38177     initComponent : function(){
38178         Ext.DataView.superclass.initComponent.call(this);
38179         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
38180             this.tpl = new Ext.XTemplate(this.tpl);
38181         }
38182
38183         this.addEvents(
38184             /**
38185              * @event beforeclick
38186              * Fires before a click is processed. Returns false to cancel the default action.
38187              * @param {Ext.DataView} this
38188              * @param {Number} index The index of the target node
38189              * @param {HTMLElement} node The target node
38190              * @param {Ext.EventObject} e The raw event object
38191              */
38192             "beforeclick",
38193             /**
38194              * @event click
38195              * Fires when a template node is clicked.
38196              * @param {Ext.DataView} this
38197              * @param {Number} index The index of the target node
38198              * @param {HTMLElement} node The target node
38199              * @param {Ext.EventObject} e The raw event object
38200              */
38201             "click",
38202             /**
38203              * @event mouseenter
38204              * Fires when the mouse enters a template node. trackOver:true or an overCls must be set to enable this event.
38205              * @param {Ext.DataView} this
38206              * @param {Number} index The index of the target node
38207              * @param {HTMLElement} node The target node
38208              * @param {Ext.EventObject} e The raw event object
38209              */
38210             "mouseenter",
38211             /**
38212              * @event mouseleave
38213              * Fires when the mouse leaves a template node. trackOver:true or an overCls must be set to enable this event.
38214              * @param {Ext.DataView} this
38215              * @param {Number} index The index of the target node
38216              * @param {HTMLElement} node The target node
38217              * @param {Ext.EventObject} e The raw event object
38218              */
38219             "mouseleave",
38220             /**
38221              * @event containerclick
38222              * Fires when a click occurs and it is not on a template node.
38223              * @param {Ext.DataView} this
38224              * @param {Ext.EventObject} e The raw event object
38225              */
38226             "containerclick",
38227             /**
38228              * @event dblclick
38229              * Fires when a template node is double clicked.
38230              * @param {Ext.DataView} this
38231              * @param {Number} index The index of the target node
38232              * @param {HTMLElement} node The target node
38233              * @param {Ext.EventObject} e The raw event object
38234              */
38235             "dblclick",
38236             /**
38237              * @event contextmenu
38238              * Fires when a template node is right clicked.
38239              * @param {Ext.DataView} this
38240              * @param {Number} index The index of the target node
38241              * @param {HTMLElement} node The target node
38242              * @param {Ext.EventObject} e The raw event object
38243              */
38244             "contextmenu",
38245             /**
38246              * @event containercontextmenu
38247              * Fires when a right click occurs that is not on a template node.
38248              * @param {Ext.DataView} this
38249              * @param {Ext.EventObject} e The raw event object
38250              */
38251             "containercontextmenu",
38252             /**
38253              * @event selectionchange
38254              * Fires when the selected nodes change.
38255              * @param {Ext.DataView} this
38256              * @param {Array} selections Array of the selected nodes
38257              */
38258             "selectionchange",
38259
38260             /**
38261              * @event beforeselect
38262              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
38263              * @param {Ext.DataView} this
38264              * @param {HTMLElement} node The node to be selected
38265              * @param {Array} selections Array of currently selected nodes
38266              */
38267             "beforeselect"
38268         );
38269
38270         this.store = Ext.StoreMgr.lookup(this.store);
38271         this.all = new Ext.CompositeElementLite();
38272         this.selected = new Ext.CompositeElementLite();
38273     },
38274
38275     // private
38276     afterRender : function(){
38277         Ext.DataView.superclass.afterRender.call(this);
38278
38279                 this.mon(this.getTemplateTarget(), {
38280             "click": this.onClick,
38281             "dblclick": this.onDblClick,
38282             "contextmenu": this.onContextMenu,
38283             scope:this
38284         });
38285
38286         if(this.overClass || this.trackOver){
38287             this.mon(this.getTemplateTarget(), {
38288                 "mouseover": this.onMouseOver,
38289                 "mouseout": this.onMouseOut,
38290                 scope:this
38291             });
38292         }
38293
38294         if(this.store){
38295             this.bindStore(this.store, true);
38296         }
38297     },
38298
38299     /**
38300      * Refreshes the view by reloading the data from the store and re-rendering the template.
38301      */
38302     refresh : function(){
38303         this.clearSelections(false, true);
38304         var el = this.getTemplateTarget();
38305         el.update("");
38306         var records = this.store.getRange();
38307         if(records.length < 1){
38308             if(!this.deferEmptyText || this.hasSkippedEmptyText){
38309                 el.update(this.emptyText);
38310             }
38311             this.all.clear();
38312         }else{
38313             this.tpl.overwrite(el, this.collectData(records, 0));
38314             this.all.fill(Ext.query(this.itemSelector, el.dom));
38315             this.updateIndexes(0);
38316         }
38317         this.hasSkippedEmptyText = true;
38318     },
38319
38320     getTemplateTarget: function(){
38321         return this.el;
38322     },
38323
38324     /**
38325      * Function which can be overridden to provide custom formatting for each Record that is used by this
38326      * DataView's {@link #tpl template} to render each node.
38327      * @param {Array/Object} data The raw data object that was used to create the Record.
38328      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
38329      * @param {Record} record The Record being prepared for rendering.
38330      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
38331      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
38332      */
38333     prepareData : function(data){
38334         return data;
38335     },
38336
38337     /**
38338      * <p>Function which can be overridden which returns the data object passed to this
38339      * DataView's {@link #tpl template} to render the whole DataView.</p>
38340      * <p>This is usually an Array of data objects, each element of which is processed by an
38341      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
38342      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
38343      * provide non-repeating data such as headings, totals etc.</p>
38344      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
38345      * @param {Number} startIndex the index number of the Record being prepared for rendering.
38346      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
38347      * contain <i>named</i> properties.
38348      */
38349     collectData : function(records, startIndex){
38350         var r = [];
38351         for(var i = 0, len = records.length; i < len; i++){
38352             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
38353         }
38354         return r;
38355     },
38356
38357     // private
38358     bufferRender : function(records){
38359         var div = document.createElement('div');
38360         this.tpl.overwrite(div, this.collectData(records));
38361         return Ext.query(this.itemSelector, div);
38362     },
38363
38364     // private
38365     onUpdate : function(ds, record){
38366         var index = this.store.indexOf(record);
38367         var sel = this.isSelected(index);
38368         var original = this.all.elements[index];
38369         var node = this.bufferRender([record], index)[0];
38370
38371         this.all.replaceElement(index, node, true);
38372         if(sel){
38373             this.selected.replaceElement(original, node);
38374             this.all.item(index).addClass(this.selectedClass);
38375         }
38376         this.updateIndexes(index, index);
38377     },
38378
38379     // private
38380     onAdd : function(ds, records, index){
38381         if(this.all.getCount() === 0){
38382             this.refresh();
38383             return;
38384         }
38385         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
38386         if(index < this.all.getCount()){
38387             n = this.all.item(index).insertSibling(nodes, 'before', true);
38388             a.splice.apply(a, [index, 0].concat(nodes));
38389         }else{
38390             n = this.all.last().insertSibling(nodes, 'after', true);
38391             a.push.apply(a, nodes);
38392         }
38393         this.updateIndexes(index);
38394     },
38395
38396     // private
38397     onRemove : function(ds, record, index){
38398         this.deselect(index);
38399         this.all.removeElement(index, true);
38400         this.updateIndexes(index);
38401         if (this.store.getCount() === 0){
38402             this.refresh();
38403         }
38404     },
38405
38406     /**
38407      * Refreshes an individual node's data from the store.
38408      * @param {Number} index The item's data index in the store
38409      */
38410     refreshNode : function(index){
38411         this.onUpdate(this.store, this.store.getAt(index));
38412     },
38413
38414     // private
38415     updateIndexes : function(startIndex, endIndex){
38416         var ns = this.all.elements;
38417         startIndex = startIndex || 0;
38418         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
38419         for(var i = startIndex; i <= endIndex; i++){
38420             ns[i].viewIndex = i;
38421         }
38422     },
38423     
38424     /**
38425      * Returns the store associated with this DataView.
38426      * @return {Ext.data.Store} The store
38427      */
38428     getStore : function(){
38429         return this.store;
38430     },
38431
38432     /**
38433      * Changes the data store bound to this view and refreshes it.
38434      * @param {Store} store The store to bind to this view
38435      */
38436     bindStore : function(store, initial){
38437         if(!initial && this.store){
38438             this.store.un("beforeload", this.onBeforeLoad, this);
38439             this.store.un("datachanged", this.refresh, this);
38440             this.store.un("add", this.onAdd, this);
38441             this.store.un("remove", this.onRemove, this);
38442             this.store.un("update", this.onUpdate, this);
38443             this.store.un("clear", this.refresh, this);
38444             if(store !== this.store && this.store.autoDestroy){
38445                 this.store.destroy();
38446             }
38447         }
38448         if(store){
38449             store = Ext.StoreMgr.lookup(store);
38450             store.on({
38451                 scope: this,
38452                 beforeload: this.onBeforeLoad,
38453                 datachanged: this.refresh,
38454                 add: this.onAdd,
38455                 remove: this.onRemove,
38456                 update: this.onUpdate,
38457                 clear: this.refresh
38458             });
38459         }
38460         this.store = store;
38461         if(store){
38462             this.refresh();
38463         }
38464     },
38465
38466     /**
38467      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
38468      * @param {HTMLElement} node
38469      * @return {HTMLElement} The template node
38470      */
38471     findItemFromChild : function(node){
38472         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
38473     },
38474
38475     // private
38476     onClick : function(e){
38477         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
38478         if(item){
38479             var index = this.indexOf(item);
38480             if(this.onItemClick(item, index, e) !== false){
38481                 this.fireEvent("click", this, index, item, e);
38482             }
38483         }else{
38484             if(this.fireEvent("containerclick", this, e) !== false){
38485                 this.onContainerClick(e);
38486             }
38487         }
38488     },
38489
38490     onContainerClick : function(e){
38491         this.clearSelections();
38492     },
38493
38494     // private
38495     onContextMenu : function(e){
38496         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
38497         if(item){
38498             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
38499         }else{
38500             this.fireEvent("containercontextmenu", this, e);
38501         }
38502     },
38503
38504     // private
38505     onDblClick : function(e){
38506         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
38507         if(item){
38508             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
38509         }
38510     },
38511
38512     // private
38513     onMouseOver : function(e){
38514         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
38515         if(item && item !== this.lastItem){
38516             this.lastItem = item;
38517             Ext.fly(item).addClass(this.overClass);
38518             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
38519         }
38520     },
38521
38522     // private
38523     onMouseOut : function(e){
38524         if(this.lastItem){
38525             if(!e.within(this.lastItem, true, true)){
38526                 Ext.fly(this.lastItem).removeClass(this.overClass);
38527                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
38528                 delete this.lastItem;
38529             }
38530         }
38531     },
38532
38533     // private
38534     onItemClick : function(item, index, e){
38535         if(this.fireEvent("beforeclick", this, index, item, e) === false){
38536             return false;
38537         }
38538         if(this.multiSelect){
38539             this.doMultiSelection(item, index, e);
38540             e.preventDefault();
38541         }else if(this.singleSelect){
38542             this.doSingleSelection(item, index, e);
38543             e.preventDefault();
38544         }
38545         return true;
38546     },
38547
38548     // private
38549     doSingleSelection : function(item, index, e){
38550         if(e.ctrlKey && this.isSelected(index)){
38551             this.deselect(index);
38552         }else{
38553             this.select(index, false);
38554         }
38555     },
38556
38557     // private
38558     doMultiSelection : function(item, index, e){
38559         if(e.shiftKey && this.last !== false){
38560             var last = this.last;
38561             this.selectRange(last, index, e.ctrlKey);
38562             this.last = last; // reset the last
38563         }else{
38564             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
38565                 this.deselect(index);
38566             }else{
38567                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
38568             }
38569         }
38570     },
38571
38572     /**
38573      * Gets the number of selected nodes.
38574      * @return {Number} The node count
38575      */
38576     getSelectionCount : function(){
38577         return this.selected.getCount();
38578     },
38579
38580     /**
38581      * Gets the currently selected nodes.
38582      * @return {Array} An array of HTMLElements
38583      */
38584     getSelectedNodes : function(){
38585         return this.selected.elements;
38586     },
38587
38588     /**
38589      * Gets the indexes of the selected nodes.
38590      * @return {Array} An array of numeric indexes
38591      */
38592     getSelectedIndexes : function(){
38593         var indexes = [], s = this.selected.elements;
38594         for(var i = 0, len = s.length; i < len; i++){
38595             indexes.push(s[i].viewIndex);
38596         }
38597         return indexes;
38598     },
38599
38600     /**
38601      * Gets an array of the selected records
38602      * @return {Array} An array of {@link Ext.data.Record} objects
38603      */
38604     getSelectedRecords : function(){
38605         var r = [], s = this.selected.elements;
38606         for(var i = 0, len = s.length; i < len; i++){
38607             r[r.length] = this.store.getAt(s[i].viewIndex);
38608         }
38609         return r;
38610     },
38611
38612     /**
38613      * Gets an array of the records from an array of nodes
38614      * @param {Array} nodes The nodes to evaluate
38615      * @return {Array} records The {@link Ext.data.Record} objects
38616      */
38617     getRecords : function(nodes){
38618         var r = [], s = nodes;
38619         for(var i = 0, len = s.length; i < len; i++){
38620             r[r.length] = this.store.getAt(s[i].viewIndex);
38621         }
38622         return r;
38623     },
38624
38625     /**
38626      * Gets a record from a node
38627      * @param {HTMLElement} node The node to evaluate
38628      * @return {Record} record The {@link Ext.data.Record} object
38629      */
38630     getRecord : function(node){
38631         return this.store.getAt(node.viewIndex);
38632     },
38633
38634     /**
38635      * Clears all selections.
38636      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
38637      */
38638     clearSelections : function(suppressEvent, skipUpdate){
38639         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
38640             if(!skipUpdate){
38641                 this.selected.removeClass(this.selectedClass);
38642             }
38643             this.selected.clear();
38644             this.last = false;
38645             if(!suppressEvent){
38646                 this.fireEvent("selectionchange", this, this.selected.elements);
38647             }
38648         }
38649     },
38650
38651     /**
38652      * Returns true if the passed node is selected, else false.
38653      * @param {HTMLElement/Number} node The node or node index to check
38654      * @return {Boolean} True if selected, else false
38655      */
38656     isSelected : function(node){
38657         return this.selected.contains(this.getNode(node));
38658     },
38659
38660     /**
38661      * Deselects a node.
38662      * @param {HTMLElement/Number} node The node to deselect
38663      */
38664     deselect : function(node){
38665         if(this.isSelected(node)){
38666             node = this.getNode(node);
38667             this.selected.removeElement(node);
38668             if(this.last == node.viewIndex){
38669                 this.last = false;
38670             }
38671             Ext.fly(node).removeClass(this.selectedClass);
38672             this.fireEvent("selectionchange", this, this.selected.elements);
38673         }
38674     },
38675
38676     /**
38677      * Selects a set of nodes.
38678      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node,
38679      * id of a template node or an array of any of those to select
38680      * @param {Boolean} keepExisting (optional) true to keep existing selections
38681      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
38682      */
38683     select : function(nodeInfo, keepExisting, suppressEvent){
38684         if(Ext.isArray(nodeInfo)){
38685             if(!keepExisting){
38686                 this.clearSelections(true);
38687             }
38688             for(var i = 0, len = nodeInfo.length; i < len; i++){
38689                 this.select(nodeInfo[i], true, true);
38690             }
38691             if(!suppressEvent){
38692                 this.fireEvent("selectionchange", this, this.selected.elements);
38693             }
38694         } else{
38695             var node = this.getNode(nodeInfo);
38696             if(!keepExisting){
38697                 this.clearSelections(true);
38698             }
38699             if(node && !this.isSelected(node)){
38700                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
38701                     Ext.fly(node).addClass(this.selectedClass);
38702                     this.selected.add(node);
38703                     this.last = node.viewIndex;
38704                     if(!suppressEvent){
38705                         this.fireEvent("selectionchange", this, this.selected.elements);
38706                     }
38707                 }
38708             }
38709         }
38710     },
38711
38712     /**
38713      * Selects a range of nodes. All nodes between start and end are selected.
38714      * @param {Number} start The index of the first node in the range
38715      * @param {Number} end The index of the last node in the range
38716      * @param {Boolean} keepExisting (optional) True to retain existing selections
38717      */
38718     selectRange : function(start, end, keepExisting){
38719         if(!keepExisting){
38720             this.clearSelections(true);
38721         }
38722         this.select(this.getNodes(start, end), true);
38723     },
38724
38725     /**
38726      * Gets a template node.
38727      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
38728      * @return {HTMLElement} The node or null if it wasn't found
38729      */
38730     getNode : function(nodeInfo){
38731         if(Ext.isString(nodeInfo)){
38732             return document.getElementById(nodeInfo);
38733         }else if(Ext.isNumber(nodeInfo)){
38734             return this.all.elements[nodeInfo];
38735         }
38736         return nodeInfo;
38737     },
38738
38739     /**
38740      * Gets a range nodes.
38741      * @param {Number} start (optional) The index of the first node in the range
38742      * @param {Number} end (optional) The index of the last node in the range
38743      * @return {Array} An array of nodes
38744      */
38745     getNodes : function(start, end){
38746         var ns = this.all.elements;
38747         start = start || 0;
38748         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
38749         var nodes = [], i;
38750         if(start <= end){
38751             for(i = start; i <= end && ns[i]; i++){
38752                 nodes.push(ns[i]);
38753             }
38754         } else{
38755             for(i = start; i >= end && ns[i]; i--){
38756                 nodes.push(ns[i]);
38757             }
38758         }
38759         return nodes;
38760     },
38761
38762     /**
38763      * Finds the index of the passed node.
38764      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
38765      * @return {Number} The index of the node or -1
38766      */
38767     indexOf : function(node){
38768         node = this.getNode(node);
38769         if(Ext.isNumber(node.viewIndex)){
38770             return node.viewIndex;
38771         }
38772         return this.all.indexOf(node);
38773     },
38774
38775     // private
38776     onBeforeLoad : function(){
38777         if(this.loadingText){
38778             this.clearSelections(false, true);
38779             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
38780             this.all.clear();
38781         }
38782     },
38783
38784     onDestroy : function(){
38785         Ext.DataView.superclass.onDestroy.call(this);
38786         this.bindStore(null);
38787     }
38788 });
38789
38790 /**
38791  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
38792  * @param {Store} store The store to bind to this view
38793  */
38794 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
38795
38796 Ext.reg('dataview', Ext.DataView);/**\r
38797  * @class Ext.ListView\r
38798  * @extends Ext.DataView\r
38799  * <p>Ext.ListView is a fast and light-weight implentation of a\r
38800  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>\r
38801  * <div class="mdetail-params"><ul>\r
38802  * <li>resizable columns</li>\r
38803  * <li>selectable</li>\r
38804  * <li>column widths are initially proportioned by percentage based on the container\r
38805  * width and number of columns</li>\r
38806  * <li>uses templates to render the data in any required format</li>\r
38807  * <li>no horizontal scrolling</li>\r
38808  * <li>no editing</li>\r
38809  * </ul></div>\r
38810  * <p>Example usage:</p>\r
38811  * <pre><code>\r
38812 // consume JSON of this form:\r
38813 {\r
38814    "images":[\r
38815       {\r
38816          "name":"dance_fever.jpg",\r
38817          "size":2067,\r
38818          "lastmod":1236974993000,\r
38819          "url":"images\/thumbs\/dance_fever.jpg"\r
38820       },\r
38821       {\r
38822          "name":"zack_sink.jpg",\r
38823          "size":2303,\r
38824          "lastmod":1236974993000,\r
38825          "url":"images\/thumbs\/zack_sink.jpg"\r
38826       }\r
38827    ]\r
38828\r
38829 var store = new Ext.data.JsonStore({\r
38830     url: 'get-images.php',\r
38831     root: 'images',\r
38832     fields: [\r
38833         'name', 'url',\r
38834         {name:'size', type: 'float'},\r
38835         {name:'lastmod', type:'date', dateFormat:'timestamp'}\r
38836     ]\r
38837 });\r
38838 store.load();\r
38839 \r
38840 var listView = new Ext.ListView({\r
38841     store: store,\r
38842     multiSelect: true,\r
38843     emptyText: 'No images to display',\r
38844     reserveScrollOffset: true,\r
38845     columns: [{\r
38846         header: 'File',\r
38847         width: .5,\r
38848         dataIndex: 'name'\r
38849     },{\r
38850         header: 'Last Modified',\r
38851         width: .35, \r
38852         dataIndex: 'lastmod',\r
38853         tpl: '{lastmod:date("m-d h:i a")}'\r
38854     },{\r
38855         header: 'Size',\r
38856         dataIndex: 'size',\r
38857         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()\r
38858         align: 'right'\r
38859     }]\r
38860 });\r
38861 \r
38862 // put it in a Panel so it looks pretty\r
38863 var panel = new Ext.Panel({\r
38864     id:'images-view',\r
38865     width:425,\r
38866     height:250,\r
38867     collapsible:true,\r
38868     layout:'fit',\r
38869     title:'Simple ListView <i>(0 items selected)</i>',\r
38870     items: listView\r
38871 });\r
38872 panel.render(document.body);\r
38873 \r
38874 // little bit of feedback\r
38875 listView.on('selectionchange', function(view, nodes){\r
38876     var l = nodes.length;\r
38877     var s = l != 1 ? 's' : '';\r
38878     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');\r
38879 });\r
38880  * </code></pre>\r
38881  * @constructor\r
38882  * @param {Object} config\r
38883  * @xtype listview\r
38884  */\r
38885 Ext.ListView = Ext.extend(Ext.DataView, {\r
38886     /**\r
38887      * Set this property to <tt>true</tt> to disable the header click handler disabling sort\r
38888      * (defaults to <tt>false</tt>).\r
38889      * @type Boolean\r
38890      * @property disableHeaders\r
38891      */\r
38892     /**\r
38893      * @cfg {Boolean} hideHeaders\r
38894      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so\r
38895      * the {@link #internalTpl header row} will be shown).\r
38896      */\r
38897     /**\r
38898      * @cfg {String} itemSelector\r
38899      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.\r
38900      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)\r
38901      * that will be used to determine what nodes the ListView will be working with.   \r
38902      */\r
38903     itemSelector: 'dl',\r
38904     /**\r
38905      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to\r
38906      * <tt>'x-list-selected'</tt>). An example overriding the default styling:\r
38907     <pre><code>\r
38908     .x-list-selected {background-color: yellow;}\r
38909     </code></pre>\r
38910      * @type String\r
38911      */\r
38912     selectedClass:'x-list-selected',\r
38913     /**\r
38914      * @cfg {String} overClass The CSS class applied when over a row (defaults to\r
38915      * <tt>'x-list-over'</tt>). An example overriding the default styling:\r
38916     <pre><code>\r
38917     .x-list-over {background-color: orange;}\r
38918     </code></pre>\r
38919      * @type String\r
38920      */\r
38921     overClass:'x-list-over',\r
38922     /**\r
38923      * @cfg {Boolean} reserveScrollOffset\r
38924      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>\r
38925      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured\r
38926      * <b><tt>{@link #scrollOffset}</tt></b> immediately.\r
38927      */\r
38928     /**\r
38929      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to\r
38930      * <tt>19</tt> pixels)\r
38931      */\r
38932     scrollOffset : 19,\r
38933     /**\r
38934      * @cfg {Boolean/Object} columnResize\r
38935      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.ColumnResizer}\r
38936      * to enable the columns to be resizable (defaults to <tt>true</tt>).\r
38937      */\r
38938     columnResize: true,\r
38939     /**\r
38940      * @cfg {Array} columns An array of column configuration objects, for example:\r
38941      * <pre><code>\r
38942 {\r
38943     align: 'right',\r
38944     dataIndex: 'size',\r
38945     header: 'Size',\r
38946     tpl: '{size:fileSize}',\r
38947     width: .35\r
38948 }\r
38949      * </code></pre> \r
38950      * Acceptable properties for each column configuration object are:\r
38951      * <div class="mdetail-params"><ul>\r
38952      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property\r
38953      * of the column. Defaults to <tt>'left'</tt>.</div></li>\r
38954      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
38955      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>\r
38956      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
38957      * {@link Ext.grid.Column#header header} for details.</div></li>\r
38958      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the\r
38959      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}\r
38960      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>\r
38961      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width\r
38962      * this column should be allocated.  Columns that have no width specified will be\r
38963      * allocated with an equal percentage to fill 100% of the container width.  To easily take\r
38964      * advantage of the full container width, leave the width of at least one column undefined.\r
38965      * Note that if you do not want to take up the full width of the container, the width of\r
38966      * every column needs to be explicitly defined.</div></li>\r
38967      * </ul></div>\r
38968      */\r
38969     /**\r
38970      * @cfg {Boolean/Object} columnSort\r
38971      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.Sorter}\r
38972      * to enable the columns to be sortable (defaults to <tt>true</tt>).\r
38973      */\r
38974     columnSort: true,\r
38975     /**\r
38976      * @cfg {String/Array} internalTpl\r
38977      * The template to be used for the header row.  See {@link #tpl} for more details.\r
38978      */\r
38979 \r
38980     initComponent : function(){\r
38981         if(this.columnResize){\r
38982             this.colResizer = new Ext.ListView.ColumnResizer(this.colResizer);\r
38983             this.colResizer.init(this);\r
38984         }\r
38985         if(this.columnSort){\r
38986             this.colSorter = new Ext.ListView.Sorter(this.columnSort);\r
38987             this.colSorter.init(this);\r
38988         }\r
38989         if(!this.internalTpl){\r
38990             this.internalTpl = new Ext.XTemplate(\r
38991                 '<div class="x-list-header"><div class="x-list-header-inner">',\r
38992                     '<tpl for="columns">',\r
38993                     '<div style="width:{width}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',\r
38994                         '{header}',\r
38995                     '</em></div>',\r
38996                     '</tpl>',\r
38997                     '<div class="x-clear"></div>',\r
38998                 '</div></div>',\r
38999                 '<div class="x-list-body"><div class="x-list-body-inner">',\r
39000                 '</div></div>'\r
39001             );\r
39002         }\r
39003         if(!this.tpl){\r
39004             this.tpl = new Ext.XTemplate(\r
39005                 '<tpl for="rows">',\r
39006                     '<dl>',\r
39007                         '<tpl for="parent.columns">',\r
39008                         '<dt style="width:{width}%;text-align:{align};"><em unselectable="on">',\r
39009                             '{[values.tpl.apply(parent)]}',\r
39010                         '</em></dt>',\r
39011                         '</tpl>',\r
39012                         '<div class="x-clear"></div>',\r
39013                     '</dl>',\r
39014                 '</tpl>'\r
39015             );\r
39016         };\r
39017         var cs = this.columns, allocatedWidth = 0, colsWithWidth = 0, len = cs.length;\r
39018         for(var i = 0; i < len; i++){\r
39019             var c = cs[i];\r
39020             if(!c.tpl){\r
39021                 c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');\r
39022             }else if(Ext.isString(c.tpl)){\r
39023                 c.tpl = new Ext.XTemplate(c.tpl);\r
39024             }\r
39025             c.align = c.align || 'left';\r
39026             if(Ext.isNumber(c.width)){\r
39027                 c.width *= 100;\r
39028                 allocatedWidth += c.width;\r
39029                 colsWithWidth++;\r
39030             }\r
39031         }\r
39032         // auto calculate missing column widths\r
39033         if(colsWithWidth < len){\r
39034             var remaining = len - colsWithWidth;\r
39035             if(allocatedWidth < 100){\r
39036                 var perCol = ((100-allocatedWidth) / remaining);\r
39037                 for(var j = 0; j < len; j++){\r
39038                     var c = cs[j];\r
39039                     if(!Ext.isNumber(c.width)){\r
39040                         c.width = perCol;\r
39041                     }\r
39042                 }\r
39043             }\r
39044         }\r
39045         Ext.ListView.superclass.initComponent.call(this);\r
39046     },\r
39047 \r
39048     onRender : function(){\r
39049         Ext.ListView.superclass.onRender.apply(this, arguments);\r
39050 \r
39051         this.internalTpl.overwrite(this.el, {columns: this.columns});\r
39052         \r
39053         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);\r
39054         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);\r
39055 \r
39056         if(this.hideHeaders){\r
39057             this.el.dom.firstChild.style.display = 'none';\r
39058         }\r
39059     },\r
39060 \r
39061     getTemplateTarget : function(){\r
39062         return this.innerBody;\r
39063     },\r
39064 \r
39065     /**\r
39066      * <p>Function which can be overridden which returns the data object passed to this\r
39067      * view's {@link #tpl template} to render the whole ListView. The returned object \r
39068      * shall contain the following properties:</p>\r
39069      * <div class="mdetail-params"><ul>\r
39070      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>\r
39071      * <li><b>rows</b> : String<div class="sub-desc">See\r
39072      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>\r
39073      * </ul></div>\r
39074      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.\r
39075      * @param {Number} startIndex the index number of the Record being prepared for rendering.\r
39076      * @return {Object} A data object containing properties to be processed by a repeating\r
39077      * XTemplate as described above.\r
39078      */\r
39079     collectData : function(){\r
39080         var rs = Ext.ListView.superclass.collectData.apply(this, arguments);\r
39081         return {\r
39082             columns: this.columns,\r
39083             rows: rs\r
39084         }\r
39085     },\r
39086 \r
39087     verifyInternalSize : function(){\r
39088         if(this.lastSize){\r
39089             this.onResize(this.lastSize.width, this.lastSize.height);\r
39090         }\r
39091     },\r
39092 \r
39093     // private\r
39094     onResize : function(w, h){\r
39095         var bd = this.innerBody.dom;\r
39096         var hd = this.innerHd.dom\r
39097         if(!bd){\r
39098             return;\r
39099         }\r
39100         var bdp = bd.parentNode;\r
39101         if(Ext.isNumber(w)){\r
39102             var sw = w - this.scrollOffset;\r
39103             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){\r
39104                 bd.style.width = sw + 'px';\r
39105                 hd.style.width = sw + 'px';\r
39106             }else{\r
39107                 bd.style.width = w + 'px';\r
39108                 hd.style.width = w + 'px';\r
39109                 setTimeout(function(){\r
39110                     if((bdp.offsetWidth - bdp.clientWidth) > 10){\r
39111                         bd.style.width = sw + 'px';\r
39112                         hd.style.width = sw + 'px';\r
39113                     }\r
39114                 }, 10);\r
39115             }\r
39116         }\r
39117         if(Ext.isNumber(h == 'number')){\r
39118             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';\r
39119         }\r
39120     },\r
39121 \r
39122     updateIndexes : function(){\r
39123         Ext.ListView.superclass.updateIndexes.apply(this, arguments);\r
39124         this.verifyInternalSize();\r
39125     },\r
39126 \r
39127     findHeaderIndex : function(hd){\r
39128         hd = hd.dom || hd;\r
39129         var pn = hd.parentNode, cs = pn.parentNode.childNodes;\r
39130         for(var i = 0, c; c = cs[i]; i++){\r
39131             if(c == pn){\r
39132                 return i;\r
39133             }\r
39134         }\r
39135         return -1;\r
39136     },\r
39137 \r
39138     setHdWidths : function(){\r
39139         var els = this.innerHd.dom.getElementsByTagName('div');\r
39140         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){\r
39141             els[i].style.width = cs[i].width + '%';\r
39142         }\r
39143     }\r
39144 });\r
39145 \r
39146 Ext.reg('listview', Ext.ListView);/**\r
39147  * @class Ext.ListView.ColumnResizer\r
39148  * @extends Ext.util.Observable\r
39149  * <p>Supporting Class for Ext.ListView.</p>\r
39150  * @constructor\r
39151  * @param {Object} config\r
39152  */\r
39153 Ext.ListView.ColumnResizer = Ext.extend(Ext.util.Observable, {\r
39154     /**\r
39155      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)\r
39156      */\r
39157     minPct: .05,\r
39158 \r
39159     constructor: function(config){\r
39160         Ext.apply(this, config);\r
39161         Ext.ListView.ColumnResizer.superclass.constructor.call(this);\r
39162     },\r
39163     init : function(listView){\r
39164         this.view = listView;\r
39165         listView.on('render', this.initEvents, this);\r
39166     },\r
39167 \r
39168     initEvents : function(view){\r
39169         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);\r
39170         this.tracker = new Ext.dd.DragTracker({\r
39171             onBeforeStart: this.onBeforeStart.createDelegate(this),\r
39172             onStart: this.onStart.createDelegate(this),\r
39173             onDrag: this.onDrag.createDelegate(this),\r
39174             onEnd: this.onEnd.createDelegate(this),\r
39175             tolerance: 3,\r
39176             autoStart: 300\r
39177         });\r
39178         this.tracker.initEl(view.innerHd);\r
39179         view.on('beforedestroy', this.tracker.destroy, this.tracker);\r
39180     },\r
39181 \r
39182     handleHdMove : function(e, t){\r
39183         var hw = 5;\r
39184         var x = e.getPageX();\r
39185         var hd = e.getTarget('em', 3, true);\r
39186         if(hd){\r
39187             var r = hd.getRegion();\r
39188             var ss = hd.dom.style;\r
39189             var pn = hd.dom.parentNode;\r
39190 \r
39191             if(x - r.left <= hw && pn != pn.parentNode.firstChild){\r
39192                 this.activeHd = Ext.get(pn.previousSibling.firstChild);\r
39193                                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';\r
39194             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){\r
39195                 this.activeHd = hd;\r
39196                                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';\r
39197             } else{\r
39198                 delete this.activeHd;\r
39199                 ss.cursor = '';\r
39200             }\r
39201         }\r
39202     },\r
39203 \r
39204     onBeforeStart : function(e){\r
39205         this.dragHd = this.activeHd;\r
39206         return !!this.dragHd;\r
39207     },\r
39208 \r
39209     onStart: function(e){\r
39210         this.view.disableHeaders = true;\r
39211         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});\r
39212         this.proxy.setHeight(this.view.el.getHeight());\r
39213 \r
39214         var x = this.tracker.getXY()[0];\r
39215         var w = this.view.innerHd.getWidth();\r
39216 \r
39217         this.hdX = this.dragHd.getX();\r
39218         this.hdIndex = this.view.findHeaderIndex(this.dragHd);\r
39219 \r
39220         this.proxy.setX(this.hdX);\r
39221         this.proxy.setWidth(x-this.hdX);\r
39222 \r
39223         this.minWidth = w*this.minPct;\r
39224         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));\r
39225     },\r
39226 \r
39227     onDrag: function(e){\r
39228         var cursorX = this.tracker.getXY()[0];\r
39229         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));\r
39230     },\r
39231 \r
39232     onEnd: function(e){\r
39233         var nw = this.proxy.getWidth();\r
39234         this.proxy.remove();\r
39235 \r
39236         var index = this.hdIndex;\r
39237         var vw = this.view, cs = vw.columns, len = cs.length;\r
39238         var w = this.view.innerHd.getWidth(), minPct = this.minPct * 100;\r
39239 \r
39240         var pct = Math.ceil((nw*100) / w);\r
39241         var diff = cs[index].width - pct;\r
39242         var each = Math.floor(diff / (len-1-index));\r
39243         var mod = diff - (each * (len-1-index));\r
39244 \r
39245         for(var i = index+1; i < len; i++){\r
39246             var cw = cs[i].width + each;\r
39247             var ncw = Math.max(minPct, cw);\r
39248             if(cw != ncw){\r
39249                 mod += cw - ncw;\r
39250             }\r
39251             cs[i].width = ncw;\r
39252         }\r
39253         cs[index].width = pct;\r
39254         cs[index+1].width += mod;\r
39255         delete this.dragHd;\r
39256         this.view.setHdWidths();\r
39257         this.view.refresh();\r
39258         setTimeout(function(){\r
39259             vw.disableHeaders = false;\r
39260         }, 100);\r
39261     }\r
39262 });/**\r
39263  * @class Ext.ListView.Sorter\r
39264  * @extends Ext.util.Observable\r
39265  * <p>Supporting Class for Ext.ListView.</p>\r
39266  * @constructor\r
39267  * @param {Object} config\r
39268  */\r
39269 Ext.ListView.Sorter = Ext.extend(Ext.util.Observable, {\r
39270     /**\r
39271      * @cfg {Array} sortClasses\r
39272      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)\r
39273      */\r
39274     sortClasses : ["sort-asc", "sort-desc"],\r
39275 \r
39276     constructor: function(config){\r
39277         Ext.apply(this, config);\r
39278         Ext.ListView.Sorter.superclass.constructor.call(this);\r
39279     },\r
39280 \r
39281     init : function(listView){\r
39282         this.view = listView;\r
39283         listView.on('render', this.initEvents, this);\r
39284     },\r
39285 \r
39286     initEvents : function(view){\r
39287         view.mon(view.innerHd, 'click', this.onHdClick, this);\r
39288         view.innerHd.setStyle('cursor', 'pointer');\r
39289         view.mon(view.store, 'datachanged', this.updateSortState, this);\r
39290         this.updateSortState.defer(10, this, [view.store]);\r
39291     },\r
39292 \r
39293     updateSortState : function(store){\r
39294         var state = store.getSortState();\r
39295         if(!state){\r
39296             return;\r
39297         }\r
39298         this.sortState = state;\r
39299         var cs = this.view.columns, sortColumn = -1;\r
39300         for(var i = 0, len = cs.length; i < len; i++){\r
39301             if(cs[i].dataIndex == state.field){\r
39302                 sortColumn = i;\r
39303                 break;\r
39304             }\r
39305         }\r
39306         if(sortColumn != -1){\r
39307             var sortDir = state.direction;\r
39308             this.updateSortIcon(sortColumn, sortDir);\r
39309         }\r
39310     },\r
39311 \r
39312     updateSortIcon : function(col, dir){\r
39313         var sc = this.sortClasses;\r
39314         var hds = this.view.innerHd.select('em').removeClass(sc);\r
39315         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);\r
39316     },\r
39317 \r
39318     onHdClick : function(e){\r
39319         var hd = e.getTarget('em', 3);\r
39320         if(hd && !this.view.disableHeaders){\r
39321             var index = this.view.findHeaderIndex(hd);\r
39322             this.view.store.sort(this.view.columns[index].dataIndex);\r
39323         }\r
39324     }\r
39325 });/**
39326  * @class Ext.TabPanel
39327  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
39328  * for layout purposes, but also have special support for containing child Components
39329  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
39330  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
39331  *
39332  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
39333  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
39334  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
39335  *
39336  * <p><b><u>TabPanel header/footer elements</u></b></p>
39337  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
39338  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
39339  * This means that a TabPanel will not display any configured title, and will not display any
39340  * configured header {@link Ext.Panel#tools tools}.</p>
39341  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
39342  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
39343  *
39344  * <p><b><u>Tab Events</u></b></p>
39345  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
39346  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
39347  * can fire additional events that only exist for tabs and are not available from other Components.
39348  * These events are:</p>
39349  * <div><ul class="mdetail-params">
39350  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
39351  * the active tab.</li>
39352  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
39353  * was the active tab becomes deactivated.</li>
39354  * </ul></div>
39355  * <p><b><u>Creating TabPanels from Code</u></b></p>
39356  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
39357  * <pre><code>
39358 var tabs = new Ext.TabPanel({
39359     renderTo: Ext.getBody(),
39360     activeTab: 0,
39361     items: [{
39362         title: 'Tab 1',
39363         html: 'A simple tab'
39364     },{
39365         title: 'Tab 2',
39366         html: 'Another one'
39367     }]
39368 });
39369 </code></pre>
39370  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
39371  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
39372  * <div><ul class="mdetail-params">
39373  *
39374  * <li>Pre-Structured Markup</li>
39375  * <div class="sub-desc">
39376  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
39377  * from existing markup (See the {@link #autoTabs} example).</p>
39378  * </div>
39379  *
39380  * <li>Un-Structured Markup</li>
39381  * <div class="sub-desc">
39382  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
39383  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
39384  * elements within the page by id regardless of page structure. For example:</p>
39385  * <pre><code>
39386 var tabs = new Ext.TabPanel({
39387     renderTo: 'my-tabs',
39388     activeTab: 0,
39389     items:[
39390         {contentEl:'tab1', title:'Tab 1'},
39391         {contentEl:'tab2', title:'Tab 2'}
39392     ]
39393 });
39394
39395 // Note that the tabs do not have to be nested within the container (although they can be)
39396 &lt;div id="my-tabs">&lt;/div>
39397 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
39398 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
39399 </code></pre>
39400  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
39401  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
39402  * to render all content tabs on page load.
39403  * </div>
39404  *
39405  * </ul></div>
39406  *
39407  * @extends Ext.Panel
39408  * @constructor
39409  * @param {Object} config The configuration options
39410  * @xtype tabpanel
39411  */
39412 Ext.TabPanel = Ext.extend(Ext.Panel,  {
39413     /**
39414      * @cfg {Boolean} layoutOnTabChange
39415      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
39416      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
39417      */
39418     /**
39419      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
39420      * class name applied to the tab strip item representing the child Component, allowing special
39421      * styling to be applied.
39422      */
39423     /**
39424      * @cfg {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on
39425      * browser resize (defaults to true).
39426      */
39427     monitorResize : true,
39428     /**
39429      * @cfg {Boolean} deferredRender
39430      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
39431      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
39432      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
39433      * is rendered. If there is a significant amount of content or a lot of heavy controls being
39434      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
39435      * improve performance.</p>
39436      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
39437      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
39438      * configuration value.</p>
39439      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
39440      * within an unactivated tab will not be available. For example, this means that if the TabPanel
39441      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
39442      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
39443      * available to either {@link Ext.form.BasicForm#getValues getValues} or
39444      * {@link Ext.form.BasicForm#setValues setValues}.</p>
39445      */
39446     deferredRender : true,
39447     /**
39448      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
39449      */
39450     tabWidth : 120,
39451     /**
39452      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
39453      */
39454     minTabWidth : 30,
39455     /**
39456      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
39457      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
39458      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
39459      */
39460     resizeTabs : false,
39461     /**
39462      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
39463      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
39464      */
39465     enableTabScroll : false,
39466     /**
39467      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
39468      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
39469      * applies when <tt>{@link #enableTabScroll} = true</tt>.
39470      */
39471     scrollIncrement : 0,
39472     /**
39473      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
39474      * continuously pressed (defaults to <tt>400</tt>).
39475      */
39476     scrollRepeatInterval : 400,
39477     /**
39478      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
39479      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
39480      */
39481     scrollDuration : 0.35,
39482     /**
39483      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
39484      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
39485      */
39486     animScroll : true,
39487     /**
39488      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
39489      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
39490      * <tt>tabPosition: 'top'</tt>.
39491      */
39492     tabPosition : 'top',
39493     /**
39494      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
39495      */
39496     baseCls : 'x-tab-panel',
39497     /**
39498      * @cfg {Boolean} autoTabs
39499      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
39500      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
39501      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
39502      * method).</p>
39503      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
39504      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
39505      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
39506      * <p>There are a couple of things to note when using this method:<ul>
39507      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
39508      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
39509      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
39510      * components.</li>
39511      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
39512      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
39513      * until each tab is activated and will be visible outside the TabPanel.</li>
39514      * </ul>Example usage:</p>
39515      * <pre><code>
39516 var tabs = new Ext.TabPanel({
39517     applyTo: 'my-tabs',
39518     activeTab: 0,
39519     deferredRender: false,
39520     autoTabs: true
39521 });
39522
39523 // This markup will be converted to a TabPanel from the code above
39524 &lt;div id="my-tabs">
39525     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
39526     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
39527 &lt;/div>
39528 </code></pre>
39529      */
39530     autoTabs : false,
39531     /**
39532      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
39533      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
39534      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
39535      * tab panel only (so that multiple tab panels from markup can be supported on a page).
39536      */
39537     autoTabSelector : 'div.x-tab',
39538     /**
39539      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
39540      * activated on render (defaults to none).
39541      */
39542     activeTab : null,
39543     /**
39544      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
39545      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
39546      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
39547      */
39548     tabMargin : 2,
39549     /**
39550      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
39551      * (defaults to <tt>false</tt>).
39552      */
39553     plain : false,
39554     /**
39555      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
39556      * scrolling (defaults to <tt>20</tt>).
39557      */
39558     wheelIncrement : 20,
39559
39560     /*
39561      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
39562      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
39563      * potentially clash with this one.
39564      */
39565     idDelimiter : '__',
39566
39567     // private
39568     itemCls : 'x-tab-item',
39569
39570     // private config overrides
39571     elements : 'body',
39572     headerAsText : false,
39573     frame : false,
39574     hideBorders :true,
39575
39576     // private
39577     initComponent : function(){
39578         this.frame = false;
39579         Ext.TabPanel.superclass.initComponent.call(this);
39580         this.addEvents(
39581             /**
39582              * @event beforetabchange
39583              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
39584              * @param {TabPanel} this
39585              * @param {Panel} newTab The tab being activated
39586              * @param {Panel} currentTab The current active tab
39587              */
39588             'beforetabchange',
39589             /**
39590              * @event tabchange
39591              * Fires after the active tab has changed.
39592              * @param {TabPanel} this
39593              * @param {Panel} tab The new active tab
39594              */
39595             'tabchange',
39596             /**
39597              * @event contextmenu
39598              * Relays the contextmenu event from a tab selector element in the tab strip.
39599              * @param {TabPanel} this
39600              * @param {Panel} tab The target tab
39601              * @param {EventObject} e
39602              */
39603             'contextmenu'
39604         );
39605         /**
39606          * @cfg {Object} layoutConfig
39607          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
39608          * <code>layoutConfig</code> may be used to configure this layout manager.
39609          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
39610          * configured on the TabPanel will be applied as configs to the layout manager.
39611          */
39612         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
39613             layoutOnCardChange: this.layoutOnTabChange,
39614             deferredRender: this.deferredRender
39615         }, this.layoutConfig)));
39616
39617         if(this.tabPosition == 'top'){
39618             this.elements += ',header';
39619             this.stripTarget = 'header';
39620         }else {
39621             this.elements += ',footer';
39622             this.stripTarget = 'footer';
39623         }
39624         if(!this.stack){
39625             this.stack = Ext.TabPanel.AccessStack();
39626         }
39627         this.initItems();
39628     },
39629
39630     // private
39631     onRender : function(ct, position){
39632         Ext.TabPanel.superclass.onRender.call(this, ct, position);
39633
39634         if(this.plain){
39635             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
39636             this[pos].addClass('x-tab-panel-'+pos+'-plain');
39637         }
39638
39639         var st = this[this.stripTarget];
39640
39641         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
39642             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
39643
39644         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
39645         this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
39646         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
39647
39648         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
39649         this.strip.createChild({cls:'x-clear'});
39650
39651         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
39652
39653         /**
39654          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
39655          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
39656          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
39657          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
39658          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
39659          * native <tt>{@link #getTemplateArgs}</tt>.</p>
39660          * <p>The child element which contains the title text must be marked by the CSS class
39661          * <tt>x-tab-strip-inner</tt>.</p>
39662          * <p>To enable closability, the created element should contain an element marked by the CSS class
39663          * <tt>x-tab-strip-close</tt>.</p>
39664          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
39665          * style rules to create the desired appearance.</p>
39666          * Below is an example of how to create customized tab selector items:<pre><code>
39667 new Ext.TabPanel({
39668     renderTo: document.body,
39669     minTabWidth: 115,
39670     tabWidth: 135,
39671     enableTabScroll: true,
39672     width: 600,
39673     height: 250,
39674     defaults: {autoScroll:true},
39675     itemTpl: new Ext.XTemplate(
39676     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
39677          '&lt;tpl if="closable">',
39678             '&lt;a class="x-tab-strip-close" onclick="return false;">&lt;/a>',
39679          '&lt;/tpl>',
39680          '&lt;a class="x-tab-right" href="#" onclick="return false;" style="padding-left:6px">',
39681             '&lt;em class="x-tab-left">',
39682                 '&lt;span class="x-tab-strip-inner">',
39683                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
39684                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
39685                 '&lt;/span>',
39686             '&lt;/em>',
39687         '&lt;/a>',
39688     '&lt;/li>'
39689     ),
39690     getTemplateArgs: function(item) {
39691 //      Call the native method to collect the base data. Like the ID!
39692         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
39693
39694 //      Add stuff used in our template
39695         return Ext.apply(result, {
39696             closable: item.closable,
39697             src: item.iconSrc,
39698             extra: item.extraText || ''
39699         });
39700     },
39701     items: [{
39702         title: 'New Tab 1',
39703         iconSrc: '../shared/icons/fam/grid.png',
39704         html: 'Tab Body 1',
39705         closable: true
39706     }, {
39707         title: 'New Tab 2',
39708         iconSrc: '../shared/icons/fam/grid.png',
39709         html: 'Tab Body 2',
39710         extraText: 'Extra stuff in the tab button'
39711     }]
39712 });
39713 </code></pre>
39714          */
39715         if(!this.itemTpl){
39716             var tt = new Ext.Template(
39717                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close" onclick="return false;"></a>',
39718                  '<a class="x-tab-right" href="#" onclick="return false;"><em class="x-tab-left">',
39719                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
39720                  '</em></a></li>'
39721             );
39722             tt.disableFormats = true;
39723             tt.compile();
39724             Ext.TabPanel.prototype.itemTpl = tt;
39725         }
39726
39727         this.items.each(this.initTab, this);
39728     },
39729
39730     // private
39731     afterRender : function(){
39732         Ext.TabPanel.superclass.afterRender.call(this);
39733         if(this.autoTabs){
39734             this.readTabs(false);
39735         }
39736         if(this.activeTab !== undefined){
39737             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
39738             delete this.activeTab;
39739             this.setActiveTab(item);
39740         }
39741     },
39742
39743     // private
39744     initEvents : function(){
39745         Ext.TabPanel.superclass.initEvents.call(this);
39746         this.on('add', this.onAdd, this, {target: this});
39747         this.on('remove', this.onRemove, this, {target: this});
39748
39749         this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);
39750         this.mon(this.strip, 'contextmenu', this.onStripContextMenu, this);
39751         if(this.enableTabScroll){
39752             this.mon(this.strip, 'mousewheel', this.onWheel, this);
39753         }
39754     },
39755
39756     // private
39757     findTargets : function(e){
39758         var item = null;
39759         var itemEl = e.getTarget('li', this.strip);
39760         if(itemEl){
39761             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
39762             if(item.disabled){
39763                 return {
39764                     close : null,
39765                     item : null,
39766                     el : null
39767                 };
39768             }
39769         }
39770         return {
39771             close : e.getTarget('.x-tab-strip-close', this.strip),
39772             item : item,
39773             el : itemEl
39774         };
39775     },
39776
39777     // private
39778     onStripMouseDown : function(e){
39779         if(e.button !== 0){
39780             return;
39781         }
39782         e.preventDefault();
39783         var t = this.findTargets(e);
39784         if(t.close){
39785             if (t.item.fireEvent('beforeclose', t.item) !== false) {
39786                 t.item.fireEvent('close', t.item);
39787                 this.remove(t.item);
39788             }
39789             return;
39790         }
39791         if(t.item && t.item != this.activeTab){
39792             this.setActiveTab(t.item);
39793         }
39794     },
39795
39796     // private
39797     onStripContextMenu : function(e){
39798         e.preventDefault();
39799         var t = this.findTargets(e);
39800         if(t.item){
39801             this.fireEvent('contextmenu', this, t.item, e);
39802         }
39803     },
39804
39805     /**
39806      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
39807      * <tt>{@link #autoTabSelector}</tt>
39808      * @param {Boolean} removeExisting True to remove existing tabs
39809      */
39810     readTabs : function(removeExisting){
39811         if(removeExisting === true){
39812             this.items.each(function(item){
39813                 this.remove(item);
39814             }, this);
39815         }
39816         var tabs = this.el.query(this.autoTabSelector);
39817         for(var i = 0, len = tabs.length; i < len; i++){
39818             var tab = tabs[i];
39819             var title = tab.getAttribute('title');
39820             tab.removeAttribute('title');
39821             this.add({
39822                 title: title,
39823                 contentEl: tab
39824             });
39825         }
39826     },
39827
39828     // private
39829     initTab : function(item, index){
39830         var before = this.strip.dom.childNodes[index];
39831         var p = this.getTemplateArgs(item);
39832         var el = before ?
39833                  this.itemTpl.insertBefore(before, p) :
39834                  this.itemTpl.append(this.strip, p);
39835
39836         Ext.fly(el).addClassOnOver('x-tab-strip-over');
39837
39838         if(item.tabTip){
39839             Ext.fly(el).child('span.x-tab-strip-text', true).qtip = item.tabTip;
39840         }
39841         item.tabEl = el;
39842
39843         item.on('disable', this.onItemDisabled, this);
39844         item.on('enable', this.onItemEnabled, this);
39845         item.on('titlechange', this.onItemTitleChanged, this);
39846         item.on('iconchange', this.onItemIconChanged, this);
39847         item.on('beforeshow', this.onBeforeShowItem, this);
39848     },
39849
39850     /**
39851      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
39852      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
39853      * to create a formatted, clickable tab selector element. The properties which must be returned
39854      * are:</p><div class="mdetail-params"><ul>
39855      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
39856      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
39857      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
39858      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
39859      * </ul></div>
39860      * @param {BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
39861      * @return {Object} An object hash containing the properties required to render the selector element.
39862      */
39863     getTemplateArgs : function(item) {
39864         var cls = item.closable ? 'x-tab-strip-closable' : '';
39865         if(item.disabled){
39866             cls += ' x-item-disabled';
39867         }
39868         if(item.iconCls){
39869             cls += ' x-tab-with-icon';
39870         }
39871         if(item.tabCls){
39872             cls += ' ' + item.tabCls;
39873         }
39874
39875         return {
39876             id: this.id + this.idDelimiter + item.getItemId(),
39877             text: item.title,
39878             cls: cls,
39879             iconCls: item.iconCls || ''
39880         };
39881     },
39882
39883     // private
39884     onAdd : function(tp, item, index){
39885         this.initTab(item, index);
39886         if(this.items.getCount() == 1){
39887             this.syncSize();
39888         }
39889         this.delegateUpdates();
39890     },
39891
39892     // private
39893     onBeforeAdd : function(item){
39894         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
39895         if(existing){
39896             this.setActiveTab(item);
39897             return false;
39898         }
39899         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
39900         var es = item.elements;
39901         item.elements = es ? es.replace(',header', '') : es;
39902         item.border = (item.border === true);
39903     },
39904
39905     // private
39906     onRemove : function(tp, item){
39907         Ext.destroy(Ext.get(this.getTabEl(item)));
39908         this.stack.remove(item);
39909         item.un('disable', this.onItemDisabled, this);
39910         item.un('enable', this.onItemEnabled, this);
39911         item.un('titlechange', this.onItemTitleChanged, this);
39912         item.un('iconchange', this.onItemIconChanged, this);
39913         item.un('beforeshow', this.onBeforeShowItem, this);
39914         if(item == this.activeTab){
39915             var next = this.stack.next();
39916             if(next){
39917                 this.setActiveTab(next);
39918             }else if(this.items.getCount() > 0){
39919                 this.setActiveTab(0);
39920             }else{
39921                 this.activeTab = null;
39922             }
39923         }
39924         this.delegateUpdates();
39925     },
39926
39927     // private
39928     onBeforeShowItem : function(item){
39929         if(item != this.activeTab){
39930             this.setActiveTab(item);
39931             return false;
39932         }
39933     },
39934
39935     // private
39936     onItemDisabled : function(item){
39937         var el = this.getTabEl(item);
39938         if(el){
39939             Ext.fly(el).addClass('x-item-disabled');
39940         }
39941         this.stack.remove(item);
39942     },
39943
39944     // private
39945     onItemEnabled : function(item){
39946         var el = this.getTabEl(item);
39947         if(el){
39948             Ext.fly(el).removeClass('x-item-disabled');
39949         }
39950     },
39951
39952     // private
39953     onItemTitleChanged : function(item){
39954         var el = this.getTabEl(item);
39955         if(el){
39956             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
39957         }
39958     },
39959
39960     //private
39961     onItemIconChanged : function(item, iconCls, oldCls){
39962         var el = this.getTabEl(item);
39963         if(el){
39964             Ext.fly(el).child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
39965         }
39966     },
39967
39968     /**
39969      * Gets the DOM element for the tab strip item which activates the child panel with the specified
39970      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
39971      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
39972      * @return {HTMLElement} The DOM node
39973      */
39974     getTabEl : function(item){
39975         return document.getElementById(this.id + this.idDelimiter + this.getComponent(item).getItemId());
39976     },
39977
39978     // private
39979     onResize : function(){
39980         Ext.TabPanel.superclass.onResize.apply(this, arguments);
39981         this.delegateUpdates();
39982     },
39983
39984     /**
39985      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
39986      */
39987     beginUpdate : function(){
39988         this.suspendUpdates = true;
39989     },
39990
39991     /**
39992      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
39993      */
39994     endUpdate : function(){
39995         this.suspendUpdates = false;
39996         this.delegateUpdates();
39997     },
39998
39999     /**
40000      * Hides the tab strip item for the passed tab
40001      * @param {Number/String/Panel} item The tab index, id or item
40002      */
40003     hideTabStripItem : function(item){
40004         item = this.getComponent(item);
40005         var el = this.getTabEl(item);
40006         if(el){
40007             el.style.display = 'none';
40008             this.delegateUpdates();
40009         }
40010         this.stack.remove(item);
40011     },
40012
40013     /**
40014      * Unhides the tab strip item for the passed tab
40015      * @param {Number/String/Panel} item The tab index, id or item
40016      */
40017     unhideTabStripItem : function(item){
40018         item = this.getComponent(item);
40019         var el = this.getTabEl(item);
40020         if(el){
40021             el.style.display = '';
40022             this.delegateUpdates();
40023         }
40024     },
40025
40026     // private
40027     delegateUpdates : function(){
40028         if(this.suspendUpdates){
40029             return;
40030         }
40031         if(this.resizeTabs && this.rendered){
40032             this.autoSizeTabs();
40033         }
40034         if(this.enableTabScroll && this.rendered){
40035             this.autoScrollTabs();
40036         }
40037     },
40038
40039     // private
40040     autoSizeTabs : function(){
40041         var count = this.items.length;
40042         var ce = this.tabPosition != 'bottom' ? 'header' : 'footer';
40043         var ow = this[ce].dom.offsetWidth;
40044         var aw = this[ce].dom.clientWidth;
40045
40046         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
40047             return;
40048         }
40049
40050         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
40051         this.lastTabWidth = each;
40052         var lis = this.strip.query("li:not([className^=x-tab-edge])");
40053         for(var i = 0, len = lis.length; i < len; i++) {
40054             var li = lis[i];
40055             var inner = Ext.fly(li).child('.x-tab-strip-inner', true);
40056             var tw = li.offsetWidth;
40057             var iw = inner.offsetWidth;
40058             inner.style.width = (each - (tw-iw)) + 'px';
40059         }
40060     },
40061
40062     // private
40063     adjustBodyWidth : function(w){
40064         if(this.header){
40065             this.header.setWidth(w);
40066         }
40067         if(this.footer){
40068             this.footer.setWidth(w);
40069         }
40070         return w;
40071     },
40072
40073     /**
40074      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
40075      * can <tt>return false</tt> to cancel the tab change.
40076      * @param {String/Number} item
40077      * The id or tab Panel to activate. This parameter may be any of the following:
40078      * <div><ul class="mdetail-params">
40079      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
40080      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
40081      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
40082      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
40083      * </ul></div>
40084      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
40085      */
40086     setActiveTab : function(item){
40087         item = this.getComponent(item);
40088         if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
40089             return;
40090         }
40091         if(!this.rendered){
40092             this.activeTab = item;
40093             return;
40094         }
40095         if(this.activeTab != item){
40096             if(this.activeTab){
40097                 var oldEl = this.getTabEl(this.activeTab);
40098                 if(oldEl){
40099                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
40100                 }
40101                 this.activeTab.fireEvent('deactivate', this.activeTab);
40102             }
40103             var el = this.getTabEl(item);
40104             Ext.fly(el).addClass('x-tab-strip-active');
40105             this.activeTab = item;
40106             this.stack.add(item);
40107
40108             this.layout.setActiveItem(item);
40109             if(this.scrolling){
40110                 this.scrollToTab(item, this.animScroll);
40111             }
40112
40113             item.fireEvent('activate', item);
40114             this.fireEvent('tabchange', this, item);
40115         }
40116     },
40117
40118     /**
40119      * Gets the currently active tab.
40120      * @return {Panel} The active tab
40121      */
40122     getActiveTab : function(){
40123         return this.activeTab || null;
40124     },
40125
40126     /**
40127      * Gets the specified tab by id.
40128      * @param {String} id The tab id
40129      * @return {Panel} The tab
40130      */
40131     getItem : function(item){
40132         return this.getComponent(item);
40133     },
40134
40135     // private
40136     autoScrollTabs : function(){
40137         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
40138         var count = this.items.length;
40139         var ow = this.pos.dom.offsetWidth;
40140         var tw = this.pos.dom.clientWidth;
40141
40142         var wrap = this.stripWrap;
40143         var wd = wrap.dom;
40144         var cw = wd.offsetWidth;
40145         var pos = this.getScrollPos();
40146         var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
40147
40148         if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
40149             return;
40150         }
40151         if(l <= tw){
40152             wd.scrollLeft = 0;
40153             wrap.setWidth(tw);
40154             if(this.scrolling){
40155                 this.scrolling = false;
40156                 this.pos.removeClass('x-tab-scrolling');
40157                 this.scrollLeft.hide();
40158                 this.scrollRight.hide();
40159                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
40160                 if(Ext.isAir || Ext.isWebKit){
40161                     wd.style.marginLeft = '';
40162                     wd.style.marginRight = '';
40163                 }
40164             }
40165         }else{
40166             if(!this.scrolling){
40167                 this.pos.addClass('x-tab-scrolling');
40168                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
40169                 if(Ext.isAir || Ext.isWebKit){
40170                     wd.style.marginLeft = '18px';
40171                     wd.style.marginRight = '18px';
40172                 }
40173             }
40174             tw -= wrap.getMargins('lr');
40175             wrap.setWidth(tw > 20 ? tw : 20);
40176             if(!this.scrolling){
40177                 if(!this.scrollLeft){
40178                     this.createScrollers();
40179                 }else{
40180                     this.scrollLeft.show();
40181                     this.scrollRight.show();
40182                 }
40183             }
40184             this.scrolling = true;
40185             if(pos > (l-tw)){ // ensure it stays within bounds
40186                 wd.scrollLeft = l-tw;
40187             }else{ // otherwise, make sure the active tab is still visible
40188                 this.scrollToTab(this.activeTab, false);
40189             }
40190             this.updateScrollButtons();
40191         }
40192     },
40193
40194     // private
40195     createScrollers : function(){
40196         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
40197         var h = this.stripWrap.dom.offsetHeight;
40198
40199         // left
40200         var sl = this.pos.insertFirst({
40201             cls:'x-tab-scroller-left'
40202         });
40203         sl.setHeight(h);
40204         sl.addClassOnOver('x-tab-scroller-left-over');
40205         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
40206             interval : this.scrollRepeatInterval,
40207             handler: this.onScrollLeft,
40208             scope: this
40209         });
40210         this.scrollLeft = sl;
40211
40212         // right
40213         var sr = this.pos.insertFirst({
40214             cls:'x-tab-scroller-right'
40215         });
40216         sr.setHeight(h);
40217         sr.addClassOnOver('x-tab-scroller-right-over');
40218         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
40219             interval : this.scrollRepeatInterval,
40220             handler: this.onScrollRight,
40221             scope: this
40222         });
40223         this.scrollRight = sr;
40224     },
40225
40226     // private
40227     getScrollWidth : function(){
40228         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
40229     },
40230
40231     // private
40232     getScrollPos : function(){
40233         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
40234     },
40235
40236     // private
40237     getScrollArea : function(){
40238         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
40239     },
40240
40241     // private
40242     getScrollAnim : function(){
40243         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
40244     },
40245
40246     // private
40247     getScrollIncrement : function(){
40248         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
40249     },
40250
40251     /**
40252      * Scrolls to a particular tab if tab scrolling is enabled
40253      * @param {Panel} item The item to scroll to
40254      * @param {Boolean} animate True to enable animations
40255      */
40256
40257     scrollToTab : function(item, animate){
40258         if(!item){ return; }
40259         var el = this.getTabEl(item);
40260         var pos = this.getScrollPos(), area = this.getScrollArea();
40261         var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
40262         var right = left + el.offsetWidth;
40263         if(left < pos){
40264             this.scrollTo(left, animate);
40265         }else if(right > (pos + area)){
40266             this.scrollTo(right - area, animate);
40267         }
40268     },
40269
40270     // private
40271     scrollTo : function(pos, animate){
40272         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
40273         if(!animate){
40274             this.updateScrollButtons();
40275         }
40276     },
40277
40278     onWheel : function(e){
40279         var d = e.getWheelDelta()*this.wheelIncrement*-1;
40280         e.stopEvent();
40281
40282         var pos = this.getScrollPos();
40283         var newpos = pos + d;
40284         var sw = this.getScrollWidth()-this.getScrollArea();
40285
40286         var s = Math.max(0, Math.min(sw, newpos));
40287         if(s != pos){
40288             this.scrollTo(s, false);
40289         }
40290     },
40291
40292     // private
40293     onScrollRight : function(){
40294         var sw = this.getScrollWidth()-this.getScrollArea();
40295         var pos = this.getScrollPos();
40296         var s = Math.min(sw, pos + this.getScrollIncrement());
40297         if(s != pos){
40298             this.scrollTo(s, this.animScroll);
40299         }
40300     },
40301
40302     // private
40303     onScrollLeft : function(){
40304         var pos = this.getScrollPos();
40305         var s = Math.max(0, pos - this.getScrollIncrement());
40306         if(s != pos){
40307             this.scrollTo(s, this.animScroll);
40308         }
40309     },
40310
40311     // private
40312     updateScrollButtons : function(){
40313         var pos = this.getScrollPos();
40314         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
40315         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
40316     },
40317
40318     // private
40319     beforeDestroy : function() {
40320         if(this.items){
40321             this.items.each(function(item){
40322                 if(item && item.tabEl){
40323                     Ext.get(item.tabEl).removeAllListeners();
40324                     item.tabEl = null;
40325                 }
40326             }, this);
40327         }
40328         if(this.strip){
40329             this.strip.removeAllListeners();
40330         }
40331         Ext.TabPanel.superclass.beforeDestroy.apply(this);
40332     }
40333
40334     /**
40335      * @cfg {Boolean} collapsible
40336      * @hide
40337      */
40338     /**
40339      * @cfg {String} header
40340      * @hide
40341      */
40342     /**
40343      * @cfg {Boolean} headerAsText
40344      * @hide
40345      */
40346     /**
40347      * @property header
40348      * @hide
40349      */
40350     /**
40351      * @property title
40352      * @hide
40353      */
40354     /**
40355      * @cfg {Array} tools
40356      * @hide
40357      */
40358     /**
40359      * @cfg {Array} toolTemplate
40360      * @hide
40361      */
40362     /**
40363      * @cfg {Boolean} hideCollapseTool
40364      * @hide
40365      */
40366     /**
40367      * @cfg {Boolean} titleCollapse
40368      * @hide
40369      */
40370     /**
40371      * @cfg {Boolean} collapsed
40372      * @hide
40373      */
40374     /**
40375      * @cfg {String} layout
40376      * @hide
40377      */
40378     /**
40379      * @cfg {Boolean} preventBodyReset
40380      * @hide
40381      */
40382 });
40383 Ext.reg('tabpanel', Ext.TabPanel);
40384
40385 /**
40386  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
40387  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
40388  * @param {String/Panel} tab The id or tab Panel to activate
40389  * @method activate
40390  */
40391 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
40392
40393 // private utility class used by TabPanel
40394 Ext.TabPanel.AccessStack = function(){
40395     var items = [];
40396     return {
40397         add : function(item){
40398             items.push(item);
40399             if(items.length > 10){
40400                 items.shift();
40401             }
40402         },
40403
40404         remove : function(item){
40405             var s = [];
40406             for(var i = 0, len = items.length; i < len; i++) {
40407                 if(items[i] != item){
40408                     s.push(items[i]);
40409                 }
40410             }
40411             items = s;
40412         },
40413
40414         next : function(){
40415             return items.pop();
40416         }
40417     };
40418 };/**
40419  * @class Ext.Button
40420  * @extends Ext.BoxComponent
40421  * Simple Button class
40422  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
40423  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
40424  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
40425  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
40426  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
40427  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
40428  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
40429  * </ul></div>
40430  * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the handler is executed. Defaults to this Button.
40431  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
40432  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
40433  * @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
40434  * @cfg {Boolean} hidden True to start hidden (defaults to false)
40435  * @cfg {Boolean} disabled True to start disabled (defaults to false)
40436  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
40437  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
40438  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
40439  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
40440  * @constructor
40441  * Create a new button
40442  * @param {Object} config The config object
40443  * @xtype button
40444  */
40445 Ext.Button = Ext.extend(Ext.BoxComponent, {
40446     /**
40447      * Read-only. True if this button is hidden
40448      * @type Boolean
40449      */
40450     hidden : false,
40451     /**
40452      * Read-only. True if this button is disabled
40453      * @type Boolean
40454      */
40455     disabled : false,
40456     /**
40457      * Read-only. True if this button is pressed (only if enableToggle = true)
40458      * @type Boolean
40459      */
40460     pressed : false,
40461     /**
40462      * The Button's owner {@link Ext.Panel} (defaults to undefined, and is set automatically when
40463      * the Button is added to a container).  Read-only.
40464      * @type Ext.Panel
40465      * @property ownerCt
40466      */
40467
40468     /**
40469      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
40470      */
40471
40472     /**
40473      * @cfg {Boolean} allowDepress
40474      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
40475      */
40476
40477     /**
40478      * @cfg {Boolean} enableToggle
40479      * True to enable pressed/not pressed toggling (defaults to false)
40480      */
40481     enableToggle: false,
40482     /**
40483      * @cfg {Function} toggleHandler
40484      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
40485      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
40486      * <li><b>state</b> : Boolean<div class="sub-desc">The next state if the Button, true means pressed.</div></li>
40487      * </ul>
40488      */
40489     /**
40490      * @cfg {Mixed} menu
40491      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
40492      */
40493     /**
40494      * @cfg {String} menuAlign
40495      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
40496      */
40497     menuAlign : 'tl-bl?',
40498
40499     /**
40500      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
40501      * text to be used if this item is shown in the overflow menu. See also
40502      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
40503      */
40504     /**
40505      * @cfg {String} iconCls
40506      * A css class which sets a background image to be used as the icon for this button
40507      */
40508     /**
40509      * @cfg {String} type
40510      * submit, reset or button - defaults to 'button'
40511      */
40512     type : 'button',
40513
40514     // private
40515     menuClassTarget: 'tr:nth(2)',
40516
40517     /**
40518      * @cfg {String} clickEvent
40519      * The type of event to map to the button's event handler (defaults to 'click')
40520      */
40521     clickEvent : 'click',
40522
40523     /**
40524      * @cfg {Boolean} handleMouseEvents
40525      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
40526      */
40527     handleMouseEvents : true,
40528
40529     /**
40530      * @cfg {String} tooltipType
40531      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
40532      */
40533     tooltipType : 'qtip',
40534
40535     /**
40536      * @cfg {String} buttonSelector
40537      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
40538      * DOM structure created.</p>
40539      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
40540      * a focussable element.</p>
40541      * <p>Defaults to <b><tt>"button:first-child"</tt></b>.</p>
40542      */
40543     buttonSelector : 'button:first-child',
40544
40545     /**
40546      * @cfg {String} scale
40547      * <p>(Optional) The size of the Button. Three values are allowed:</p>
40548      * <ul class="mdetail-params">
40549      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
40550      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
40551      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
40552      * </ul>
40553      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
40554      */
40555     scale: 'small',
40556
40557     /**
40558      * @cfg {String} iconAlign
40559      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
40560      * <ul class="mdetail-params">
40561      * <li>'top'<div class="sub-desc"></div></li>
40562      * <li>'right'<div class="sub-desc"></div></li>
40563      * <li>'bottom'<div class="sub-desc"></div></li>
40564      * <li>'left'<div class="sub-desc"></div></li>
40565      * </ul>
40566      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
40567      */
40568     iconAlign : 'left',
40569
40570     /**
40571      * @cfg {String} arrowAlign
40572      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
40573      * Two values are allowed:</p>
40574      * <ul class="mdetail-params">
40575      * <li>'right'<div class="sub-desc"></div></li>
40576      * <li>'bottom'<div class="sub-desc"></div></li>
40577      * </ul>
40578      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
40579      */
40580     arrowAlign : 'right',
40581
40582     /**
40583      * @cfg {Ext.Template} template (Optional)
40584      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
40585      * Instances, or subclasses which need a different DOM structure may provide a different
40586      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
40587      * @type Ext.Template
40588      * @property template
40589      */
40590     /**
40591      * @cfg {String} cls
40592      * A CSS class string to apply to the button's main element.
40593      */
40594     /**
40595      * @property menu
40596      * @type Menu
40597      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
40598      */
40599
40600     initComponent : function(){
40601         Ext.Button.superclass.initComponent.call(this);
40602
40603         this.addEvents(
40604             /**
40605              * @event click
40606              * Fires when this button is clicked
40607              * @param {Button} this
40608              * @param {EventObject} e The click event
40609              */
40610             'click',
40611             /**
40612              * @event toggle
40613              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
40614              * @param {Button} this
40615              * @param {Boolean} pressed
40616              */
40617             'toggle',
40618             /**
40619              * @event mouseover
40620              * Fires when the mouse hovers over the button
40621              * @param {Button} this
40622              * @param {Event} e The event object
40623              */
40624             'mouseover',
40625             /**
40626              * @event mouseout
40627              * Fires when the mouse exits the button
40628              * @param {Button} this
40629              * @param {Event} e The event object
40630              */
40631             'mouseout',
40632             /**
40633              * @event menushow
40634              * If this button has a menu, this event fires when it is shown
40635              * @param {Button} this
40636              * @param {Menu} menu
40637              */
40638             'menushow',
40639             /**
40640              * @event menuhide
40641              * If this button has a menu, this event fires when it is hidden
40642              * @param {Button} this
40643              * @param {Menu} menu
40644              */
40645             'menuhide',
40646             /**
40647              * @event menutriggerover
40648              * If this button has a menu, this event fires when the mouse enters the menu triggering element
40649              * @param {Button} this
40650              * @param {Menu} menu
40651              * @param {EventObject} e
40652              */
40653             'menutriggerover',
40654             /**
40655              * @event menutriggerout
40656              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
40657              * @param {Button} this
40658              * @param {Menu} menu
40659              * @param {EventObject} e
40660              */
40661             'menutriggerout'
40662         );
40663         if(this.menu){
40664             this.menu = Ext.menu.MenuMgr.get(this.menu);
40665         }
40666         if(Ext.isString(this.toggleGroup)){
40667             this.enableToggle = true;
40668         }
40669     },
40670
40671 /**
40672   * <p>This method returns an object which provides substitution parameters for the {@link #template Template} used
40673   * to create this Button's DOM structure.</p>
40674   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
40675   * own implementation of this method.</p>
40676   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
40677   * following items:</p><div class="mdetail-params"><ul>
40678   * <li>The Button's {@link #text}</li>
40679   * <li>The &lt;button&gt;'s {@link #type}</li>
40680   * <li>The {@link iconCls} applied to the &lt;button&gt; {@link #btnEl element}</li>
40681   * <li>The {@link #cls} applied to the Button's main {@link #getEl Element}</li>
40682   * <li>A CSS class name controlling the Button's {@link #scale} and {@link #iconAlign icon alignment}</li>
40683   * <li>A CSS class name which applies an arrow to the Button if configured with a {@link #menu}</li>
40684   * </ul></div>
40685   * @return {Object} Substitution data for a Template.
40686  */
40687     getTemplateArgs : function(){
40688         var cls = (this.cls || '');
40689         cls += (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';
40690         if(this.pressed){
40691             cls += ' x-btn-pressed';
40692         }
40693         return [this.text || '&#160;', this.type, this.iconCls || '', cls, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass()];
40694     },
40695
40696     // protected
40697     getMenuClass : function(){
40698         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
40699     },
40700
40701     // private
40702     onRender : function(ct, position){
40703         if(!this.template){
40704             if(!Ext.Button.buttonTemplate){
40705                 // hideous table template
40706                 Ext.Button.buttonTemplate = new Ext.Template(
40707                     '<table cellspacing="0" class="x-btn {3}"><tbody class="{4}">',
40708                     '<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>',
40709                     '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{5}" unselectable="on"><button class="x-btn-text {2}" type="{1}">{0}</button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',
40710                     '<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>',
40711                     "</tbody></table>");
40712                 Ext.Button.buttonTemplate.compile();
40713             }
40714             this.template = Ext.Button.buttonTemplate;
40715         }
40716
40717         var btn, targs = this.getTemplateArgs();
40718
40719         if(position){
40720             btn = this.template.insertBefore(position, targs, true);
40721         }else{
40722             btn = this.template.append(ct, targs, true);
40723         }
40724         /**
40725          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
40726          * this references a <tt>&lt;button&gt;</tt> element. Read only.
40727          * @type Ext.Element
40728          * @property btnEl
40729          */
40730         this.btnEl = btn.child(this.buttonSelector);
40731         this.mon(this.btnEl, {
40732             scope: this,
40733             focus: this.onFocus,
40734             blur: this.onBlur
40735         });
40736
40737         this.initButtonEl(btn, this.btnEl);
40738
40739         Ext.ButtonToggleMgr.register(this);
40740     },
40741
40742     // private
40743     initButtonEl : function(btn, btnEl){
40744         this.el = btn;
40745
40746         if(this.id){
40747             this.el.dom.id = this.el.id = this.id;
40748         }
40749         if(this.icon){
40750             btnEl.setStyle('background-image', 'url(' +this.icon +')');
40751         }
40752         if(this.tabIndex !== undefined){
40753             btnEl.dom.tabIndex = this.tabIndex;
40754         }
40755         if(this.tooltip){
40756             this.setTooltip(this.tooltip, true);
40757         }
40758
40759         if(this.handleMouseEvents){
40760             this.mon(btn, {
40761                 scope: this,
40762                 mouseover: this.onMouseOver,
40763                 mousedown: this.onMouseDown
40764             });
40765             
40766             // new functionality for monitoring on the document level
40767             //this.mon(btn, 'mouseout', this.onMouseOut, this);
40768         }
40769
40770         if(this.menu){
40771             this.mon(this.menu, {
40772                 scope: this,
40773                 show: this.onMenuShow,
40774                 hide: this.onMenuHide
40775             });
40776         }
40777
40778         if(this.repeat){
40779             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
40780             this.mon(repeater, 'click', this.onClick, this);
40781         }
40782         
40783         this.mon(btn, this.clickEvent, this.onClick, this);
40784     },
40785
40786     // private
40787     afterRender : function(){
40788         Ext.Button.superclass.afterRender.call(this);
40789         this.doAutoWidth();
40790     },
40791
40792     /**
40793      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
40794      * the value of the {@link iconCls} config internally.
40795      * @param {String} cls The CSS class providing the icon image
40796      * @return {Ext.Button} this
40797      */
40798     setIconClass : function(cls){
40799         if(this.el){
40800             this.btnEl.replaceClass(this.iconCls, cls);
40801         }
40802         this.iconCls = cls;
40803         return this;
40804     },
40805
40806     /**
40807      * Sets the tooltip for this Button.
40808      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
40809      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
40810      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
40811      * </ul></div>
40812      * @return {Ext.Button} this
40813      */
40814     setTooltip : function(tooltip, /* private */ initial){
40815         if(this.rendered){
40816             if(!initial){
40817                 this.clearTip();
40818             }
40819             if(Ext.isObject(tooltip)){
40820                 Ext.QuickTips.register(Ext.apply({
40821                       target: this.btnEl.id
40822                 }, tooltip));
40823                 this.tooltip = tooltip;
40824             }else{
40825                 this.btnEl.dom[this.tooltipType] = tooltip;
40826             }
40827         }else{
40828             this.tooltip = tooltip;
40829         }
40830         return this;
40831     },
40832     
40833     // private
40834     clearTip: function(){
40835         if(Ext.isObject(this.tooltip)){
40836             Ext.QuickTips.unregister(this.btnEl);
40837         }
40838     },
40839     
40840     // private
40841     beforeDestroy: function(){
40842         if(this.rendered){
40843             this.clearTip();
40844         }
40845         Ext.destroy(this.menu, this.repeater);
40846     },
40847
40848     // private
40849     onDestroy : function(){
40850         var doc = Ext.getDoc();
40851         doc.un('mouseover', this.monitorMouseOver, this);
40852         doc.un('mouseup', this.onMouseUp, this);
40853         if(this.rendered){
40854             Ext.ButtonToggleMgr.unregister(this);
40855         }
40856     },
40857
40858     // private
40859     doAutoWidth : function(){
40860         if(this.el && this.text && this.width === undefined){
40861             this.el.setWidth('auto');
40862             if(Ext.isIE7 && Ext.isStrict){
40863                 var ib = this.btnEl;
40864                 if(ib && ib.getWidth() > 20){
40865                     ib.clip();
40866                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
40867                 }
40868             }
40869             if(this.minWidth){
40870                 if(this.el.getWidth() < this.minWidth){
40871                     this.el.setWidth(this.minWidth);
40872                 }
40873             }
40874         }
40875     },
40876
40877     /**
40878      * Assigns this Button's click handler
40879      * @param {Function} handler The function to call when the button is clicked
40880      * @param {Object} scope (optional) Scope for the function passed in
40881      * @return {Ext.Button} this
40882      */
40883     setHandler : function(handler, scope){
40884         this.handler = handler;
40885         this.scope = scope;
40886         return this;
40887     },
40888
40889     /**
40890      * Sets this Button's text
40891      * @param {String} text The button text
40892      * @return {Ext.Button} this
40893      */
40894     setText : function(text){
40895         this.text = text;
40896         if(this.el){
40897             this.el.child('td.x-btn-mc ' + this.buttonSelector).update(text);
40898         }
40899         this.doAutoWidth();
40900         return this;
40901     },
40902
40903     /**
40904      * Gets the text for this Button
40905      * @return {String} The button text
40906      */
40907     getText : function(){
40908         return this.text;
40909     },
40910
40911     /**
40912      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
40913      * @param {Boolean} state (optional) Force a particular state
40914      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
40915      * @return {Ext.Button} this
40916      */
40917     toggle : function(state, suppressEvent){
40918         state = state === undefined ? !this.pressed : !!state;
40919         if(state != this.pressed){
40920             this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
40921             this.pressed = state;
40922             if(!suppressEvent){
40923                 this.fireEvent('toggle', this, state);
40924                 if(this.toggleHandler){
40925                     this.toggleHandler.call(this.scope || this, this, state);
40926                 }
40927             }
40928         }
40929         return this;
40930     },
40931
40932     /**
40933      * Focus the button
40934      */
40935     focus : function(){
40936         this.btnEl.focus();
40937     },
40938
40939     // private
40940     onDisable : function(){
40941         this.onDisableChange(true);
40942     },
40943
40944     // private
40945     onEnable : function(){
40946         this.onDisableChange(false);
40947     },
40948     
40949     onDisableChange : function(disabled){
40950         if(this.el){
40951             if(!Ext.isIE6 || !this.text){
40952                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
40953             }
40954             this.el.dom.disabled = disabled;
40955         }
40956         this.disabled = disabled;
40957     },
40958
40959     /**
40960      * Show this button's menu (if it has one)
40961      */
40962     showMenu : function(){
40963         if(this.rendered && this.menu){
40964             if(this.tooltip){
40965                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
40966             }
40967             this.menu.show(this.el, this.menuAlign);
40968         }
40969         return this;
40970     },
40971
40972     /**
40973      * Hide this button's menu (if it has one)
40974      */
40975     hideMenu : function(){
40976         if(this.menu){
40977             this.menu.hide();
40978         }
40979         return this;
40980     },
40981
40982     /**
40983      * Returns true if the button has a menu and it is visible
40984      * @return {Boolean}
40985      */
40986     hasVisibleMenu : function(){
40987         return this.menu && this.menu.isVisible();
40988     },
40989
40990     // private
40991     onClick : function(e){
40992         if(e){
40993             e.preventDefault();
40994         }
40995         if(e.button !== 0){
40996             return;
40997         }
40998         if(!this.disabled){
40999             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
41000                 this.toggle();
41001             }
41002             if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
41003                 this.showMenu();
41004             }
41005             this.fireEvent('click', this, e);
41006             if(this.handler){
41007                 //this.el.removeClass('x-btn-over');
41008                 this.handler.call(this.scope || this, this, e);
41009             }
41010         }
41011     },
41012
41013     // private
41014     isMenuTriggerOver : function(e, internal){
41015         return this.menu && !internal;
41016     },
41017
41018     // private
41019     isMenuTriggerOut : function(e, internal){
41020         return this.menu && !internal;
41021     },
41022
41023     // private
41024     onMouseOver : function(e){
41025         if(!this.disabled){
41026             var internal = e.within(this.el,  true);
41027             if(!internal){
41028                 this.el.addClass('x-btn-over');
41029                 if(!this.monitoringMouseOver){
41030                     Ext.getDoc().on('mouseover', this.monitorMouseOver, this);
41031                     this.monitoringMouseOver = true;
41032                 }
41033                 this.fireEvent('mouseover', this, e);
41034             }
41035             if(this.isMenuTriggerOver(e, internal)){
41036                 this.fireEvent('menutriggerover', this, this.menu, e);
41037             }
41038         }
41039     },
41040
41041     // private
41042     monitorMouseOver : function(e){
41043         if(e.target != this.el.dom && !e.within(this.el)){
41044             if(this.monitoringMouseOver){
41045                 Ext.getDoc().un('mouseover', this.monitorMouseOver, this);
41046                 this.monitoringMouseOver = false;
41047             }
41048             this.onMouseOut(e);
41049         }
41050     },
41051
41052     // private
41053     onMouseOut : function(e){
41054         var internal = e.within(this.el) && e.target != this.el.dom;
41055         this.el.removeClass('x-btn-over');
41056         this.fireEvent('mouseout', this, e);
41057         if(this.isMenuTriggerOut(e, internal)){
41058             this.fireEvent('menutriggerout', this, this.menu, e);
41059         }
41060     },
41061     // private
41062     onFocus : function(e){
41063         if(!this.disabled){
41064             this.el.addClass('x-btn-focus');
41065         }
41066     },
41067     // private
41068     onBlur : function(e){
41069         this.el.removeClass('x-btn-focus');
41070     },
41071
41072     // private
41073     getClickEl : function(e, isUp){
41074        return this.el;
41075     },
41076
41077     // private
41078     onMouseDown : function(e){
41079         if(!this.disabled && e.button === 0){
41080             this.getClickEl(e).addClass('x-btn-click');
41081             Ext.getDoc().on('mouseup', this.onMouseUp, this);
41082         }
41083     },
41084     // private
41085     onMouseUp : function(e){
41086         if(e.button === 0){
41087             this.getClickEl(e, true).removeClass('x-btn-click');
41088             Ext.getDoc().un('mouseup', this.onMouseUp, this);
41089         }
41090     },
41091     // private
41092     onMenuShow : function(e){
41093         this.ignoreNextClick = 0;
41094         this.el.addClass('x-btn-menu-active');
41095         this.fireEvent('menushow', this, this.menu);
41096     },
41097     // private
41098     onMenuHide : function(e){
41099         this.el.removeClass('x-btn-menu-active');
41100         this.ignoreNextClick = this.restoreClick.defer(250, this);
41101         this.fireEvent('menuhide', this, this.menu);
41102     },
41103
41104     // private
41105     restoreClick : function(){
41106         this.ignoreNextClick = 0;
41107     }
41108
41109
41110
41111     /**
41112      * @cfg {String} autoEl @hide
41113      */
41114 });
41115 Ext.reg('button', Ext.Button);
41116
41117 // Private utility class used by Button
41118 Ext.ButtonToggleMgr = function(){
41119    var groups = {};
41120
41121    function toggleGroup(btn, state){
41122        if(state){
41123            var g = groups[btn.toggleGroup];
41124            for(var i = 0, l = g.length; i < l; i++){
41125                if(g[i] != btn){
41126                    g[i].toggle(false);
41127                }
41128            }
41129        }
41130    }
41131
41132    return {
41133        register : function(btn){
41134            if(!btn.toggleGroup){
41135                return;
41136            }
41137            var g = groups[btn.toggleGroup];
41138            if(!g){
41139                g = groups[btn.toggleGroup] = [];
41140            }
41141            g.push(btn);
41142            btn.on('toggle', toggleGroup);
41143        },
41144
41145        unregister : function(btn){
41146            if(!btn.toggleGroup){
41147                return;
41148            }
41149            var g = groups[btn.toggleGroup];
41150            if(g){
41151                g.remove(btn);
41152                btn.un('toggle', toggleGroup);
41153            }
41154        },
41155
41156        /**
41157         * Gets the pressed button in the passed group or null
41158         * @param {String} group
41159         * @return Button
41160         */
41161        getPressed : function(group){
41162            var g = groups[group];
41163            if(g){
41164                for(var i = 0, len = g.length; i < len; i++){
41165                    if(g[i].pressed === true){
41166                        return g[i];
41167                    }
41168                }
41169            }
41170            return null;
41171        }
41172    };
41173 }();/**\r
41174  * @class Ext.SplitButton\r
41175  * @extends Ext.Button\r
41176  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default\r
41177  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional\r
41178  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:\r
41179  * <pre><code>\r
41180 // display a dropdown menu:\r
41181 new Ext.SplitButton({\r
41182         renderTo: 'button-ct', // the container id\r
41183         text: 'Options',\r
41184         handler: optionsHandler, // handle a click on the button itself\r
41185         menu: new Ext.menu.Menu({\r
41186         items: [\r
41187                 // these items will render as dropdown menu items when the arrow is clicked:\r
41188                 {text: 'Item 1', handler: item1Handler},\r
41189                 {text: 'Item 2', handler: item2Handler}\r
41190         ]\r
41191         })\r
41192 });\r
41193 \r
41194 // Instead of showing a menu, you provide any type of custom\r
41195 // functionality you want when the dropdown arrow is clicked:\r
41196 new Ext.SplitButton({\r
41197         renderTo: 'button-ct',\r
41198         text: 'Options',\r
41199         handler: optionsHandler,\r
41200         arrowHandler: myCustomHandler\r
41201 });\r
41202 </code></pre>\r
41203  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)\r
41204  * @cfg {String} arrowTooltip The title attribute of the arrow\r
41205  * @constructor\r
41206  * Create a new menu button\r
41207  * @param {Object} config The config object\r
41208  * @xtype splitbutton\r
41209  */\r
41210 Ext.SplitButton = Ext.extend(Ext.Button, {\r
41211         // private\r
41212     arrowSelector : 'em',\r
41213     split: true,\r
41214 \r
41215     // private\r
41216     initComponent : function(){\r
41217         Ext.SplitButton.superclass.initComponent.call(this);\r
41218         /**\r
41219          * @event arrowclick\r
41220          * Fires when this button's arrow is clicked\r
41221          * @param {MenuButton} this\r
41222          * @param {EventObject} e The click event\r
41223          */\r
41224         this.addEvents("arrowclick");\r
41225     },\r
41226 \r
41227     // private\r
41228     onRender : function(){\r
41229         Ext.SplitButton.superclass.onRender.apply(this, arguments);\r
41230         if(this.arrowTooltip){\r
41231             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;\r
41232         }\r
41233     },\r
41234 \r
41235     /**\r
41236      * Sets this button's arrow click handler.\r
41237      * @param {Function} handler The function to call when the arrow is clicked\r
41238      * @param {Object} scope (optional) Scope for the function passed above\r
41239      */\r
41240     setArrowHandler : function(handler, scope){\r
41241         this.arrowHandler = handler;\r
41242         this.scope = scope;\r
41243     },\r
41244 \r
41245     getMenuClass : function(){\r
41246         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');\r
41247     },\r
41248 \r
41249     isClickOnArrow : function(e){\r
41250         return this.arrowAlign != 'bottom' ?\r
41251                e.getPageX() > this.el.child(this.buttonSelector).getRegion().right :\r
41252                e.getPageY() > this.el.child(this.buttonSelector).getRegion().bottom;\r
41253     },\r
41254 \r
41255     // private\r
41256     onClick : function(e, t){\r
41257         e.preventDefault();\r
41258         if(!this.disabled){\r
41259             if(this.isClickOnArrow(e)){\r
41260                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){\r
41261                     this.showMenu();\r
41262                 }\r
41263                 this.fireEvent("arrowclick", this, e);\r
41264                 if(this.arrowHandler){\r
41265                     this.arrowHandler.call(this.scope || this, this, e);\r
41266                 }\r
41267             }else{\r
41268                 if(this.enableToggle){\r
41269                     this.toggle();\r
41270                 }\r
41271                 this.fireEvent("click", this, e);\r
41272                 if(this.handler){\r
41273                     this.handler.call(this.scope || this, this, e);\r
41274                 }\r
41275             }\r
41276         }\r
41277     },\r
41278 \r
41279     // private\r
41280     isMenuTriggerOver : function(e){\r
41281         return this.menu && e.target.tagName == 'em';\r
41282     },\r
41283 \r
41284     // private\r
41285     isMenuTriggerOut : function(e, internal){\r
41286         return this.menu && e.target.tagName != 'em';\r
41287     }\r
41288 });\r
41289 \r
41290 Ext.reg('splitbutton', Ext.SplitButton);/**\r
41291  * @class Ext.CycleButton\r
41292  * @extends Ext.SplitButton\r
41293  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically\r
41294  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's\r
41295  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the\r
41296  * button displays the dropdown menu just like a normal SplitButton.  Example usage:\r
41297  * <pre><code>\r
41298 var btn = new Ext.CycleButton({\r
41299     showText: true,\r
41300     prependText: 'View as ',\r
41301     items: [{\r
41302         text:'text only',\r
41303         iconCls:'view-text',\r
41304         checked:true\r
41305     },{\r
41306         text:'HTML',\r
41307         iconCls:'view-html'\r
41308     }],\r
41309     changeHandler:function(btn, item){\r
41310         Ext.Msg.alert('Change View', item.text);\r
41311     }\r
41312 });\r
41313 </code></pre>\r
41314  * @constructor\r
41315  * Create a new split button\r
41316  * @param {Object} config The config object\r
41317  * @xtype cycle\r
41318  */\r
41319 Ext.CycleButton = Ext.extend(Ext.SplitButton, {\r
41320     /**\r
41321      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the\r
41322      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})\r
41323      */\r
41324     /**\r
41325      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)\r
41326      */\r
41327     /**\r
41328      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the\r
41329      * button's text (only applies when showText = true, defaults to '')\r
41330      */\r
41331     /**\r
41332      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu\r
41333      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead\r
41334      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the\r
41335      * following argument list: (SplitButton this, Ext.menu.CheckItem item)\r
41336      */\r
41337     /**\r
41338      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This\r
41339      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the \r
41340      * default behavior of changing the button's icon to match the selected item's icon on change.\r
41341      */\r
41342     /**\r
41343      * @property menu\r
41344      * @type Menu\r
41345      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.\r
41346      */\r
41347 \r
41348     // private\r
41349     getItemText : function(item){\r
41350         if(item && this.showText === true){\r
41351             var text = '';\r
41352             if(this.prependText){\r
41353                 text += this.prependText;\r
41354             }\r
41355             text += item.text;\r
41356             return text;\r
41357         }\r
41358         return undefined;\r
41359     },\r
41360 \r
41361     /**\r
41362      * Sets the button's active menu item.\r
41363      * @param {Ext.menu.CheckItem} item The item to activate\r
41364      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)\r
41365      */\r
41366     setActiveItem : function(item, suppressEvent){\r
41367         if(typeof item != 'object'){\r
41368             item = this.menu.items.get(item);\r
41369         }\r
41370         if(item){\r
41371             if(!this.rendered){\r
41372                 this.text = this.getItemText(item);\r
41373                 this.iconCls = item.iconCls;\r
41374             }else{\r
41375                 var t = this.getItemText(item);\r
41376                 if(t){\r
41377                     this.setText(t);\r
41378                 }\r
41379                 this.setIconClass(item.iconCls);\r
41380             }\r
41381             this.activeItem = item;\r
41382             if(!item.checked){\r
41383                 item.setChecked(true, true);\r
41384             }\r
41385             if(this.forceIcon){\r
41386                 this.setIconClass(this.forceIcon);\r
41387             }\r
41388             if(!suppressEvent){\r
41389                 this.fireEvent('change', this, item);\r
41390             }\r
41391         }\r
41392     },\r
41393 \r
41394     /**\r
41395      * Gets the currently active menu item.\r
41396      * @return {Ext.menu.CheckItem} The active item\r
41397      */\r
41398     getActiveItem : function(){\r
41399         return this.activeItem;\r
41400     },\r
41401 \r
41402     // private\r
41403     initComponent : function(){\r
41404         this.addEvents(\r
41405             /**\r
41406              * @event change\r
41407              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function\r
41408              * is set on this CycleButton, it will be called instead on active item change and this change event will\r
41409              * not be fired.\r
41410              * @param {Ext.CycleButton} this\r
41411              * @param {Ext.menu.CheckItem} item The menu item that was selected\r
41412              */\r
41413             "change"\r
41414         );\r
41415 \r
41416         if(this.changeHandler){\r
41417             this.on('change', this.changeHandler, this.scope||this);\r
41418             delete this.changeHandler;\r
41419         }\r
41420 \r
41421         this.itemCount = this.items.length;\r
41422 \r
41423         this.menu = {cls:'x-cycle-menu', items:[]};\r
41424         var checked;\r
41425         for(var i = 0, len = this.itemCount; i < len; i++){\r
41426             var item = this.items[i];\r
41427             item.group = item.group || this.id;\r
41428             item.itemIndex = i;\r
41429             item.checkHandler = this.checkHandler;\r
41430             item.scope = this;\r
41431             item.checked = item.checked || false;\r
41432             this.menu.items.push(item);\r
41433             if(item.checked){\r
41434                 checked = item;\r
41435             }\r
41436         }\r
41437         this.setActiveItem(checked, true);\r
41438         Ext.CycleButton.superclass.initComponent.call(this);\r
41439 \r
41440         this.on('click', this.toggleSelected, this);\r
41441     },\r
41442 \r
41443     // private\r
41444     checkHandler : function(item, pressed){\r
41445         if(pressed){\r
41446             this.setActiveItem(item);\r
41447         }\r
41448     },\r
41449 \r
41450     /**\r
41451      * This is normally called internally on button click, but can be called externally to advance the button's\r
41452      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu\r
41453      * the active item will be set to the first item in the menu.\r
41454      */\r
41455     toggleSelected : function(){\r
41456         this.menu.render();\r
41457         \r
41458         var nextIdx, checkItem;\r
41459         for (var i = 1; i < this.itemCount; i++) {\r
41460             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;\r
41461             // check the potential item\r
41462             checkItem = this.menu.items.itemAt(nextIdx);\r
41463             // if its not disabled then check it.\r
41464             if (!checkItem.disabled) {\r
41465                 checkItem.setChecked(true);\r
41466                 break;\r
41467             }\r
41468         }\r
41469     }\r
41470 });\r
41471 Ext.reg('cycle', Ext.CycleButton);/**\r
41472  * @class Ext.layout.ToolbarLayout\r
41473  * @extends Ext.layout.ContainerLayout\r
41474  * Layout manager implicitly used by Ext.Toolbar.\r
41475  */\r
41476 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
41477     monitorResize : true,\r
41478     triggerWidth : 18,\r
41479     lastOverflow : false,\r
41480 \r
41481     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',\r
41482     // private\r
41483     onLayout : function(ct, target){\r
41484         if(!this.leftTr){\r
41485             target.addClass('x-toolbar-layout-ct');\r
41486             target.insertHtml('beforeEnd',\r
41487                  '<table cellspacing="0" class="x-toolbar-ct"><tbody><tr><td class="x-toolbar-left" align="left"><table cellspacing="0"><tbody><tr class="x-toolbar-left-row"></tr></tbody></table></td><td class="x-toolbar-right" align="right"><table cellspacing="0" class="x-toolbar-right-ct"><tbody><tr><td><table cellspacing="0"><tbody><tr class="x-toolbar-right-row"></tr></tbody></table></td><td><table cellspacing="0"><tbody><tr class="x-toolbar-extras-row"></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table>');\r
41488             this.leftTr = target.child('tr.x-toolbar-left-row', true);\r
41489             this.rightTr = target.child('tr.x-toolbar-right-row', true);\r
41490             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);\r
41491         }\r
41492         var side = this.leftTr;\r
41493         var pos = 0;\r
41494 \r
41495         var items = ct.items.items;\r
41496         for(var i = 0, len = items.length, c; i < len; i++, pos++) {\r
41497             c = items[i];\r
41498             if(c.isFill){\r
41499                 side = this.rightTr;\r
41500                 pos = -1;\r
41501             }else if(!c.rendered){\r
41502                 c.render(this.insertCell(c, side, pos));\r
41503             }else{\r
41504                 if(!c.xtbHidden && !this.isValidParent(c, side.childNodes[pos])){\r
41505                     var td = this.insertCell(c, side, pos);\r
41506                     td.appendChild(c.getDomPositionEl().dom);\r
41507                     c.container = Ext.get(td);\r
41508                 }\r
41509             }\r
41510         }\r
41511         //strip extra empty cells\r
41512         this.cleanup(this.leftTr);\r
41513         this.cleanup(this.rightTr);\r
41514         this.cleanup(this.extrasTr);\r
41515         this.fitToSize(target);\r
41516     },\r
41517 \r
41518     cleanup : function(row){\r
41519         var cn = row.childNodes;\r
41520         for(var i = cn.length-1, c; i >= 0 && (c = cn[i]); i--){\r
41521             if(!c.firstChild){\r
41522                 row.removeChild(c);\r
41523             }\r
41524         }\r
41525     },\r
41526 \r
41527     insertCell : function(c, side, pos){\r
41528         var td = document.createElement('td');\r
41529         td.className='x-toolbar-cell';\r
41530         side.insertBefore(td, side.childNodes[pos]||null);\r
41531         return td;\r
41532     },\r
41533 \r
41534     hideItem : function(item){\r
41535         var h = (this.hiddens = this.hiddens || []);\r
41536         h.push(item);\r
41537         item.xtbHidden = true;\r
41538         item.xtbWidth = item.getDomPositionEl().dom.parentNode.offsetWidth;\r
41539         item.hide();\r
41540     },\r
41541 \r
41542     unhideItem : function(item){\r
41543         item.show();\r
41544         item.xtbHidden = false;\r
41545         this.hiddens.remove(item);\r
41546         if(this.hiddens.length < 1){\r
41547             delete this.hiddens;\r
41548         }\r
41549     },\r
41550 \r
41551     getItemWidth : function(c){\r
41552         return c.hidden ? (c.xtbWidth || 0) : c.getDomPositionEl().dom.parentNode.offsetWidth;\r
41553     },\r
41554 \r
41555     fitToSize : function(t){\r
41556         if(this.container.enableOverflow === false){\r
41557             return;\r
41558         }\r
41559         var w = t.dom.clientWidth;\r
41560         var lw = this.lastWidth || 0;\r
41561         this.lastWidth = w;\r
41562         var iw = t.dom.firstChild.offsetWidth;\r
41563 \r
41564         var clipWidth = w - this.triggerWidth;\r
41565         var hideIndex = -1;\r
41566 \r
41567         if(iw > w || (this.hiddens && w >= lw)){\r
41568             var i, items = this.container.items.items, len = items.length, c;\r
41569             var loopWidth = 0;\r
41570             for(i = 0; i < len; i++) {\r
41571                 c = items[i];\r
41572                 if(!c.isFill){\r
41573                     loopWidth += this.getItemWidth(c);\r
41574                     if(loopWidth > clipWidth){\r
41575                         if(!c.xtbHidden){\r
41576                             this.hideItem(c);\r
41577                         }\r
41578                     }else{\r
41579                         if(c.xtbHidden){\r
41580                             this.unhideItem(c);\r
41581                         }\r
41582                     }\r
41583                 }\r
41584             }\r
41585         }\r
41586         if(this.hiddens){\r
41587             this.initMore();\r
41588             if(!this.lastOverflow){\r
41589                 this.container.fireEvent('overflowchange', this.container, true);\r
41590                 this.lastOverflow = true;\r
41591             }\r
41592         }else if(this.more){\r
41593             this.clearMenu();\r
41594             this.more.destroy();\r
41595             delete this.more;\r
41596             if(this.lastOverflow){\r
41597                 this.container.fireEvent('overflowchange', this.container, false);\r
41598                 this.lastOverflow = false;\r
41599             }\r
41600         }\r
41601     },\r
41602 \r
41603     createMenuConfig : function(c, hideOnClick){\r
41604         var cfg = Ext.apply({}, c.initialConfig),\r
41605             group = c.toggleGroup;\r
41606 \r
41607         Ext.apply(cfg, {\r
41608             text: c.overflowText || c.text,\r
41609             iconCls: c.iconCls,\r
41610             icon: c.icon,\r
41611             itemId: c.itemId,\r
41612             disabled: c.disabled,\r
41613             handler: c.handler,\r
41614             scope: c.scope,\r
41615             menu: c.menu,\r
41616             hideOnClick: hideOnClick\r
41617         });\r
41618         if(group || c.enableToggle){\r
41619             Ext.apply(cfg, {\r
41620                 group: group,\r
41621                 checked: c.pressed,\r
41622                 listeners: {\r
41623                     checkchange: function(item, checked){\r
41624                         c.toggle(checked);\r
41625                     }\r
41626                 }\r
41627             });\r
41628         }\r
41629         delete cfg.xtype;\r
41630         delete cfg.id;\r
41631         return cfg;\r
41632     },\r
41633 \r
41634     // private\r
41635     addComponentToMenu : function(m, c){\r
41636         if(c instanceof Ext.Toolbar.Separator){\r
41637             m.add('-');\r
41638         }else if(Ext.isFunction(c.isXType)){\r
41639             if(c.isXType('splitbutton')){\r
41640                 m.add(this.createMenuConfig(c, true));\r
41641             }else if(c.isXType('button')){\r
41642                 m.add(this.createMenuConfig(c, !c.menu));\r
41643             }else if(c.isXType('buttongroup')){\r
41644                 c.items.each(function(item){\r
41645                      this.addComponentToMenu(m, item);\r
41646                 }, this);\r
41647             }\r
41648         }\r
41649     },\r
41650 \r
41651     clearMenu : function(){\r
41652         var m = this.moreMenu;\r
41653         if(m && m.items){\r
41654             this.moreMenu.items.each(function(item){\r
41655                 delete item.menu;\r
41656             });\r
41657         }\r
41658     },\r
41659 \r
41660     // private\r
41661     beforeMoreShow : function(m){\r
41662         var h = this.container.items.items,\r
41663             len = h.length,\r
41664             c,\r
41665             prev,\r
41666             needsSep = function(group, item){\r
41667                 return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);\r
41668             };\r
41669 \r
41670         this.clearMenu();\r
41671         m.removeAll();\r
41672         for(var i = 0; i < len; i++){\r
41673             c = h[i];\r
41674             if(c.xtbHidden){\r
41675                 if(prev && (needsSep(c, prev) || needsSep(prev, c))){\r
41676                     m.add('-');\r
41677                 }\r
41678                 this.addComponentToMenu(m, c);\r
41679                 prev = c;\r
41680             }\r
41681         }\r
41682         // put something so the menu isn't empty\r
41683         // if no compatible items found\r
41684         if(m.items.length < 1){\r
41685             m.add(this.noItemsMenuText);\r
41686         }\r
41687     },\r
41688 \r
41689     initMore : function(){\r
41690         if(!this.more){\r
41691             this.moreMenu = new Ext.menu.Menu({\r
41692                 listeners: {\r
41693                     beforeshow: this.beforeMoreShow,\r
41694                     scope: this\r
41695                 }\r
41696             });\r
41697             this.more = new Ext.Button({\r
41698                 iconCls: 'x-toolbar-more-icon',\r
41699                 cls: 'x-toolbar-more',\r
41700                 menu: this.moreMenu\r
41701             });\r
41702             var td = this.insertCell(this.more, this.extrasTr, 100);\r
41703             this.more.render(td);\r
41704         }\r
41705     },\r
41706 \r
41707     destroy : function(){\r
41708         Ext.destroy(this.more, this.moreMenu);\r
41709         Ext.layout.ToolbarLayout.superclass.destroy.call(this);\r
41710     }\r
41711     /**\r
41712      * @property activeItem\r
41713      * @hide\r
41714      */\r
41715 });\r
41716 \r
41717 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;\r
41718 \r
41719 /**\r
41720  * @class Ext.Toolbar\r
41721  * @extends Ext.Container\r
41722  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar\r
41723  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may\r
41724  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,\r
41725  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>\r
41726  * <p>Some items have shortcut strings for creation:</p>\r
41727  * <pre>\r
41728 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>\r
41729 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container\r
41730 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items\r
41731 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements\r
41732  * </pre>\r
41733  *\r
41734  * Example usage of various elements:\r
41735  * <pre><code>\r
41736 var tb = new Ext.Toolbar({\r
41737     renderTo: document.body,\r
41738     width: 600,\r
41739     height: 100,\r
41740     items: [\r
41741         {\r
41742             // xtype: 'button', // default for Toolbars, same as 'tbbutton'\r
41743             text: 'Button'\r
41744         },\r
41745         {\r
41746             xtype: 'splitbutton', // same as 'tbsplitbutton'\r
41747             text: 'Split Button'\r
41748         },\r
41749         // begin using the right-justified button container\r
41750         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill\r
41751         {\r
41752             xtype: 'textfield',\r
41753             name: 'field1',\r
41754             emptyText: 'enter search term'\r
41755         },\r
41756         // add a vertical separator bar between toolbar items\r
41757         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator\r
41758         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem\r
41759         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer\r
41760         'text 2',\r
41761         {xtype: 'tbspacer', width: 50}, // add a 50px space\r
41762         'text 3'\r
41763     ]\r
41764 });\r
41765  * </code></pre>\r
41766  * Example adding a ComboBox within a menu of a button:\r
41767  * <pre><code>\r
41768 // ComboBox creation\r
41769 var combo = new Ext.form.ComboBox({\r
41770     store: new Ext.data.ArrayStore({\r
41771         autoDestroy: true,\r
41772         fields: ['initials', 'fullname'],\r
41773         data : [\r
41774             ['FF', 'Fred Flintstone'],\r
41775             ['BR', 'Barney Rubble']\r
41776         ]\r
41777     }),\r
41778     displayField: 'fullname',\r
41779     typeAhead: true,\r
41780     mode: 'local',\r
41781     forceSelection: true,\r
41782     triggerAction: 'all',\r
41783     emptyText: 'Select a name...',\r
41784     selectOnFocus: true,\r
41785     width: 135,\r
41786     getListParent: function() {\r
41787         return this.el.up('.x-menu');\r
41788     },\r
41789     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu\r
41790 });\r
41791 \r
41792 // put ComboBox in a Menu\r
41793 var menu = new Ext.menu.Menu({\r
41794     id: 'mainMenu',\r
41795     items: [\r
41796         combo // A Field in a Menu\r
41797     ]\r
41798 });\r
41799 \r
41800 // add a Button with the menu\r
41801 tb.add({\r
41802         text:'Button w/ Menu',\r
41803         menu: menu  // assign menu by instance\r
41804     });\r
41805 tb.doLayout();\r
41806  * </code></pre>\r
41807  * @constructor\r
41808  * Creates a new Toolbar\r
41809  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>\r
41810  * @xtype toolbar\r
41811  */\r
41812 Ext.Toolbar = function(config){\r
41813     if(Ext.isArray(config)){\r
41814         config = {items: config, layout: 'toolbar'};\r
41815     } else {\r
41816         config = Ext.apply({\r
41817             layout: 'toolbar'\r
41818         }, config);\r
41819         if(config.buttons) {\r
41820             config.items = config.buttons;\r
41821         }\r
41822     }\r
41823     Ext.Toolbar.superclass.constructor.call(this, config);\r
41824 };\r
41825 \r
41826 (function(){\r
41827 \r
41828 var T = Ext.Toolbar;\r
41829 \r
41830 Ext.extend(T, Ext.Container, {\r
41831 \r
41832     defaultType: 'button',\r
41833 \r
41834     trackMenus : true,\r
41835     internalDefaults: {removeMode: 'container', hideParent: true},\r
41836     toolbarCls: 'x-toolbar',\r
41837 \r
41838     initComponent : function(){\r
41839         T.superclass.initComponent.call(this);\r
41840 \r
41841         /**\r
41842          * @event overflowchange\r
41843          * Fires after the overflow state has changed.\r
41844          * @param {Object} c The Container\r
41845          * @param {Boolean} lastOverflow overflow state\r
41846          */\r
41847         this.addEvents('overflowchange');\r
41848     },\r
41849 \r
41850     // private\r
41851     onRender : function(ct, position){\r
41852         if(!this.el){\r
41853             if(!this.autoCreate){\r
41854                 this.autoCreate = {\r
41855                     cls: this.toolbarCls + ' x-small-editor'\r
41856                 };\r
41857             }\r
41858             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);\r
41859         }\r
41860     },\r
41861 \r
41862     /**\r
41863      * Adds element(s) to the toolbar -- this function takes a variable number of\r
41864      * arguments of mixed type and adds them to the toolbar.\r
41865      * @param {Mixed} arg1 The following types of arguments are all valid:<br />\r
41866      * <ul>\r
41867      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>\r
41868      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>\r
41869      * <li>Field: Any form field (equivalent to {@link #addField})</li>\r
41870      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>\r
41871      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).\r
41872      * Note that there are a few special strings that are treated differently as explained next.</li>\r
41873      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>\r
41874      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>\r
41875      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>\r
41876      * </ul>\r
41877      * @param {Mixed} arg2\r
41878      * @param {Mixed} etc.\r
41879      * @method add\r
41880      */\r
41881 \r
41882     // private\r
41883     lookupComponent : function(c){\r
41884         if(Ext.isString(c)){\r
41885             if(c == '-'){\r
41886                 c = new T.Separator();\r
41887             }else if(c == ' '){\r
41888                 c = new T.Spacer();\r
41889             }else if(c == '->'){\r
41890                 c = new T.Fill();\r
41891             }else{\r
41892                 c = new T.TextItem(c);\r
41893             }\r
41894             this.applyDefaults(c);\r
41895         }else{\r
41896             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item\r
41897                 c = this.constructItem(c);\r
41898             }else if(c.tag){ // DomHelper spec\r
41899                 c = new T.Item({autoEl: c});\r
41900             }else if(c.tagName){ // element\r
41901                 c = new T.Item({el:c});\r
41902             }else if(Ext.isObject(c)){ // must be button config?\r
41903                 c = c.xtype ? this.constructItem(c) : this.constructButton(c);\r
41904             }\r
41905         }\r
41906         return c;\r
41907     },\r
41908 \r
41909     // private\r
41910     applyDefaults : function(c){\r
41911         if(!Ext.isString(c)){\r
41912             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);\r
41913             var d = this.internalDefaults;\r
41914             if(c.events){\r
41915                 Ext.applyIf(c.initialConfig, d);\r
41916                 Ext.apply(c, d);\r
41917             }else{\r
41918                 Ext.applyIf(c, d);\r
41919             }\r
41920         }\r
41921         return c;\r
41922     },\r
41923 \r
41924     // private\r
41925     constructItem : function(item, type){\r
41926         return Ext.create(item, type || this.defaultType);\r
41927     },\r
41928 \r
41929     /**\r
41930      * Adds a separator\r
41931      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}\r
41932      */\r
41933     addSeparator : function(){\r
41934         return this.add(new T.Separator());\r
41935     },\r
41936 \r
41937     /**\r
41938      * Adds a spacer element\r
41939      * @return {Ext.Toolbar.Spacer} The spacer item\r
41940      */\r
41941     addSpacer : function(){\r
41942         return this.add(new T.Spacer());\r
41943     },\r
41944 \r
41945     /**\r
41946      * Forces subsequent additions into the float:right toolbar\r
41947      */\r
41948     addFill : function(){\r
41949         this.add(new T.Fill());\r
41950     },\r
41951 \r
41952     /**\r
41953      * Adds any standard HTML element to the toolbar\r
41954      * @param {Mixed} el The element or id of the element to add\r
41955      * @return {Ext.Toolbar.Item} The element's item\r
41956      */\r
41957     addElement : function(el){\r
41958         return this.addItem(new T.Item({el:el}));\r
41959     },\r
41960 \r
41961     /**\r
41962      * Adds any Toolbar.Item or subclass\r
41963      * @param {Ext.Toolbar.Item} item\r
41964      * @return {Ext.Toolbar.Item} The item\r
41965      */\r
41966     addItem : function(item){\r
41967         return Ext.Toolbar.superclass.add.apply(this, arguments);\r
41968     },\r
41969 \r
41970     /**\r
41971      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.\r
41972      * @param {Object/Array} config A button config or array of configs\r
41973      * @return {Ext.Button/Array}\r
41974      */\r
41975     addButton : function(config){\r
41976         if(Ext.isArray(config)){\r
41977             var buttons = [];\r
41978             for(var i = 0, len = config.length; i < len; i++) {\r
41979                 buttons.push(this.addButton(config[i]));\r
41980             }\r
41981             return buttons;\r
41982         }\r
41983         return this.add(this.constructButton(config));\r
41984     },\r
41985 \r
41986     /**\r
41987      * Adds text to the toolbar\r
41988      * @param {String} text The text to add\r
41989      * @return {Ext.Toolbar.Item} The element's item\r
41990      */\r
41991     addText : function(text){\r
41992         return this.addItem(new T.TextItem(text));\r
41993     },\r
41994 \r
41995     /**\r
41996      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config\r
41997      * @param {Object} config\r
41998      * @return {Ext.Toolbar.Item} The element's item\r
41999      */\r
42000     addDom : function(config){\r
42001         return this.add(new T.Item({autoEl: config}));\r
42002     },\r
42003 \r
42004     /**\r
42005      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have\r
42006      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.\r
42007      * @param {Ext.form.Field} field\r
42008      * @return {Ext.Toolbar.Item}\r
42009      */\r
42010     addField : function(field){\r
42011         return this.add(field);\r
42012     },\r
42013 \r
42014     /**\r
42015      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.\r
42016      * @param {Number} index The index where the item is to be inserted\r
42017      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be\r
42018      * inserted, or an array of buttons/configs.\r
42019      * @return {Ext.Button/Item}\r
42020      */\r
42021     insertButton : function(index, item){\r
42022         if(Ext.isArray(item)){\r
42023             var buttons = [];\r
42024             for(var i = 0, len = item.length; i < len; i++) {\r
42025                buttons.push(this.insertButton(index + i, item[i]));\r
42026             }\r
42027             return buttons;\r
42028         }\r
42029         return Ext.Toolbar.superclass.insert.call(this, index, item);\r
42030     },\r
42031 \r
42032     // private\r
42033     initMenuTracking : function(item){\r
42034         if(this.trackMenus && item.menu){\r
42035             this.mon(item, {\r
42036                 'menutriggerover' : this.onButtonTriggerOver,\r
42037                 'menushow' : this.onButtonMenuShow,\r
42038                 'menuhide' : this.onButtonMenuHide,\r
42039                 scope: this\r
42040             });\r
42041         }\r
42042     },\r
42043 \r
42044     // private\r
42045     constructButton : function(item){\r
42046         var b = item.events ? item : this.constructItem(item, item.split ? 'splitbutton' : this.defaultType);\r
42047         this.initMenuTracking(b);\r
42048         return b;\r
42049     },\r
42050 \r
42051     // private\r
42052     onDisable : function(){\r
42053         this.items.each(function(item){\r
42054              if(item.disable){\r
42055                  item.disable();\r
42056              }\r
42057         });\r
42058     },\r
42059 \r
42060     // private\r
42061     onEnable : function(){\r
42062         this.items.each(function(item){\r
42063              if(item.enable){\r
42064                  item.enable();\r
42065              }\r
42066         });\r
42067     },\r
42068 \r
42069     // private\r
42070     onButtonTriggerOver : function(btn){\r
42071         if(this.activeMenuBtn && this.activeMenuBtn != btn){\r
42072             this.activeMenuBtn.hideMenu();\r
42073             btn.showMenu();\r
42074             this.activeMenuBtn = btn;\r
42075         }\r
42076     },\r
42077 \r
42078     // private\r
42079     onButtonMenuShow : function(btn){\r
42080         this.activeMenuBtn = btn;\r
42081     },\r
42082 \r
42083     // private\r
42084     onButtonMenuHide : function(btn){\r
42085         delete this.activeMenuBtn;\r
42086     }\r
42087 });\r
42088 Ext.reg('toolbar', Ext.Toolbar);\r
42089 \r
42090 /**\r
42091  * @class Ext.Toolbar.Item\r
42092  * @extends Ext.BoxComponent\r
42093  * The base class that other non-interacting Toolbar Item classes should extend in order to\r
42094  * get some basic common toolbar item functionality.\r
42095  * @constructor\r
42096  * Creates a new Item\r
42097  * @param {HTMLElement} el\r
42098  * @xtype tbitem\r
42099  */\r
42100 T.Item = Ext.extend(Ext.BoxComponent, {\r
42101     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD\r
42102     enable:Ext.emptyFn,\r
42103     disable:Ext.emptyFn,\r
42104     focus:Ext.emptyFn\r
42105     /**\r
42106      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.\r
42107      */\r
42108 });\r
42109 Ext.reg('tbitem', T.Item);\r
42110 \r
42111 /**\r
42112  * @class Ext.Toolbar.Separator\r
42113  * @extends Ext.Toolbar.Item\r
42114  * A simple class that adds a vertical separator bar between toolbar items\r
42115  * (css class:<tt>'xtb-sep'</tt>). Example usage:\r
42116  * <pre><code>\r
42117 new Ext.Panel({\r
42118     tbar : [\r
42119         'Item 1',\r
42120         {xtype: 'tbseparator'}, // or '-'\r
42121         'Item 2'\r
42122     ]\r
42123 });\r
42124 </code></pre>\r
42125  * @constructor\r
42126  * Creates a new Separator\r
42127  * @xtype tbseparator\r
42128  */\r
42129 T.Separator = Ext.extend(T.Item, {\r
42130     onRender : function(ct, position){\r
42131         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);\r
42132     }\r
42133 });\r
42134 Ext.reg('tbseparator', T.Separator);\r
42135 \r
42136 /**\r
42137  * @class Ext.Toolbar.Spacer\r
42138  * @extends Ext.Toolbar.Item\r
42139  * A simple element that adds extra horizontal space between items in a toolbar.\r
42140  * By default a 2px wide space is added via css specification:<pre><code>\r
42141 .x-toolbar .xtb-spacer {\r
42142     width:2px;\r
42143 }\r
42144  * </code></pre>\r
42145  * <p>Example usage:</p>\r
42146  * <pre><code>\r
42147 new Ext.Panel({\r
42148     tbar : [\r
42149         'Item 1',\r
42150         {xtype: 'tbspacer'}, // or ' '\r
42151         'Item 2',\r
42152         // space width is also configurable via javascript\r
42153         {xtype: 'tbspacer', width: 50}, // add a 50px space\r
42154         'Item 3'\r
42155     ]\r
42156 });\r
42157 </code></pre>\r
42158  * @constructor\r
42159  * Creates a new Spacer\r
42160  * @xtype tbspacer\r
42161  */\r
42162 T.Spacer = Ext.extend(T.Item, {\r
42163     /**\r
42164      * @cfg {Number} width\r
42165      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).\r
42166      */\r
42167 \r
42168     onRender : function(ct, position){\r
42169         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);\r
42170     }\r
42171 });\r
42172 Ext.reg('tbspacer', T.Spacer);\r
42173 \r
42174 /**\r
42175  * @class Ext.Toolbar.Fill\r
42176  * @extends Ext.Toolbar.Spacer\r
42177  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using\r
42178  * the right-justified button container.\r
42179  * <pre><code>\r
42180 new Ext.Panel({\r
42181     tbar : [\r
42182         'Item 1',\r
42183         {xtype: 'tbfill'}, // or '->'\r
42184         'Item 2'\r
42185     ]\r
42186 });\r
42187 </code></pre>\r
42188  * @constructor\r
42189  * Creates a new Fill\r
42190  * @xtype tbfill\r
42191  */\r
42192 T.Fill = Ext.extend(T.Item, {\r
42193     // private\r
42194     render : Ext.emptyFn,\r
42195     isFill : true\r
42196 });\r
42197 Ext.reg('tbfill', T.Fill);\r
42198 \r
42199 /**\r
42200  * @class Ext.Toolbar.TextItem\r
42201  * @extends Ext.Toolbar.Item\r
42202  * A simple class that renders text directly into a toolbar\r
42203  * (with css class:<tt>'xtb-text'</tt>). Example usage:\r
42204  * <pre><code>\r
42205 new Ext.Panel({\r
42206     tbar : [\r
42207         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'\r
42208     ]\r
42209 });\r
42210 </code></pre>\r
42211  * @constructor\r
42212  * Creates a new TextItem\r
42213  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property\r
42214  * @xtype tbtext\r
42215  */\r
42216 T.TextItem = Ext.extend(T.Item, {\r
42217     constructor: function(config){\r
42218         if (Ext.isString(config)) {\r
42219             config = { autoEl: {cls: 'xtb-text', html: config }};\r
42220         } else {\r
42221             config.autoEl = {cls: 'xtb-text', html: config.text || ''};\r
42222         }\r
42223         T.TextItem.superclass.constructor.call(this, config);\r
42224     },\r
42225     /**\r
42226      * Updates this item's text, setting the text to be used as innerHTML.\r
42227      * @param {String} t The text to display (html accepted).\r
42228      */\r
42229     setText : function(t) {\r
42230         if (this.rendered) {\r
42231             this.el.dom.innerHTML = t;\r
42232         } else {\r
42233             this.autoEl.html = t;\r
42234         }\r
42235     }\r
42236 });\r
42237 Ext.reg('tbtext', T.TextItem);\r
42238 \r
42239 // backwards compat\r
42240 T.Button = Ext.extend(Ext.Button, {});\r
42241 T.SplitButton = Ext.extend(Ext.SplitButton, {});\r
42242 Ext.reg('tbbutton', T.Button);\r
42243 Ext.reg('tbsplit', T.SplitButton);\r
42244 \r
42245 })();\r
42246 /**\r
42247  * @class Ext.ButtonGroup\r
42248  * @extends Ext.Panel\r
42249  * Container for a group of buttons. Example usage:\r
42250  * <pre><code>\r
42251 var p = new Ext.Panel({\r
42252     title: 'Panel with Button Group',\r
42253     width: 300,\r
42254     height:200,\r
42255     renderTo: document.body,\r
42256     html: 'whatever',\r
42257     tbar: [{\r
42258         xtype: 'buttongroup',\r
42259         {@link #columns}: 3,\r
42260         title: 'Clipboard',\r
42261         items: [{\r
42262             text: 'Paste',\r
42263             scale: 'large',\r
42264             rowspan: 3, iconCls: 'add',\r
42265             iconAlign: 'top',\r
42266             cls: 'x-btn-as-arrow'\r
42267         },{\r
42268             xtype:'splitbutton',\r
42269             text: 'Menu Button',\r
42270             scale: 'large',\r
42271             rowspan: 3,\r
42272             iconCls: 'add',\r
42273             iconAlign: 'top',\r
42274             arrowAlign:'bottom',\r
42275             menu: [{text: 'Menu Item 1'}]\r
42276         },{\r
42277             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]\r
42278         },{\r
42279             text: 'Copy', iconCls: 'add16'\r
42280         },{\r
42281             text: 'Format', iconCls: 'add16'\r
42282         }]\r
42283     }]\r
42284 });\r
42285  * </code></pre>\r
42286  * @xtype buttongroup\r
42287  */\r
42288 Ext.ButtonGroup = Ext.extend(Ext.Panel, {\r
42289     /**\r
42290      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the\r
42291      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.\r
42292      */\r
42293     /**\r
42294      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.\r
42295      */\r
42296     baseCls: 'x-btn-group',\r
42297     /**\r
42298      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.\r
42299      */\r
42300     layout:'table',\r
42301     defaultType: 'button',\r
42302     /**\r
42303      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.\r
42304      */\r
42305     frame: true,\r
42306     internalDefaults: {removeMode: 'container', hideParent: true},\r
42307 \r
42308     initComponent : function(){\r
42309         this.layoutConfig = this.layoutConfig || {};\r
42310         Ext.applyIf(this.layoutConfig, {\r
42311             columns : this.columns\r
42312         });\r
42313         if(!this.title){\r
42314             this.addClass('x-btn-group-notitle');\r
42315         }\r
42316         this.on('afterlayout', this.onAfterLayout, this);\r
42317         Ext.ButtonGroup.superclass.initComponent.call(this);\r
42318     },\r
42319 \r
42320     applyDefaults : function(c){\r
42321         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);\r
42322         var d = this.internalDefaults;\r
42323         if(c.events){\r
42324             Ext.applyIf(c.initialConfig, d);\r
42325             Ext.apply(c, d);\r
42326         }else{\r
42327             Ext.applyIf(c, d);\r
42328         }\r
42329         return c;\r
42330     },\r
42331 \r
42332     onAfterLayout : function(){\r
42333         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;\r
42334         this.body.setWidth(bodyWidth);\r
42335         this.el.setWidth(bodyWidth + this.getFrameWidth());\r
42336     }\r
42337     /**\r
42338      * @cfg {Array} tools  @hide\r
42339      */\r
42340 });\r
42341 \r
42342 Ext.reg('buttongroup', Ext.ButtonGroup);\r
42343 /**
42344  * @class Ext.PagingToolbar
42345  * @extends Ext.Toolbar
42346  * <p>As the amount of records increases, the time required for the browser to render
42347  * them increases. Paging is used to reduce the amount of data exchanged with the client.
42348  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
42349  * scrollbars will be added.</p>
42350  * <p>Paging is typically handled on the server side (see exception below). The client sends
42351  * parameters to the server side, which the server needs to interpret and then respond with the
42352  * approprate data.</p>
42353  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
42354  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
42355  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
42356  * paging criteria.</p>
42357  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
42358  * <pre><code>
42359 Ext.QuickTips.init(); // to display button quicktips
42360
42361 var myStore = new Ext.data.Store({
42362     ...
42363 });
42364
42365 var myPageSize = 25;  // server script should only send back 25 items
42366
42367 var grid = new Ext.grid.GridPanel({
42368     ...
42369     store: myStore,
42370     bbar: new Ext.PagingToolbar({
42371         {@link #store}: myStore,       // grid and PagingToolbar using same store
42372         {@link #displayInfo}: true,
42373         {@link #pageSize}: myPageSize,
42374         {@link #prependButtons}: true,
42375         items: [
42376             'text 1'
42377         ]
42378     })
42379 });
42380  * </code></pre>
42381  *
42382  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
42383  * <pre><code>
42384 store.load({
42385     params: {
42386         start: 0,          // specify params for the first page load if using paging
42387         limit: myPageSize,
42388         foo:   'bar'
42389     }
42390 });
42391  * </code></pre>
42392  * <p><u>Paging with Local Data</u></p>
42393  * <p>Paging can also be accomplished with local data using extensions:</p>
42394  * <div class="mdetail-params"><ul>
42395  * <li><a href="http://extjs.com/forum/showthread.php?t=57386">Ext.ux.data.PagingStore</a></li>
42396  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
42397  * </ul></div>
42398  * @constructor
42399  * Create a new PagingToolbar
42400  * @param {Object} config The config object
42401  * @xtype paging
42402  */
42403 (function() {
42404
42405 var T = Ext.Toolbar;
42406
42407 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
42408     /**
42409      * @cfg {Ext.data.Store} store
42410      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
42411      */
42412     /**
42413      * @cfg {Boolean} displayInfo
42414      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
42415      */
42416     /**
42417      * @cfg {Number} pageSize
42418      * The number of records to display per page (defaults to <tt>20</tt>)
42419      */
42420     pageSize : 20,
42421     /**
42422      * @cfg {Boolean} prependButtons
42423      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
42424      * Defaults to <tt>false</tt>.
42425      */
42426     /**
42427      * @cfg {String} displayMsg
42428      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
42429      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
42430      * that are replaced by the values for start, end and total respectively. These tokens should
42431      * be preserved when overriding this string if showing those values is desired.
42432      */
42433     displayMsg : 'Displaying {0} - {1} of {2}',
42434     /**
42435      * @cfg {String} emptyMsg
42436      * The message to display when no records are found (defaults to 'No data to display')
42437      */
42438     emptyMsg : 'No data to display',
42439     /**
42440      * @cfg {String} beforePageText
42441      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
42442      */
42443     beforePageText : 'Page',
42444     /**
42445      * @cfg {String} afterPageText
42446      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
42447      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
42448      * total pages. This token should be preserved when overriding this string if showing the
42449      * total page count is desired.
42450      */
42451     afterPageText : 'of {0}',
42452     /**
42453      * @cfg {String} firstText
42454      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
42455      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
42456      */
42457     firstText : 'First Page',
42458     /**
42459      * @cfg {String} prevText
42460      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
42461      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
42462      */
42463     prevText : 'Previous Page',
42464     /**
42465      * @cfg {String} nextText
42466      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
42467      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
42468      */
42469     nextText : 'Next Page',
42470     /**
42471      * @cfg {String} lastText
42472      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
42473      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
42474      */
42475     lastText : 'Last Page',
42476     /**
42477      * @cfg {String} refreshText
42478      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
42479      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
42480      */
42481     refreshText : 'Refresh',
42482
42483     /**
42484      * @deprecated
42485      * <b>The defaults for these should be set in the data store.</b>
42486      * Object mapping of parameter names used for load calls, initially set to:
42487      * <pre>{start: 'start', limit: 'limit'}</pre>
42488      */
42489
42490     /**
42491      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
42492      * @type Number
42493      * @property pageSize
42494      */
42495
42496     /**
42497      * Indicator for the record position.  This property might be used to get the active page
42498      * number for example:<pre><code>
42499      * // t is reference to the paging toolbar instance
42500      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
42501      * </code></pre>
42502      * @type Number
42503      * @property cursor
42504      */
42505
42506     initComponent : function(){
42507         var pagingItems = [this.first = new T.Button({
42508             tooltip: this.firstText,
42509             overflowText: this.firstText,
42510             iconCls: 'x-tbar-page-first',
42511             disabled: true,
42512             handler: this.moveFirst,
42513             scope: this
42514         }), this.prev = new T.Button({
42515             tooltip: this.prevText,
42516             overflowText: this.prevText,
42517             iconCls: 'x-tbar-page-prev',
42518             disabled: true,
42519             handler: this.movePrevious,
42520             scope: this
42521         }), '-', this.beforePageText,
42522         this.inputItem = new Ext.form.NumberField({
42523             cls: 'x-tbar-page-number',
42524             allowDecimals: false,
42525             allowNegative: false,
42526             enableKeyEvents: true,
42527             selectOnFocus: true,
42528             listeners: {
42529                 scope: this,
42530                 keydown: this.onPagingKeyDown,
42531                 blur: this.onPagingBlur
42532             }
42533         }), this.afterTextItem = new T.TextItem({
42534             text: String.format(this.afterPageText, 1)
42535         }), '-', this.next = new T.Button({
42536             tooltip: this.nextText,
42537             overflowText: this.nextText,
42538             iconCls: 'x-tbar-page-next',
42539             disabled: true,
42540             handler: this.moveNext,
42541             scope: this
42542         }), this.last = new T.Button({
42543             tooltip: this.lastText,
42544             overflowText: this.lastText,
42545             iconCls: 'x-tbar-page-last',
42546             disabled: true,
42547             handler: this.moveLast,
42548             scope: this
42549         }), '-', this.refresh = new T.Button({
42550             tooltip: this.refreshText,
42551             overflowText: this.refreshText,
42552             iconCls: 'x-tbar-loading',
42553             handler: this.refresh,
42554             scope: this
42555         })];
42556
42557
42558         var userItems = this.items || this.buttons || [];
42559         if (this.prependButtons) {
42560             this.items = userItems.concat(pagingItems);
42561         }else{
42562             this.items = pagingItems.concat(userItems);
42563         }
42564         delete this.buttons;
42565         if(this.displayInfo){
42566             this.items.push('->');
42567             this.items.push(this.displayItem = new T.TextItem({}));
42568         }
42569         Ext.PagingToolbar.superclass.initComponent.call(this);
42570         this.addEvents(
42571             /**
42572              * @event change
42573              * Fires after the active page has been changed.
42574              * @param {Ext.PagingToolbar} this
42575              * @param {Object} pageData An object that has these properties:<ul>
42576              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
42577              * returned by the server</div></li>
42578              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
42579              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
42580              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
42581              * </ul>
42582              */
42583             'change',
42584             /**
42585              * @event beforechange
42586              * Fires just before the active page is changed.
42587              * Return false to prevent the active page from being changed.
42588              * @param {Ext.PagingToolbar} this
42589              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
42590              * loading the required page. This will contain:<ul>
42591              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
42592              * be retrieved from the server</div></li>
42593              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
42594              * </ul>
42595              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
42596              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
42597              * <p>Parameters may be added as required in the event handler.</p>
42598              */
42599             'beforechange'
42600         );
42601         this.on('afterlayout', this.onFirstLayout, this, {single: true});
42602         this.cursor = 0;
42603         this.bindStore(this.store);
42604     },
42605
42606     // private
42607     onFirstLayout : function(){
42608         if(this.dsLoaded){
42609             this.onLoad.apply(this, this.dsLoaded);
42610         }
42611     },
42612
42613     // private
42614     updateInfo : function(){
42615         if(this.displayItem){
42616             var count = this.store.getCount();
42617             var msg = count == 0 ?
42618                 this.emptyMsg :
42619                 String.format(
42620                     this.displayMsg,
42621                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
42622                 );
42623             this.displayItem.setText(msg);
42624         }
42625     },
42626
42627     // private
42628     onLoad : function(store, r, o){
42629         if(!this.rendered){
42630             this.dsLoaded = [store, r, o];
42631             return;
42632         }
42633         var p = this.getParams();
42634         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
42635         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
42636
42637         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
42638         this.inputItem.setValue(ap);
42639         this.first.setDisabled(ap == 1);
42640         this.prev.setDisabled(ap == 1);
42641         this.next.setDisabled(ap == ps);
42642         this.last.setDisabled(ap == ps);
42643         this.refresh.enable();
42644         this.updateInfo();
42645         this.fireEvent('change', this, d);
42646     },
42647
42648     // private
42649     getPageData : function(){
42650         var total = this.store.getTotalCount();
42651         return {
42652             total : total,
42653             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
42654             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
42655         };
42656     },
42657
42658     /**
42659      * Change the active page
42660      * @param {Integer} page The page to display
42661      */
42662     changePage : function(page){
42663         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
42664     },
42665
42666     // private
42667     onLoadError : function(){
42668         if(!this.rendered){
42669             return;
42670         }
42671         this.refresh.enable();
42672     },
42673
42674     // private
42675     readPage : function(d){
42676         var v = this.inputItem.getValue(), pageNum;
42677         if (!v || isNaN(pageNum = parseInt(v, 10))) {
42678             this.inputItem.setValue(d.activePage);
42679             return false;
42680         }
42681         return pageNum;
42682     },
42683
42684     onPagingFocus : function(){
42685         this.inputItem.select();
42686     },
42687
42688     //private
42689     onPagingBlur : function(e){
42690         this.inputItem.setValue(this.getPageData().activePage);
42691     },
42692
42693     // private
42694     onPagingKeyDown : function(field, e){
42695         var k = e.getKey(), d = this.getPageData(), pageNum;
42696         if (k == e.RETURN) {
42697             e.stopEvent();
42698             pageNum = this.readPage(d);
42699             if(pageNum !== false){
42700                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
42701                 this.doLoad(pageNum * this.pageSize);
42702             }
42703         }else if (k == e.HOME || k == e.END){
42704             e.stopEvent();
42705             pageNum = k == e.HOME ? 1 : d.pages;
42706             field.setValue(pageNum);
42707         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
42708             e.stopEvent();
42709             if((pageNum = this.readPage(d))){
42710                 var increment = e.shiftKey ? 10 : 1;
42711                 if(k == e.DOWN || k == e.PAGEDOWN){
42712                     increment *= -1;
42713                 }
42714                 pageNum += increment;
42715                 if(pageNum >= 1 & pageNum <= d.pages){
42716                     field.setValue(pageNum);
42717                 }
42718             }
42719         }
42720     },
42721
42722     // private
42723     getParams : function(){
42724         //retain backwards compat, allow params on the toolbar itself, if they exist.
42725         return this.paramNames || this.store.paramNames;
42726     },
42727
42728     // private
42729     beforeLoad : function(){
42730         if(this.rendered && this.refresh){
42731             this.refresh.disable();
42732         }
42733     },
42734
42735     // private
42736     doLoad : function(start){
42737         var o = {}, pn = this.getParams();
42738         o[pn.start] = start;
42739         o[pn.limit] = this.pageSize;
42740         if(this.fireEvent('beforechange', this, o) !== false){
42741             this.store.load({params:o});
42742         }
42743     },
42744
42745     /**
42746      * Move to the first page, has the same effect as clicking the 'first' button.
42747      */
42748     moveFirst : function(){
42749         this.doLoad(0);
42750     },
42751
42752     /**
42753      * Move to the previous page, has the same effect as clicking the 'previous' button.
42754      */
42755     movePrevious : function(){
42756         this.doLoad(Math.max(0, this.cursor-this.pageSize));
42757     },
42758
42759     /**
42760      * Move to the next page, has the same effect as clicking the 'next' button.
42761      */
42762     moveNext : function(){
42763         this.doLoad(this.cursor+this.pageSize);
42764     },
42765
42766     /**
42767      * Move to the last page, has the same effect as clicking the 'last' button.
42768      */
42769     moveLast : function(){
42770         var total = this.store.getTotalCount(),
42771             extra = total % this.pageSize;
42772
42773         this.doLoad(extra ? (total - extra) : total - this.pageSize);
42774     },
42775
42776     /**
42777      * Refresh the current page, has the same effect as clicking the 'refresh' button.
42778      */
42779     refresh : function(){
42780         this.doLoad(this.cursor);
42781     },
42782
42783     /**
42784      * Binds the paging toolbar to the specified {@link Ext.data.Store}
42785      * @param {Store} store The store to bind to this toolbar
42786      * @param {Boolean} initial (Optional) true to not remove listeners
42787      */
42788     bindStore : function(store, initial){
42789         var doLoad;
42790         if(!initial && this.store){
42791             this.store.un('beforeload', this.beforeLoad, this);
42792             this.store.un('load', this.onLoad, this);
42793             this.store.un('exception', this.onLoadError, this);
42794             if(store !== this.store && this.store.autoDestroy){
42795                 this.store.destroy();
42796             }
42797         }
42798         if(store){
42799             store = Ext.StoreMgr.lookup(store);
42800             store.on({
42801                 scope: this,
42802                 beforeload: this.beforeLoad,
42803                 load: this.onLoad,
42804                 exception: this.onLoadError
42805             });
42806             doLoad = store.getCount() > 0;
42807         }
42808         this.store = store;
42809         if(doLoad){
42810             this.onLoad(store, null, {});
42811         }
42812     },
42813
42814     /**
42815      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
42816      * @param {Ext.data.Store} store The data store to unbind
42817      */
42818     unbind : function(store){
42819         this.bindStore(null);
42820     },
42821
42822     /**
42823      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
42824      * @param {Ext.data.Store} store The data store to bind
42825      */
42826     bind : function(store){
42827         this.bindStore(store);
42828     },
42829
42830     // private
42831     onDestroy : function(){
42832         this.bindStore(null);
42833         Ext.PagingToolbar.superclass.onDestroy.call(this);
42834     }
42835 });
42836
42837 })();
42838 Ext.reg('paging', Ext.PagingToolbar);/**\r
42839  * @class Ext.History\r
42840  * @extends Ext.util.Observable\r
42841  * History management component that allows you to register arbitrary tokens that signify application\r
42842  * history state on navigation actions.  You can then handle the history {@link #change} event in order\r
42843  * to reset your application UI to the appropriate state when the user navigates forward or backward through\r
42844  * the browser history stack.\r
42845  * @singleton\r
42846  */\r
42847 Ext.History = (function () {\r
42848     var iframe, hiddenField;\r
42849     var ready = false;\r
42850     var currentToken;\r
42851 \r
42852     function getHash() {\r
42853         var href = top.location.href, i = href.indexOf("#");\r
42854         return i >= 0 ? href.substr(i + 1) : null;\r
42855     }\r
42856 \r
42857     function doSave() {\r
42858         hiddenField.value = currentToken;\r
42859     }\r
42860 \r
42861     function handleStateChange(token) {\r
42862         currentToken = token;\r
42863         Ext.History.fireEvent('change', token);\r
42864     }\r
42865 \r
42866     function updateIFrame (token) {\r
42867         var html = ['<html><body><div id="state">',token,'</div></body></html>'].join('');\r
42868         try {\r
42869             var doc = iframe.contentWindow.document;\r
42870             doc.open();\r
42871             doc.write(html);\r
42872             doc.close();\r
42873             return true;\r
42874         } catch (e) {\r
42875             return false;\r
42876         }\r
42877     }\r
42878 \r
42879     function checkIFrame() {\r
42880         if (!iframe.contentWindow || !iframe.contentWindow.document) {\r
42881             setTimeout(checkIFrame, 10);\r
42882             return;\r
42883         }\r
42884 \r
42885         var doc = iframe.contentWindow.document;\r
42886         var elem = doc.getElementById("state");\r
42887         var token = elem ? elem.innerText : null;\r
42888 \r
42889         var hash = getHash();\r
42890 \r
42891         setInterval(function () {\r
42892 \r
42893             doc = iframe.contentWindow.document;\r
42894             elem = doc.getElementById("state");\r
42895 \r
42896             var newtoken = elem ? elem.innerText : null;\r
42897 \r
42898             var newHash = getHash();\r
42899 \r
42900             if (newtoken !== token) {\r
42901                 token = newtoken;\r
42902                 handleStateChange(token);\r
42903                 top.location.hash = token;\r
42904                 hash = token;\r
42905                 doSave();\r
42906             } else if (newHash !== hash) {\r
42907                 hash = newHash;\r
42908                 updateIFrame(newHash);\r
42909             }\r
42910 \r
42911         }, 50);\r
42912 \r
42913         ready = true;\r
42914 \r
42915         Ext.History.fireEvent('ready', Ext.History);\r
42916     }\r
42917 \r
42918     function startUp() {\r
42919         currentToken = hiddenField.value ? hiddenField.value : getHash();\r
42920 \r
42921         if (Ext.isIE) {\r
42922             checkIFrame();\r
42923         } else {\r
42924             var hash = getHash();\r
42925             setInterval(function () {\r
42926                 var newHash = getHash();\r
42927                 if (newHash !== hash) {\r
42928                     hash = newHash;\r
42929                     handleStateChange(hash);\r
42930                     doSave();\r
42931                 }\r
42932             }, 50);\r
42933             ready = true;\r
42934             Ext.History.fireEvent('ready', Ext.History);\r
42935         }\r
42936     }\r
42937 \r
42938     return {\r
42939         /**\r
42940          * The id of the hidden field required for storing the current history token.\r
42941          * @type String\r
42942          * @property\r
42943          */\r
42944         fieldId: 'x-history-field',\r
42945         /**\r
42946          * The id of the iframe required by IE to manage the history stack.\r
42947          * @type String\r
42948          * @property\r
42949          */\r
42950         iframeId: 'x-history-frame',\r
42951         \r
42952         events:{},\r
42953 \r
42954         /**\r
42955          * Initialize the global History instance.\r
42956          * @param {Boolean} onReady (optional) A callback function that will be called once the history\r
42957          * component is fully initialized.\r
42958          * @param {Object} scope (optional) The callback scope\r
42959          */\r
42960         init: function (onReady, scope) {\r
42961             if(ready) {\r
42962                 Ext.callback(onReady, scope, [this]);\r
42963                 return;\r
42964             }\r
42965             if(!Ext.isReady){\r
42966                 Ext.onReady(function(){\r
42967                     Ext.History.init(onReady, scope);\r
42968                 });\r
42969                 return;\r
42970             }\r
42971             hiddenField = Ext.getDom(Ext.History.fieldId);\r
42972             if (Ext.isIE) {\r
42973                 iframe = Ext.getDom(Ext.History.iframeId);\r
42974             }\r
42975             this.addEvents('ready', 'change');\r
42976             if(onReady){\r
42977                 this.on('ready', onReady, scope, {single:true});\r
42978             }\r
42979             startUp();\r
42980         },\r
42981 \r
42982         /**\r
42983          * Add a new token to the history stack. This can be any arbitrary value, although it would\r
42984          * commonly be the concatenation of a component id and another id marking the specifc history\r
42985          * state of that component.  Example usage:\r
42986          * <pre><code>\r
42987 // Handle tab changes on a TabPanel\r
42988 tabPanel.on('tabchange', function(tabPanel, tab){\r
42989     Ext.History.add(tabPanel.id + ':' + tab.id);\r
42990 });\r
42991 </code></pre>\r
42992          * @param {String} token The value that defines a particular application-specific history state\r
42993          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token\r
42994          * it will not save a new history step. Set to false if the same state can be saved more than once\r
42995          * at the same history stack location (defaults to true).\r
42996          */\r
42997         add: function (token, preventDup) {\r
42998             if(preventDup !== false){\r
42999                 if(this.getToken() == token){\r
43000                     return true;\r
43001                 }\r
43002             }\r
43003             if (Ext.isIE) {\r
43004                 return updateIFrame(token);\r
43005             } else {\r
43006                 top.location.hash = token;\r
43007                 return true;\r
43008             }\r
43009         },\r
43010 \r
43011         /**\r
43012          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).\r
43013          */\r
43014         back: function(){\r
43015             history.go(-1);\r
43016         },\r
43017 \r
43018         /**\r
43019          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).\r
43020          */\r
43021         forward: function(){\r
43022             history.go(1);\r
43023         },\r
43024 \r
43025         /**\r
43026          * Retrieves the currently-active history token.\r
43027          * @return {String} The token\r
43028          */\r
43029         getToken: function() {\r
43030             return ready ? currentToken : getHash();\r
43031         }\r
43032     };\r
43033 })();\r
43034 Ext.apply(Ext.History, new Ext.util.Observable());/**\r
43035  * @class Ext.Tip\r
43036  * @extends Ext.Panel\r
43037  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and\r
43038  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned\r
43039  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.\r
43040  * @constructor\r
43041  * Create a new Tip\r
43042  * @param {Object} config The configuration options\r
43043  */\r
43044 Ext.Tip = Ext.extend(Ext.Panel, {\r
43045     /**\r
43046      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).\r
43047      */\r
43048     /**\r
43049      * @cfg {Number} width\r
43050      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of\r
43051      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.\r
43052      */\r
43053     /**\r
43054      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).\r
43055      */\r
43056     minWidth : 40,\r
43057     /**\r
43058      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.\r
43059      */\r
43060     maxWidth : 300,\r
43061     /**\r
43062      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"\r
43063      * for bottom-right shadow (defaults to "sides").\r
43064      */\r
43065     shadow : "sides",\r
43066     /**\r
43067      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value\r
43068      * for this tip relative to its element of origin (defaults to "tl-bl?").\r
43069      */\r
43070     defaultAlign : "tl-bl?",\r
43071     autoRender: true,\r
43072     quickShowInterval : 250,\r
43073 \r
43074     // private panel overrides\r
43075     frame:true,\r
43076     hidden:true,\r
43077     baseCls: 'x-tip',\r
43078     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},\r
43079     autoHeight:true,\r
43080 \r
43081     closeAction: 'hide',\r
43082 \r
43083     // private\r
43084     initComponent : function(){\r
43085         Ext.Tip.superclass.initComponent.call(this);\r
43086         if(this.closable && !this.title){\r
43087             this.elements += ',header';\r
43088         }\r
43089     },\r
43090 \r
43091     // private\r
43092     afterRender : function(){\r
43093         Ext.Tip.superclass.afterRender.call(this);\r
43094         if(this.closable){\r
43095             this.addTool({\r
43096                 id: 'close',\r
43097                 handler: this[this.closeAction],\r
43098                 scope: this\r
43099             });\r
43100         }\r
43101     },\r
43102 \r
43103     /**\r
43104      * Shows this tip at the specified XY position.  Example usage:\r
43105      * <pre><code>\r
43106 // Show the tip at x:50 and y:100\r
43107 tip.showAt([50,100]);\r
43108 </code></pre>\r
43109      * @param {Array} xy An array containing the x and y coordinates\r
43110      */\r
43111     showAt : function(xy){\r
43112         Ext.Tip.superclass.show.call(this);\r
43113         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){\r
43114             this.doAutoWidth();\r
43115         }\r
43116         if(this.constrainPosition){\r
43117             xy = this.el.adjustForConstraints(xy);\r
43118         }\r
43119         this.setPagePosition(xy[0], xy[1]);\r
43120     },\r
43121 \r
43122     // protected\r
43123     doAutoWidth : function(){\r
43124         var bw = this.body.getTextWidth();\r
43125         if(this.title){\r
43126             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));\r
43127         }\r
43128         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr");\r
43129         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));\r
43130         \r
43131         // IE7 repaint bug on initial show\r
43132         if(Ext.isIE7 && !this.repainted){\r
43133             this.el.repaint();\r
43134             this.repainted = true;\r
43135         }\r
43136     },\r
43137 \r
43138     /**\r
43139      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}\r
43140      * anchor position value.  Example usage:\r
43141      * <pre><code>\r
43142 // Show the tip at the default position ('tl-br?')\r
43143 tip.showBy('my-el');\r
43144 \r
43145 // Show the tip's top-left corner anchored to the element's top-right corner\r
43146 tip.showBy('my-el', 'tl-tr');\r
43147 </code></pre>\r
43148      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to\r
43149      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or\r
43150      * {@link #defaultAlign} if specified).\r
43151      */\r
43152     showBy : function(el, pos){\r
43153         if(!this.rendered){\r
43154             this.render(Ext.getBody());\r
43155         }\r
43156         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));\r
43157     },\r
43158 \r
43159     initDraggable : function(){\r
43160         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);\r
43161         this.header.addClass('x-tip-draggable');\r
43162     }\r
43163 });\r
43164 \r
43165 // private - custom Tip DD implementation\r
43166 Ext.Tip.DD = function(tip, config){\r
43167     Ext.apply(this, config);\r
43168     this.tip = tip;\r
43169     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);\r
43170     this.setHandleElId(tip.header.id);\r
43171     this.scroll = false;\r
43172 };\r
43173 \r
43174 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {\r
43175     moveOnly:true,\r
43176     scroll:false,\r
43177     headerOffsets:[100, 25],\r
43178     startDrag : function(){\r
43179         this.tip.el.disableShadow();\r
43180     },\r
43181     endDrag : function(e){\r
43182         this.tip.el.enableShadow(true);\r
43183     }\r
43184 });/**\r
43185  * @class Ext.ToolTip\r
43186  * @extends Ext.Tip\r
43187  * A standard tooltip implementation for providing additional information when hovering over a target element.\r
43188  * @constructor\r
43189  * Create a new Tooltip\r
43190  * @param {Object} config The configuration options\r
43191  */\r
43192 Ext.ToolTip = Ext.extend(Ext.Tip, {\r
43193     /**\r
43194      * When a Tooltip is configured with the {@link #delegate} option to cause selected child elements of the {@link #target}\r
43195      * Element to each trigger a seperate show event, this property is set to the DOM element which triggered the show.\r
43196      * @type DOMElement\r
43197      * @property triggerElement\r
43198      */\r
43199     /**\r
43200      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor for mouseover events to trigger\r
43201      * showing this ToolTip.\r
43202      */\r
43203     /**\r
43204      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the mouse exits the target element\r
43205      * or after the {@link #dismissDelay} has expired if set (defaults to true).  If {@link closable} = true a close\r
43206      * tool button will be rendered into the tooltip header.\r
43207      */\r
43208     /**\r
43209      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays after the mouse enters the\r
43210      * target element (defaults to 500)\r
43211      */\r
43212     showDelay: 500,\r
43213     /**\r
43214      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the target element but before the\r
43215      * tooltip actually hides (defaults to 200).  Set to 0 for the tooltip to hide immediately.\r
43216      */\r
43217     hideDelay: 200,\r
43218     /**\r
43219      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip automatically hides (defaults to 5000).\r
43220      * To disable automatic hiding, set dismissDelay = 0.\r
43221      */\r
43222     dismissDelay: 5000,\r
43223     /**\r
43224      * @cfg {Array} mouseOffset An XY offset from the mouse position where the tooltip should be shown (defaults to [15,18]).\r
43225      */\r
43226     /**\r
43227      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it moves over the target element (defaults to false).\r
43228      */\r
43229     trackMouse : false,\r
43230     /**\r
43231      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target element, false to\r
43232      * anchor it relative to the mouse coordinates (defaults to true).  When anchorToTarget is\r
43233      * true, use {@link #defaultAlign} to control tooltip alignment to the target element.  When\r
43234      * anchorToTarget is false, use {@link #anchorPosition} instead to control alignment.\r
43235      */\r
43236     anchorToTarget: true,\r
43237     /**\r
43238      * @cfg {Number} anchorOffset A numeric pixel value used to offset the default position of the\r
43239      * anchor arrow (defaults to 0).  When the anchor position is on the top or bottom of the tooltip,\r
43240      * anchorOffset will be used as a horizontal offset.  Likewise, when the anchor position is on the\r
43241      * left or right side, anchorOffset will be used as a vertical offset.\r
43242      */\r
43243     anchorOffset: 0,\r
43244     /**\r
43245      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery} selector which allows selection of individual elements\r
43246      * within the {@link #target} element to trigger showing and hiding the ToolTip as the mouse moves within the target.</p>\r
43247      * <p>When specified, the child element of the target which caused a show event is placed into the {@link #triggerElement} property\r
43248      * before the ToolTip is shown.</p>\r
43249      * <p>This may be useful when a Component has regular, repeating elements in it, each of which need a Tooltip which contains\r
43250      * information specific to that element. For example:</p><pre><code>\r
43251 var myGrid = new Ext.grid.gridPanel(gridConfig);\r
43252 myGrid.on('render', function(grid) {\r
43253     var store = grid.getStore();  // Capture the Store.\r
43254     var view = grid.getView();    // Capture the GridView.\r
43255     myGrid.tip = new Ext.ToolTip({\r
43256         target: view.mainBody,    // The overall target element.\r
43257         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.\r
43258         trackMouse: true,         // Moving within the row should not hide the tip.\r
43259         renderTo: document.body,  // Render immediately so that tip.body can be referenced prior to the first show.\r
43260         listeners: {              // Change content dynamically depending on which element triggered the show.\r
43261             beforeshow: function updateTipBody(tip) {\r
43262                 var rowIndex = view.findRowIndex(tip.triggerElement);\r
43263                 tip.body.dom.innerHTML = "Over Record ID " + store.getAt(rowIndex).id;\r
43264             }\r
43265         }\r
43266     });\r
43267 });</code></pre>\r
43268      */\r
43269 \r
43270     // private\r
43271     targetCounter: 0,\r
43272 \r
43273     constrainPosition: false,\r
43274 \r
43275     // private\r
43276     initComponent: function(){\r
43277         Ext.ToolTip.superclass.initComponent.call(this);\r
43278         this.lastActive = new Date();\r
43279         this.initTarget(this.target);\r
43280         this.origAnchor = this.anchor;\r
43281     },\r
43282 \r
43283     // private\r
43284     onRender : function(ct, position){\r
43285         Ext.ToolTip.superclass.onRender.call(this, ct, position);\r
43286         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();\r
43287         this.anchorEl = this.el.createChild({\r
43288             cls: 'x-tip-anchor ' + this.anchorCls\r
43289         });\r
43290     },\r
43291 \r
43292     // private\r
43293     afterRender : function(){\r
43294         Ext.ToolTip.superclass.afterRender.call(this);\r
43295         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);\r
43296     },\r
43297 \r
43298     /**\r
43299      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.\r
43300      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to\r
43301      */\r
43302     initTarget : function(target){\r
43303         var t;\r
43304         if((t = Ext.get(target))){\r
43305             if(this.target){\r
43306                 this.target = Ext.get(this.target);\r
43307                 this.target.un('mouseover', this.onTargetOver, this);\r
43308                 this.target.un('mouseout', this.onTargetOut, this);\r
43309                 this.target.un('mousemove', this.onMouseMove, this);\r
43310             }\r
43311             this.mon(t, {\r
43312                 mouseover: this.onTargetOver,\r
43313                 mouseout: this.onTargetOut,\r
43314                 mousemove: this.onMouseMove,\r
43315                 scope: this\r
43316             });\r
43317             this.target = t;\r
43318         }\r
43319         if(this.anchor){\r
43320             this.anchorTarget = this.target;\r
43321         }\r
43322     },\r
43323 \r
43324     // private\r
43325     onMouseMove : function(e){\r
43326         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;\r
43327         if (t) {\r
43328             this.targetXY = e.getXY();\r
43329             if (t === this.triggerElement) {\r
43330                 if(!this.hidden && this.trackMouse){\r
43331                     this.setPagePosition(this.getTargetXY());\r
43332                 }\r
43333             } else {\r
43334                 this.hide();\r
43335                 this.lastActive = new Date(0);\r
43336                 this.onTargetOver(e);\r
43337             }\r
43338         } else if (!this.closable && this.isVisible()) {\r
43339             this.hide();\r
43340         }\r
43341     },\r
43342 \r
43343     // private\r
43344     getTargetXY : function(){\r
43345         if(this.anchor){\r
43346             this.targetCounter++;\r
43347             var offsets = this.getOffsets();\r
43348             var xy = (this.anchorToTarget && !this.trackMouse) ?\r
43349                 this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) :\r
43350                 this.targetXY;\r
43351 \r
43352             var dw = Ext.lib.Dom.getViewWidth()-5;\r
43353             var dh = Ext.lib.Dom.getViewHeight()-5;\r
43354             var scrollX = (document.documentElement.scrollLeft || document.body.scrollLeft || 0)+5;\r
43355             var scrollY = (document.documentElement.scrollTop || document.body.scrollTop || 0)+5;\r
43356 \r
43357             var axy = [xy[0] + offsets[0], xy[1] + offsets[1]];\r
43358             var sz = this.getSize();\r
43359             this.anchorEl.removeClass(this.anchorCls);\r
43360 \r
43361             if(this.targetCounter < 2){\r
43362                 if(axy[0] < scrollX){\r
43363                     if(this.anchorToTarget){\r
43364                         this.defaultAlign = 'l-r';\r
43365                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
43366                     }\r
43367                     this.anchor = 'left';\r
43368                     return this.getTargetXY();\r
43369                 }\r
43370                 if(axy[0]+sz.width > dw){\r
43371                     if(this.anchorToTarget){\r
43372                         this.defaultAlign = 'r-l';\r
43373                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
43374                     }\r
43375                     this.anchor = 'right';\r
43376                     return this.getTargetXY();\r
43377                 }\r
43378                 if(axy[1] < scrollY){\r
43379                     if(this.anchorToTarget){\r
43380                         this.defaultAlign = 't-b';\r
43381                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
43382                     }\r
43383                     this.anchor = 'top';\r
43384                     return this.getTargetXY();\r
43385                 }\r
43386                 if(axy[1]+sz.height > dh){\r
43387                     if(this.anchorToTarget){\r
43388                         this.defaultAlign = 'b-t';\r
43389                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
43390                     }\r
43391                     this.anchor = 'bottom';\r
43392                     return this.getTargetXY();\r
43393                 }\r
43394             }\r
43395 \r
43396             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();\r
43397             this.anchorEl.addClass(this.anchorCls);\r
43398             this.targetCounter = 0;\r
43399             return axy;\r
43400         }else{\r
43401             var mouseOffset = this.getMouseOffset();\r
43402             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];\r
43403         }\r
43404     },\r
43405 \r
43406     getMouseOffset : function(){\r
43407         var offset = this.anchor ? [0,0] : [15,18];\r
43408         if(this.mouseOffset){\r
43409             offset[0] += this.mouseOffset[0];\r
43410             offset[1] += this.mouseOffset[1];\r
43411         }\r
43412         return offset;\r
43413     },\r
43414 \r
43415     // private\r
43416     getAnchorPosition : function(){\r
43417         if(this.anchor){\r
43418             this.tipAnchor = this.anchor.charAt(0);\r
43419         }else{\r
43420             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
43421             if(!m){\r
43422                throw "AnchorTip.defaultAlign is invalid";\r
43423             }\r
43424             this.tipAnchor = m[1].charAt(0);\r
43425         }\r
43426 \r
43427         switch(this.tipAnchor){\r
43428             case 't': return 'top';\r
43429             case 'b': return 'bottom';\r
43430             case 'r': return 'right';\r
43431         }\r
43432         return 'left';\r
43433     },\r
43434 \r
43435     // private\r
43436     getAnchorAlign : function(){\r
43437         switch(this.anchor){\r
43438             case 'top'  : return 'tl-bl';\r
43439             case 'left' : return 'tl-tr';\r
43440             case 'right': return 'tr-tl';\r
43441             default     : return 'bl-tl';\r
43442         }\r
43443     },\r
43444 \r
43445     // private\r
43446     getOffsets: function(){\r
43447         var offsets, ap = this.getAnchorPosition().charAt(0);\r
43448         if(this.anchorToTarget && !this.trackMouse){\r
43449             switch(ap){\r
43450                 case 't':\r
43451                     offsets = [0, 9];\r
43452                     break;\r
43453                 case 'b':\r
43454                     offsets = [0, -13];\r
43455                     break;\r
43456                 case 'r':\r
43457                     offsets = [-13, 0];\r
43458                     break;\r
43459                 default:\r
43460                     offsets = [9, 0];\r
43461                     break;\r
43462             }\r
43463         }else{\r
43464             switch(ap){\r
43465                 case 't':\r
43466                     offsets = [-15-this.anchorOffset, 30];\r
43467                     break;\r
43468                 case 'b':\r
43469                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];\r
43470                     break;\r
43471                 case 'r':\r
43472                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];\r
43473                     break;\r
43474                 default:\r
43475                     offsets = [25, -13-this.anchorOffset];\r
43476                     break;\r
43477             }\r
43478         }\r
43479         var mouseOffset = this.getMouseOffset();\r
43480         offsets[0] += mouseOffset[0];\r
43481         offsets[1] += mouseOffset[1];\r
43482 \r
43483         return offsets;\r
43484     },\r
43485 \r
43486     // private\r
43487     onTargetOver : function(e){\r
43488         if(this.disabled || e.within(this.target.dom, true)){\r
43489             return;\r
43490         }\r
43491         var t = e.getTarget(this.delegate);\r
43492         if (t) {\r
43493             this.triggerElement = t;\r
43494             this.clearTimer('hide');\r
43495             this.targetXY = e.getXY();\r
43496             this.delayShow();\r
43497         }\r
43498     },\r
43499 \r
43500     // private\r
43501     delayShow : function(){\r
43502         if(this.hidden && !this.showTimer){\r
43503             if(this.lastActive.getElapsed() < this.quickShowInterval){\r
43504                 this.show();\r
43505             }else{\r
43506                 this.showTimer = this.show.defer(this.showDelay, this);\r
43507             }\r
43508         }else if(!this.hidden && this.autoHide !== false){\r
43509             this.show();\r
43510         }\r
43511     },\r
43512 \r
43513     // private\r
43514     onTargetOut : function(e){\r
43515         if(this.disabled || e.within(this.target.dom, true)){\r
43516             return;\r
43517         }\r
43518         this.clearTimer('show');\r
43519         if(this.autoHide !== false){\r
43520             this.delayHide();\r
43521         }\r
43522     },\r
43523 \r
43524     // private\r
43525     delayHide : function(){\r
43526         if(!this.hidden && !this.hideTimer){\r
43527             this.hideTimer = this.hide.defer(this.hideDelay, this);\r
43528         }\r
43529     },\r
43530 \r
43531     /**\r
43532      * Hides this tooltip if visible.\r
43533      */\r
43534     hide: function(){\r
43535         this.clearTimer('dismiss');\r
43536         this.lastActive = new Date();\r
43537         if(this.anchorEl){\r
43538             this.anchorEl.hide();\r
43539         }\r
43540         Ext.ToolTip.superclass.hide.call(this);\r
43541         delete this.triggerElement;\r
43542     },\r
43543 \r
43544     /**\r
43545      * Shows this tooltip at the current event target XY position.\r
43546      */\r
43547     show : function(){\r
43548         if(this.anchor){\r
43549             // pre-show it off screen so that the el will have dimensions\r
43550             // for positioning calcs when getting xy next\r
43551             this.showAt([-1000,-1000]);\r
43552             this.origConstrainPosition = this.constrainPosition;\r
43553             this.constrainPosition = false;\r
43554             this.anchor = this.origAnchor;\r
43555         }\r
43556         this.showAt(this.getTargetXY());\r
43557 \r
43558         if(this.anchor){\r
43559             this.syncAnchor();\r
43560             this.anchorEl.show();\r
43561             this.constrainPosition = this.origConstrainPosition;\r
43562         }else{\r
43563             this.anchorEl.hide();\r
43564         }\r
43565     },\r
43566 \r
43567     // inherit docs\r
43568     showAt : function(xy){\r
43569         this.lastActive = new Date();\r
43570         this.clearTimers();\r
43571         Ext.ToolTip.superclass.showAt.call(this, xy);\r
43572         if(this.dismissDelay && this.autoHide !== false){\r
43573             this.dismissTimer = this.hide.defer(this.dismissDelay, this);\r
43574         }\r
43575     },\r
43576 \r
43577     // private\r
43578     syncAnchor : function(){\r
43579         var anchorPos, targetPos, offset;\r
43580         switch(this.tipAnchor.charAt(0)){\r
43581             case 't':\r
43582                 anchorPos = 'b';\r
43583                 targetPos = 'tl';\r
43584                 offset = [20+this.anchorOffset, 2];\r
43585                 break;\r
43586             case 'r':\r
43587                 anchorPos = 'l';\r
43588                 targetPos = 'tr';\r
43589                 offset = [-2, 11+this.anchorOffset];\r
43590                 break;\r
43591             case 'b':\r
43592                 anchorPos = 't';\r
43593                 targetPos = 'bl';\r
43594                 offset = [20+this.anchorOffset, -2];\r
43595                 break;\r
43596             default:\r
43597                 anchorPos = 'r';\r
43598                 targetPos = 'tl';\r
43599                 offset = [2, 11+this.anchorOffset];\r
43600                 break;\r
43601         }\r
43602         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);\r
43603     },\r
43604 \r
43605     // private\r
43606     setPagePosition : function(x, y){\r
43607         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);\r
43608         if(this.anchor){\r
43609             this.syncAnchor();\r
43610         }\r
43611     },\r
43612 \r
43613     // private\r
43614     clearTimer : function(name){\r
43615         name = name + 'Timer';\r
43616         clearTimeout(this[name]);\r
43617         delete this[name];\r
43618     },\r
43619 \r
43620     // private\r
43621     clearTimers : function(){\r
43622         this.clearTimer('show');\r
43623         this.clearTimer('dismiss');\r
43624         this.clearTimer('hide');\r
43625     },\r
43626 \r
43627     // private\r
43628     onShow : function(){\r
43629         Ext.ToolTip.superclass.onShow.call(this);\r
43630         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);\r
43631     },\r
43632 \r
43633     // private\r
43634     onHide : function(){\r
43635         Ext.ToolTip.superclass.onHide.call(this);\r
43636         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
43637     },\r
43638 \r
43639     // private\r
43640     onDocMouseDown : function(e){\r
43641         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){\r
43642             this.disable();\r
43643             this.enable.defer(100, this);\r
43644         }\r
43645     },\r
43646 \r
43647     // private\r
43648     onDisable : function(){\r
43649         this.clearTimers();\r
43650         this.hide();\r
43651     },\r
43652 \r
43653     // private\r
43654     adjustPosition : function(x, y){\r
43655         if(this.contstrainPosition){\r
43656             var ay = this.targetXY[1], h = this.getSize().height;\r
43657             if(y <= ay && (y+h) >= ay){\r
43658                 y = ay-h-5;\r
43659             }\r
43660         }\r
43661         return {x : x, y: y};\r
43662     },\r
43663 \r
43664     // private\r
43665     onDestroy : function(){\r
43666         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
43667         Ext.ToolTip.superclass.onDestroy.call(this);\r
43668     }\r
43669 });/**\r
43670  * @class Ext.QuickTip\r
43671  * @extends Ext.ToolTip\r
43672  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global\r
43673  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.\r
43674  * @constructor\r
43675  * Create a new Tip\r
43676  * @param {Object} config The configuration options\r
43677  */\r
43678 Ext.QuickTip = Ext.extend(Ext.ToolTip, {\r
43679     /**\r
43680      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).\r
43681      */\r
43682     /**\r
43683      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).\r
43684      */\r
43685     interceptTitles : false,\r
43686 \r
43687     // private\r
43688     tagConfig : {\r
43689         namespace : "ext",\r
43690         attribute : "qtip",\r
43691         width : "qwidth",\r
43692         target : "target",\r
43693         title : "qtitle",\r
43694         hide : "hide",\r
43695         cls : "qclass",\r
43696         align : "qalign",\r
43697         anchor : "anchor"\r
43698     },\r
43699 \r
43700     // private\r
43701     initComponent : function(){\r
43702         this.target = this.target || Ext.getDoc();\r
43703         this.targets = this.targets || {};\r
43704         Ext.QuickTip.superclass.initComponent.call(this);\r
43705     },\r
43706 \r
43707     /**\r
43708      * Configures a new quick tip instance and assigns it to a target element.  The following config values are\r
43709      * supported (for example usage, see the {@link Ext.QuickTips} class header):\r
43710      * <div class="mdetail-params"><ul>\r
43711      * <li>autoHide</li>\r
43712      * <li>cls</li>\r
43713      * <li>dismissDelay (overrides the singleton value)</li>\r
43714      * <li>target (required)</li>\r
43715      * <li>text (required)</li>\r
43716      * <li>title</li>\r
43717      * <li>width</li></ul></div>\r
43718      * @param {Object} config The config object\r
43719      */\r
43720     register : function(config){\r
43721         var cs = Ext.isArray(config) ? config : arguments;\r
43722         for(var i = 0, len = cs.length; i < len; i++){\r
43723             var c = cs[i];\r
43724             var target = c.target;\r
43725             if(target){\r
43726                 if(Ext.isArray(target)){\r
43727                     for(var j = 0, jlen = target.length; j < jlen; j++){\r
43728                         this.targets[Ext.id(target[j])] = c;\r
43729                     }\r
43730                 } else{\r
43731                     this.targets[Ext.id(target)] = c;\r
43732                 }\r
43733             }\r
43734         }\r
43735     },\r
43736 \r
43737     /**\r
43738      * Removes this quick tip from its element and destroys it.\r
43739      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
43740      */\r
43741     unregister : function(el){\r
43742         delete this.targets[Ext.id(el)];\r
43743     },\r
43744     \r
43745     /**\r
43746      * Hides a visible tip or cancels an impending show for a particular element.\r
43747      * @param {String/HTMLElement/Element} el The element that is the target of the tip.\r
43748      */\r
43749     cancelShow: function(el){\r
43750         var at = this.activeTarget;\r
43751         el = Ext.get(el).dom;\r
43752         if(this.isVisible()){\r
43753             if(at && at.el == el){\r
43754                 this.hide();\r
43755             }\r
43756         }else if(at && at.el == el){\r
43757             this.clearTimer('show');\r
43758         }\r
43759     },\r
43760 \r
43761     // private\r
43762     onTargetOver : function(e){\r
43763         if(this.disabled){\r
43764             return;\r
43765         }\r
43766         this.targetXY = e.getXY();\r
43767         var t = e.getTarget();\r
43768         if(!t || t.nodeType !== 1 || t == document || t == document.body){\r
43769             return;\r
43770         }\r
43771         if(this.activeTarget && t == this.activeTarget.el){\r
43772             this.clearTimer('hide');\r
43773             this.show();\r
43774             return;\r
43775         }\r
43776         if(t && this.targets[t.id]){\r
43777             this.activeTarget = this.targets[t.id];\r
43778             this.activeTarget.el = t;\r
43779             this.anchor = this.activeTarget.anchor;\r
43780             if(this.anchor){\r
43781                 this.anchorTarget = t;\r
43782             }\r
43783             this.delayShow();\r
43784             return;\r
43785         }\r
43786         \r
43787         var ttp, et = Ext.fly(t), cfg = this.tagConfig;\r
43788         var ns = cfg.namespace;\r
43789         if(this.interceptTitles && t.title){\r
43790             ttp = t.title;\r
43791             t.qtip = ttp;\r
43792             t.removeAttribute("title");\r
43793             e.preventDefault();\r
43794         } else{\r
43795             ttp = t.qtip || et.getAttribute(cfg.attribute, ns);\r
43796         }\r
43797         if(ttp){\r
43798             var autoHide = et.getAttribute(cfg.hide, ns);\r
43799             this.activeTarget = {\r
43800                 el: t,\r
43801                 text: ttp,\r
43802                 width: et.getAttribute(cfg.width, ns),\r
43803                 autoHide: autoHide != "user" && autoHide !== 'false',\r
43804                 title: et.getAttribute(cfg.title, ns),\r
43805                 cls: et.getAttribute(cfg.cls, ns),\r
43806                 align: et.getAttribute(cfg.align, ns)\r
43807                 \r
43808             };\r
43809             this.anchor = et.getAttribute(cfg.anchor, ns);\r
43810             if(this.anchor){\r
43811                 this.anchorTarget = t;\r
43812             }\r
43813             this.delayShow();\r
43814         }\r
43815     },\r
43816 \r
43817     // private\r
43818     onTargetOut : function(e){\r
43819         this.clearTimer('show');\r
43820         if(this.autoHide !== false){\r
43821             this.delayHide();\r
43822         }\r
43823     },\r
43824 \r
43825     // inherit docs\r
43826     showAt : function(xy){\r
43827         var t = this.activeTarget;\r
43828         if(t){\r
43829             if(!this.rendered){\r
43830                 this.render(Ext.getBody());\r
43831                 this.activeTarget = t;\r
43832             }\r
43833             if(t.width){\r
43834                 this.setWidth(t.width);\r
43835                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));\r
43836                 this.measureWidth = false;\r
43837             } else{\r
43838                 this.measureWidth = true;\r
43839             }\r
43840             this.setTitle(t.title || '');\r
43841             this.body.update(t.text);\r
43842             this.autoHide = t.autoHide;\r
43843             this.dismissDelay = t.dismissDelay || this.dismissDelay;\r
43844             if(this.lastCls){\r
43845                 this.el.removeClass(this.lastCls);\r
43846                 delete this.lastCls;\r
43847             }\r
43848             if(t.cls){\r
43849                 this.el.addClass(t.cls);\r
43850                 this.lastCls = t.cls;\r
43851             }\r
43852             if(this.anchor){\r
43853                 this.constrainPosition = false;\r
43854             }else if(t.align){ // TODO: this doesn't seem to work consistently\r
43855                 xy = this.el.getAlignToXY(t.el, t.align);\r
43856                 this.constrainPosition = false;\r
43857             }else{\r
43858                 this.constrainPosition = true;\r
43859             }\r
43860         }\r
43861         Ext.QuickTip.superclass.showAt.call(this, xy);\r
43862     },\r
43863 \r
43864     // inherit docs\r
43865     hide: function(){\r
43866         delete this.activeTarget;\r
43867         Ext.QuickTip.superclass.hide.call(this);\r
43868     }\r
43869 });/**\r
43870  * @class Ext.QuickTips\r
43871  * <p>Provides attractive and customizable tooltips for any element. The QuickTips\r
43872  * singleton is used to configure and manage tooltips globally for multiple elements\r
43873  * in a generic manner.  To create individual tooltips with maximum customizability,\r
43874  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>\r
43875  * <p>Quicktips can be configured via tag attributes directly in markup, or by\r
43876  * registering quick tips programmatically via the {@link #register} method.</p>\r
43877  * <p>The singleton's instance of {@link Ext.QuickTip} is available via\r
43878  * {@link #getQuickTip}, and supports all the methods, and all the all the\r
43879  * configuration properties of Ext.QuickTip. These settings will apply to all\r
43880  * tooltips shown by the singleton.</p>\r
43881  * <p>Below is the summary of the configuration properties which can be used.\r
43882  * For detailed descriptions see {@link #getQuickTip}</p>\r
43883  * <p><b>QuickTips singleton configs (all are optional)</b></p>\r
43884  * <div class="mdetail-params"><ul><li>dismissDelay</li>\r
43885  * <li>hideDelay</li>\r
43886  * <li>maxWidth</li>\r
43887  * <li>minWidth</li>\r
43888  * <li>showDelay</li>\r
43889  * <li>trackMouse</li></ul></div>\r
43890  * <p><b>Target element configs (optional unless otherwise noted)</b></p>\r
43891  * <div class="mdetail-params"><ul><li>autoHide</li>\r
43892  * <li>cls</li>\r
43893  * <li>dismissDelay (overrides singleton value)</li>\r
43894  * <li>target (required)</li>\r
43895  * <li>text (required)</li>\r
43896  * <li>title</li>\r
43897  * <li>width</li></ul></div>\r
43898  * <p>Here is an example showing how some of these config options could be used:</p>\r
43899  * <pre><code>\r
43900 // Init the singleton.  Any tag-based quick tips will start working.\r
43901 Ext.QuickTips.init();\r
43902 \r
43903 // Apply a set of config properties to the singleton\r
43904 Ext.apply(Ext.QuickTips.getQuickTip(), {\r
43905     maxWidth: 200,\r
43906     minWidth: 100,\r
43907     showDelay: 50,\r
43908     trackMouse: true\r
43909 });\r
43910 \r
43911 // Manually register a quick tip for a specific element\r
43912 Ext.QuickTips.register({\r
43913     target: 'my-div',\r
43914     title: 'My Tooltip',\r
43915     text: 'This tooltip was added in code',\r
43916     width: 100,\r
43917     dismissDelay: 20\r
43918 });\r
43919 </code></pre>\r
43920  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with\r
43921  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary\r
43922  * of supported attributes (optional unless otherwise noted):</p>\r
43923  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the\r
43924  * same as autoHide = true.</li>\r
43925  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>\r
43926  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>\r
43927  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>\r
43928  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>\r
43929  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>\r
43930  * <pre><code>\r
43931 // Add a quick tip to an HTML button\r
43932 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"\r
43933      ext:qtip="This is a quick tip from markup!">&lt;/input>\r
43934 </code></pre>\r
43935  * @singleton\r
43936  */\r
43937 Ext.QuickTips = function(){\r
43938     var tip, locks = [];\r
43939     return {\r
43940         /**\r
43941          * Initialize the global QuickTips instance and prepare any quick tips.\r
43942          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) \r
43943          */\r
43944         init : function(autoRender){\r
43945             if(!tip){\r
43946                 if(!Ext.isReady){\r
43947                     Ext.onReady(function(){\r
43948                         Ext.QuickTips.init(autoRender);\r
43949                     });\r
43950                     return;\r
43951                 }\r
43952                 tip = new Ext.QuickTip({elements:'header,body'});\r
43953                 if(autoRender !== false){\r
43954                     tip.render(Ext.getBody());\r
43955                 }\r
43956             }\r
43957         },\r
43958 \r
43959         /**\r
43960          * Enable quick tips globally.\r
43961          */\r
43962         enable : function(){\r
43963             if(tip){\r
43964                 locks.pop();\r
43965                 if(locks.length < 1){\r
43966                     tip.enable();\r
43967                 }\r
43968             }\r
43969         },\r
43970 \r
43971         /**\r
43972          * Disable quick tips globally.\r
43973          */\r
43974         disable : function(){\r
43975             if(tip){\r
43976                 tip.disable();\r
43977             }\r
43978             locks.push(1);\r
43979         },\r
43980 \r
43981         /**\r
43982          * Returns true if quick tips are enabled, else false.\r
43983          * @return {Boolean}\r
43984          */\r
43985         isEnabled : function(){\r
43986             return tip !== undefined && !tip.disabled;\r
43987         },\r
43988 \r
43989         /**\r
43990          * Gets the global QuickTips instance.\r
43991          */\r
43992         getQuickTip : function(){\r
43993             return tip;\r
43994         },\r
43995 \r
43996         /**\r
43997          * Configures a new quick tip instance and assigns it to a target element.  See\r
43998          * {@link Ext.QuickTip#register} for details.\r
43999          * @param {Object} config The config object\r
44000          */\r
44001         register : function(){\r
44002             tip.register.apply(tip, arguments);\r
44003         },\r
44004 \r
44005         /**\r
44006          * Removes any registered quick tip from the target element and destroys it.\r
44007          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
44008          */\r
44009         unregister : function(){\r
44010             tip.unregister.apply(tip, arguments);\r
44011         },\r
44012 \r
44013         /**\r
44014          * Alias of {@link #register}.\r
44015          * @param {Object} config The config object\r
44016          */\r
44017         tips :function(){\r
44018             tip.register.apply(tip, arguments);\r
44019         }\r
44020     }\r
44021 }();/**\r
44022  * @class Ext.tree.TreePanel\r
44023  * @extends Ext.Panel\r
44024  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>\r
44025  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata\r
44026  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>\r
44027  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be\r
44028  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.\r
44029  * <p>An example of tree rendered to an existing div:</p><pre><code>\r
44030 var tree = new Ext.tree.TreePanel({\r
44031     renderTo: 'tree-div',\r
44032     useArrows: true,\r
44033     autoScroll: true,\r
44034     animate: true,\r
44035     enableDD: true,\r
44036     containerScroll: true,\r
44037     border: false,\r
44038     // auto create TreeLoader\r
44039     dataUrl: 'get-nodes.php',\r
44040 \r
44041     root: {\r
44042         nodeType: 'async',\r
44043         text: 'Ext JS',\r
44044         draggable: false,\r
44045         id: 'source'\r
44046     }\r
44047 });\r
44048 \r
44049 tree.getRootNode().expand();\r
44050  * </code></pre>\r
44051  * <p>The example above would work with a data packet similar to this:</p><pre><code>\r
44052 [{\r
44053     "text": "adapter",\r
44054     "id": "source\/adapter",\r
44055     "cls": "folder"\r
44056 }, {\r
44057     "text": "dd",\r
44058     "id": "source\/dd",\r
44059     "cls": "folder"\r
44060 }, {\r
44061     "text": "debug.js",\r
44062     "id": "source\/debug.js",\r
44063     "leaf": true,\r
44064     "cls": "file"\r
44065 }]\r
44066  * </code></pre>\r
44067  * <p>An example of tree within a Viewport:</p><pre><code>\r
44068 new Ext.Viewport({\r
44069     layout: 'border',\r
44070     items: [{\r
44071         region: 'west',\r
44072         collapsible: true,\r
44073         title: 'Navigation',\r
44074         xtype: 'treepanel',\r
44075         width: 200,\r
44076         autoScroll: true,\r
44077         split: true,\r
44078         loader: new Ext.tree.TreeLoader(),\r
44079         root: new Ext.tree.AsyncTreeNode({\r
44080             expanded: true,\r
44081             children: [{\r
44082                 text: 'Menu Option 1',\r
44083                 leaf: true\r
44084             }, {\r
44085                 text: 'Menu Option 2',\r
44086                 leaf: true\r
44087             }, {\r
44088                 text: 'Menu Option 3',\r
44089                 leaf: true\r
44090             }]\r
44091         }),\r
44092         rootVisible: false,\r
44093         listeners: {\r
44094             click: function(n) {\r
44095                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');\r
44096             }\r
44097         }\r
44098     }, {\r
44099         region: 'center',\r
44100         xtype: 'tabpanel',\r
44101         // remaining code not shown ...\r
44102     }]\r
44103 });\r
44104 </code></pre>\r
44105  *\r
44106  * @cfg {Ext.tree.TreeNode} root The root node for the tree.\r
44107  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)\r
44108  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)\r
44109  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop\r
44110  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag\r
44111  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop\r
44112  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance\r
44113  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance\r
44114  * @cfg {String} ddGroup The DD group this TreePanel belongs to\r
44115  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)\r
44116  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling\r
44117  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager\r
44118  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})\r
44119  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)\r
44120  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})\r
44121  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded\r
44122  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})\r
44123  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting\r
44124  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel\r
44125  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)\r
44126  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)\r
44127  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
44128  *\r
44129  * @constructor\r
44130  * @param {Object} config\r
44131  * @xtype treepanel\r
44132  */\r
44133 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {\r
44134     rootVisible : true,\r
44135     animate: Ext.enableFx,\r
44136     lines : true,\r
44137     enableDD : false,\r
44138     hlDrop : Ext.enableFx,\r
44139     pathSeparator: "/",\r
44140 \r
44141     initComponent : function(){\r
44142         Ext.tree.TreePanel.superclass.initComponent.call(this);\r
44143 \r
44144         if(!this.eventModel){\r
44145             this.eventModel = new Ext.tree.TreeEventModel(this);\r
44146         }\r
44147 \r
44148         // initialize the loader\r
44149         var l = this.loader;\r
44150         if(!l){\r
44151             l = new Ext.tree.TreeLoader({\r
44152                 dataUrl: this.dataUrl,\r
44153                 requestMethod: this.requestMethod\r
44154             });\r
44155         }else if(typeof l == 'object' && !l.load){\r
44156             l = new Ext.tree.TreeLoader(l);\r
44157         }\r
44158         this.loader = l;\r
44159 \r
44160         this.nodeHash = {};\r
44161 \r
44162         /**\r
44163         * The root node of this tree.\r
44164         * @type Ext.tree.TreeNode\r
44165         * @property root\r
44166         */\r
44167         if(this.root){\r
44168             var r = this.root;\r
44169             delete this.root;\r
44170             this.setRootNode(r);\r
44171         }\r
44172 \r
44173 \r
44174         this.addEvents(\r
44175 \r
44176             /**\r
44177             * @event append\r
44178             * Fires when a new child node is appended to a node in this tree.\r
44179             * @param {Tree} tree The owner tree\r
44180             * @param {Node} parent The parent node\r
44181             * @param {Node} node The newly appended node\r
44182             * @param {Number} index The index of the newly appended node\r
44183             */\r
44184            "append",\r
44185            /**\r
44186             * @event remove\r
44187             * Fires when a child node is removed from a node in this tree.\r
44188             * @param {Tree} tree The owner tree\r
44189             * @param {Node} parent The parent node\r
44190             * @param {Node} node The child node removed\r
44191             */\r
44192            "remove",\r
44193            /**\r
44194             * @event movenode\r
44195             * Fires when a node is moved to a new location in the tree\r
44196             * @param {Tree} tree The owner tree\r
44197             * @param {Node} node The node moved\r
44198             * @param {Node} oldParent The old parent of this node\r
44199             * @param {Node} newParent The new parent of this node\r
44200             * @param {Number} index The index it was moved to\r
44201             */\r
44202            "movenode",\r
44203            /**\r
44204             * @event insert\r
44205             * Fires when a new child node is inserted in a node in this tree.\r
44206             * @param {Tree} tree The owner tree\r
44207             * @param {Node} parent The parent node\r
44208             * @param {Node} node The child node inserted\r
44209             * @param {Node} refNode The child node the node was inserted before\r
44210             */\r
44211            "insert",\r
44212            /**\r
44213             * @event beforeappend\r
44214             * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
44215             * @param {Tree} tree The owner tree\r
44216             * @param {Node} parent The parent node\r
44217             * @param {Node} node The child node to be appended\r
44218             */\r
44219            "beforeappend",\r
44220            /**\r
44221             * @event beforeremove\r
44222             * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
44223             * @param {Tree} tree The owner tree\r
44224             * @param {Node} parent The parent node\r
44225             * @param {Node} node The child node to be removed\r
44226             */\r
44227            "beforeremove",\r
44228            /**\r
44229             * @event beforemovenode\r
44230             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
44231             * @param {Tree} tree The owner tree\r
44232             * @param {Node} node The node being moved\r
44233             * @param {Node} oldParent The parent of the node\r
44234             * @param {Node} newParent The new parent the node is moving to\r
44235             * @param {Number} index The index it is being moved to\r
44236             */\r
44237            "beforemovenode",\r
44238            /**\r
44239             * @event beforeinsert\r
44240             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
44241             * @param {Tree} tree The owner tree\r
44242             * @param {Node} parent The parent node\r
44243             * @param {Node} node The child node to be inserted\r
44244             * @param {Node} refNode The child node the node is being inserted before\r
44245             */\r
44246             "beforeinsert",\r
44247 \r
44248             /**\r
44249             * @event beforeload\r
44250             * Fires before a node is loaded, return false to cancel\r
44251             * @param {Node} node The node being loaded\r
44252             */\r
44253             "beforeload",\r
44254             /**\r
44255             * @event load\r
44256             * Fires when a node is loaded\r
44257             * @param {Node} node The node that was loaded\r
44258             */\r
44259             "load",\r
44260             /**\r
44261             * @event textchange\r
44262             * Fires when the text for a node is changed\r
44263             * @param {Node} node The node\r
44264             * @param {String} text The new text\r
44265             * @param {String} oldText The old text\r
44266             */\r
44267             "textchange",\r
44268             /**\r
44269             * @event beforeexpandnode\r
44270             * Fires before a node is expanded, return false to cancel.\r
44271             * @param {Node} node The node\r
44272             * @param {Boolean} deep\r
44273             * @param {Boolean} anim\r
44274             */\r
44275             "beforeexpandnode",\r
44276             /**\r
44277             * @event beforecollapsenode\r
44278             * Fires before a node is collapsed, return false to cancel.\r
44279             * @param {Node} node The node\r
44280             * @param {Boolean} deep\r
44281             * @param {Boolean} anim\r
44282             */\r
44283             "beforecollapsenode",\r
44284             /**\r
44285             * @event expandnode\r
44286             * Fires when a node is expanded\r
44287             * @param {Node} node The node\r
44288             */\r
44289             "expandnode",\r
44290             /**\r
44291             * @event disabledchange\r
44292             * Fires when the disabled status of a node changes\r
44293             * @param {Node} node The node\r
44294             * @param {Boolean} disabled\r
44295             */\r
44296             "disabledchange",\r
44297             /**\r
44298             * @event collapsenode\r
44299             * Fires when a node is collapsed\r
44300             * @param {Node} node The node\r
44301             */\r
44302             "collapsenode",\r
44303             /**\r
44304             * @event beforeclick\r
44305             * Fires before click processing on a node. Return false to cancel the default action.\r
44306             * @param {Node} node The node\r
44307             * @param {Ext.EventObject} e The event object\r
44308             */\r
44309             "beforeclick",\r
44310             /**\r
44311             * @event click\r
44312             * Fires when a node is clicked\r
44313             * @param {Node} node The node\r
44314             * @param {Ext.EventObject} e The event object\r
44315             */\r
44316             "click",\r
44317             /**\r
44318             * @event checkchange\r
44319             * Fires when a node with a checkbox's checked property changes\r
44320             * @param {Node} this This node\r
44321             * @param {Boolean} checked\r
44322             */\r
44323             "checkchange",\r
44324             /**\r
44325             * @event dblclick\r
44326             * Fires when a node is double clicked\r
44327             * @param {Node} node The node\r
44328             * @param {Ext.EventObject} e The event object\r
44329             */\r
44330             "dblclick",\r
44331             /**\r
44332             * @event contextmenu\r
44333             * Fires when a node is right clicked. To display a context menu in response to this\r
44334             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add\r
44335             * a handler for this event:<pre><code>\r
44336 new Ext.tree.TreePanel({\r
44337     title: 'My TreePanel',\r
44338     root: new Ext.tree.AsyncTreeNode({\r
44339         text: 'The Root',\r
44340         children: [\r
44341             { text: 'Child node 1', leaf: true },\r
44342             { text: 'Child node 2', leaf: true }\r
44343         ]\r
44344     }),\r
44345     contextMenu: new Ext.menu.Menu({\r
44346         items: [{\r
44347             id: 'delete-node',\r
44348             text: 'Delete Node'\r
44349         }],\r
44350         listeners: {\r
44351             itemclick: function(item) {\r
44352                 switch (item.id) {\r
44353                     case 'delete-node':\r
44354                         var n = item.parentMenu.contextNode;\r
44355                         if (n.parentNode) {\r
44356                             n.remove();\r
44357                         }\r
44358                         break;\r
44359                 }\r
44360             }\r
44361         }\r
44362     }),\r
44363     listeners: {\r
44364         contextmenu: function(node, e) {\r
44365 //          Register the context node with the menu so that a Menu Item's handler function can access\r
44366 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.\r
44367             node.select();\r
44368             var c = node.getOwnerTree().contextMenu;\r
44369             c.contextNode = node;\r
44370             c.showAt(e.getXY());\r
44371         }\r
44372     }\r
44373 });\r
44374 </code></pre>\r
44375             * @param {Node} node The node\r
44376             * @param {Ext.EventObject} e The event object\r
44377             */\r
44378             "contextmenu",\r
44379             /**\r
44380             * @event beforechildrenrendered\r
44381             * Fires right before the child nodes for a node are rendered\r
44382             * @param {Node} node The node\r
44383             */\r
44384             "beforechildrenrendered",\r
44385            /**\r
44386              * @event startdrag\r
44387              * Fires when a node starts being dragged\r
44388              * @param {Ext.tree.TreePanel} this\r
44389              * @param {Ext.tree.TreeNode} node\r
44390              * @param {event} e The raw browser event\r
44391              */\r
44392             "startdrag",\r
44393             /**\r
44394              * @event enddrag\r
44395              * Fires when a drag operation is complete\r
44396              * @param {Ext.tree.TreePanel} this\r
44397              * @param {Ext.tree.TreeNode} node\r
44398              * @param {event} e The raw browser event\r
44399              */\r
44400             "enddrag",\r
44401             /**\r
44402              * @event dragdrop\r
44403              * Fires when a dragged node is dropped on a valid DD target\r
44404              * @param {Ext.tree.TreePanel} this\r
44405              * @param {Ext.tree.TreeNode} node\r
44406              * @param {DD} dd The dd it was dropped on\r
44407              * @param {event} e The raw browser event\r
44408              */\r
44409             "dragdrop",\r
44410             /**\r
44411              * @event beforenodedrop\r
44412              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent\r
44413              * passed to handlers has the following properties:<br />\r
44414              * <ul style="padding:5px;padding-left:16px;">\r
44415              * <li>tree - The TreePanel</li>\r
44416              * <li>target - The node being targeted for the drop</li>\r
44417              * <li>data - The drag data from the drag source</li>\r
44418              * <li>point - The point of the drop - append, above or below</li>\r
44419              * <li>source - The drag source</li>\r
44420              * <li>rawEvent - Raw mouse event</li>\r
44421              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)\r
44422              * to be inserted by setting them on this object.</li>\r
44423              * <li>cancel - Set this to true to cancel the drop.</li>\r
44424              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true\r
44425              * will prevent the animated "repair" from appearing.</li>\r
44426              * </ul>\r
44427              * @param {Object} dropEvent\r
44428              */\r
44429             "beforenodedrop",\r
44430             /**\r
44431              * @event nodedrop\r
44432              * Fires after a DD object is dropped on a node in this tree. The dropEvent\r
44433              * passed to handlers has the following properties:<br />\r
44434              * <ul style="padding:5px;padding-left:16px;">\r
44435              * <li>tree - The TreePanel</li>\r
44436              * <li>target - The node being targeted for the drop</li>\r
44437              * <li>data - The drag data from the drag source</li>\r
44438              * <li>point - The point of the drop - append, above or below</li>\r
44439              * <li>source - The drag source</li>\r
44440              * <li>rawEvent - Raw mouse event</li>\r
44441              * <li>dropNode - Dropped node(s).</li>\r
44442              * </ul>\r
44443              * @param {Object} dropEvent\r
44444              */\r
44445             "nodedrop",\r
44446              /**\r
44447              * @event nodedragover\r
44448              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent\r
44449              * passed to handlers has the following properties:<br />\r
44450              * <ul style="padding:5px;padding-left:16px;">\r
44451              * <li>tree - The TreePanel</li>\r
44452              * <li>target - The node being targeted for the drop</li>\r
44453              * <li>data - The drag data from the drag source</li>\r
44454              * <li>point - The point of the drop - append, above or below</li>\r
44455              * <li>source - The drag source</li>\r
44456              * <li>rawEvent - Raw mouse event</li>\r
44457              * <li>dropNode - Drop node(s) provided by the source.</li>\r
44458              * <li>cancel - Set this to true to signal drop not allowed.</li>\r
44459              * </ul>\r
44460              * @param {Object} dragOverEvent\r
44461              */\r
44462             "nodedragover"\r
44463         );\r
44464         if(this.singleExpand){\r
44465             this.on("beforeexpandnode", this.restrictExpand, this);\r
44466         }\r
44467     },\r
44468 \r
44469     // private\r
44470     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){\r
44471         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){\r
44472             ename = ename+'node';\r
44473         }\r
44474         // args inline for performance while bubbling events\r
44475         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);\r
44476     },\r
44477 \r
44478 \r
44479     /**\r
44480      * Returns this root node for this tree\r
44481      * @return {Node}\r
44482      */\r
44483     getRootNode : function(){\r
44484         return this.root;\r
44485     },\r
44486 \r
44487     /**\r
44488      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the\r
44489      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.\r
44490      * @param {Node} node\r
44491      * @return {Node}\r
44492      */\r
44493     setRootNode : function(node){\r
44494         Ext.destroy(this.root);\r
44495         if(!node.render){ // attributes passed\r
44496             node = this.loader.createNode(node);\r
44497         }\r
44498         this.root = node;\r
44499         node.ownerTree = this;\r
44500         node.isRoot = true;\r
44501         this.registerNode(node);\r
44502         if(!this.rootVisible){\r
44503             var uiP = node.attributes.uiProvider;\r
44504             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);\r
44505         }\r
44506         if (this.innerCt) {\r
44507             this.innerCt.update('');\r
44508             this.afterRender();\r
44509         }\r
44510         return node;\r
44511     },\r
44512 \r
44513     /**\r
44514      * Gets a node in this tree by its id\r
44515      * @param {String} id\r
44516      * @return {Node}\r
44517      */\r
44518     getNodeById : function(id){\r
44519         return this.nodeHash[id];\r
44520     },\r
44521 \r
44522     // private\r
44523     registerNode : function(node){\r
44524         this.nodeHash[node.id] = node;\r
44525     },\r
44526 \r
44527     // private\r
44528     unregisterNode : function(node){\r
44529         delete this.nodeHash[node.id];\r
44530     },\r
44531 \r
44532     // private\r
44533     toString : function(){\r
44534         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
44535     },\r
44536 \r
44537     // private\r
44538     restrictExpand : function(node){\r
44539         var p = node.parentNode;\r
44540         if(p){\r
44541             if(p.expandedChild && p.expandedChild.parentNode == p){\r
44542                 p.expandedChild.collapse();\r
44543             }\r
44544             p.expandedChild = node;\r
44545         }\r
44546     },\r
44547 \r
44548     /**\r
44549      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")\r
44550      * @param {String} attribute (optional) Defaults to null (return the actual nodes)\r
44551      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root\r
44552      * @return {Array}\r
44553      */\r
44554     getChecked : function(a, startNode){\r
44555         startNode = startNode || this.root;\r
44556         var r = [];\r
44557         var f = function(){\r
44558             if(this.attributes.checked){\r
44559                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));\r
44560             }\r
44561         };\r
44562         startNode.cascade(f);\r
44563         return r;\r
44564     },\r
44565 \r
44566     /**\r
44567      * Returns the container element for this TreePanel.\r
44568      * @return {Element} The container element for this TreePanel.\r
44569      */\r
44570     getEl : function(){\r
44571         return this.el;\r
44572     },\r
44573 \r
44574     /**\r
44575      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.\r
44576      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.\r
44577      */\r
44578     getLoader : function(){\r
44579         return this.loader;\r
44580     },\r
44581 \r
44582     /**\r
44583      * Expand all nodes\r
44584      */\r
44585     expandAll : function(){\r
44586         this.root.expand(true);\r
44587     },\r
44588 \r
44589     /**\r
44590      * Collapse all nodes\r
44591      */\r
44592     collapseAll : function(){\r
44593         this.root.collapse(true);\r
44594     },\r
44595 \r
44596     /**\r
44597      * Returns the selection model used by this TreePanel.\r
44598      * @return {TreeSelectionModel} The selection model used by this TreePanel\r
44599      */\r
44600     getSelectionModel : function(){\r
44601         if(!this.selModel){\r
44602             this.selModel = new Ext.tree.DefaultSelectionModel();\r
44603         }\r
44604         return this.selModel;\r
44605     },\r
44606 \r
44607     /**\r
44608      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
44609      * @param {String} path\r
44610      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
44611      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with\r
44612      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.\r
44613      */\r
44614     expandPath : function(path, attr, callback){\r
44615         attr = attr || "id";\r
44616         var keys = path.split(this.pathSeparator);\r
44617         var curNode = this.root;\r
44618         if(curNode.attributes[attr] != keys[1]){ // invalid root\r
44619             if(callback){\r
44620                 callback(false, null);\r
44621             }\r
44622             return;\r
44623         }\r
44624         var index = 1;\r
44625         var f = function(){\r
44626             if(++index == keys.length){\r
44627                 if(callback){\r
44628                     callback(true, curNode);\r
44629                 }\r
44630                 return;\r
44631             }\r
44632             var c = curNode.findChild(attr, keys[index]);\r
44633             if(!c){\r
44634                 if(callback){\r
44635                     callback(false, curNode);\r
44636                 }\r
44637                 return;\r
44638             }\r
44639             curNode = c;\r
44640             c.expand(false, false, f);\r
44641         };\r
44642         curNode.expand(false, false, f);\r
44643     },\r
44644 \r
44645     /**\r
44646      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
44647      * @param {String} path\r
44648      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
44649      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with\r
44650      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.\r
44651      */\r
44652     selectPath : function(path, attr, callback){\r
44653         attr = attr || "id";\r
44654         var keys = path.split(this.pathSeparator);\r
44655         var v = keys.pop();\r
44656         if(keys.length > 0){\r
44657             var f = function(success, node){\r
44658                 if(success && node){\r
44659                     var n = node.findChild(attr, v);\r
44660                     if(n){\r
44661                         n.select();\r
44662                         if(callback){\r
44663                             callback(true, n);\r
44664                         }\r
44665                     }else if(callback){\r
44666                         callback(false, n);\r
44667                     }\r
44668                 }else{\r
44669                     if(callback){\r
44670                         callback(false, n);\r
44671                     }\r
44672                 }\r
44673             };\r
44674             this.expandPath(keys.join(this.pathSeparator), attr, f);\r
44675         }else{\r
44676             this.root.select();\r
44677             if(callback){\r
44678                 callback(true, this.root);\r
44679             }\r
44680         }\r
44681     },\r
44682 \r
44683     /**\r
44684      * Returns the underlying Element for this tree\r
44685      * @return {Ext.Element} The Element\r
44686      */\r
44687     getTreeEl : function(){\r
44688         return this.body;\r
44689     },\r
44690 \r
44691     // private\r
44692     onRender : function(ct, position){\r
44693         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);\r
44694         this.el.addClass('x-tree');\r
44695         this.innerCt = this.body.createChild({tag:"ul",\r
44696                cls:"x-tree-root-ct " +\r
44697                (this.useArrows ? 'x-tree-arrows' : this.lines ? "x-tree-lines" : "x-tree-no-lines")});\r
44698     },\r
44699 \r
44700     // private\r
44701     initEvents : function(){\r
44702         Ext.tree.TreePanel.superclass.initEvents.call(this);\r
44703 \r
44704         if(this.containerScroll){\r
44705             Ext.dd.ScrollManager.register(this.body);\r
44706         }\r
44707         if((this.enableDD || this.enableDrop) && !this.dropZone){\r
44708            /**\r
44709             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})\r
44710             * @property dropZone\r
44711             * @type Ext.tree.TreeDropZone\r
44712             */\r
44713              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {\r
44714                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true\r
44715            });\r
44716         }\r
44717         if((this.enableDD || this.enableDrag) && !this.dragZone){\r
44718            /**\r
44719             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})\r
44720             * @property dragZone\r
44721             * @type Ext.tree.TreeDragZone\r
44722             */\r
44723             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {\r
44724                ddGroup: this.ddGroup || "TreeDD",\r
44725                scroll: this.ddScroll\r
44726            });\r
44727         }\r
44728         this.getSelectionModel().init(this);\r
44729     },\r
44730 \r
44731     // private\r
44732     afterRender : function(){\r
44733         Ext.tree.TreePanel.superclass.afterRender.call(this);\r
44734         this.root.render();\r
44735         if(!this.rootVisible){\r
44736             this.root.renderChildren();\r
44737         }\r
44738     },\r
44739 \r
44740     onDestroy : function(){\r
44741         if(this.rendered){\r
44742             this.body.removeAllListeners();\r
44743             Ext.dd.ScrollManager.unregister(this.body);\r
44744             if(this.dropZone){\r
44745                 this.dropZone.unreg();\r
44746             }\r
44747             if(this.dragZone){\r
44748                this.dragZone.unreg();\r
44749             }\r
44750         }\r
44751         this.root.destroy();\r
44752         this.nodeHash = null;\r
44753         Ext.tree.TreePanel.superclass.onDestroy.call(this);\r
44754     }\r
44755 \r
44756     /**\r
44757      * @cfg {String/Number} activeItem\r
44758      * @hide\r
44759      */\r
44760     /**\r
44761      * @cfg {Boolean} autoDestroy\r
44762      * @hide\r
44763      */\r
44764     /**\r
44765      * @cfg {Object/String/Function} autoLoad\r
44766      * @hide\r
44767      */\r
44768     /**\r
44769      * @cfg {Boolean} autoWidth\r
44770      * @hide\r
44771      */\r
44772     /**\r
44773      * @cfg {Boolean/Number} bufferResize\r
44774      * @hide\r
44775      */\r
44776     /**\r
44777      * @cfg {String} defaultType\r
44778      * @hide\r
44779      */\r
44780     /**\r
44781      * @cfg {Object} defaults\r
44782      * @hide\r
44783      */\r
44784     /**\r
44785      * @cfg {Boolean} hideBorders\r
44786      * @hide\r
44787      */\r
44788     /**\r
44789      * @cfg {Mixed} items\r
44790      * @hide\r
44791      */\r
44792     /**\r
44793      * @cfg {String} layout\r
44794      * @hide\r
44795      */\r
44796     /**\r
44797      * @cfg {Object} layoutConfig\r
44798      * @hide\r
44799      */\r
44800     /**\r
44801      * @cfg {Boolean} monitorResize\r
44802      * @hide\r
44803      */\r
44804     /**\r
44805      * @property items\r
44806      * @hide\r
44807      */\r
44808     /**\r
44809      * @method cascade\r
44810      * @hide\r
44811      */\r
44812     /**\r
44813      * @method doLayout\r
44814      * @hide\r
44815      */\r
44816     /**\r
44817      * @method find\r
44818      * @hide\r
44819      */\r
44820     /**\r
44821      * @method findBy\r
44822      * @hide\r
44823      */\r
44824     /**\r
44825      * @method findById\r
44826      * @hide\r
44827      */\r
44828     /**\r
44829      * @method findByType\r
44830      * @hide\r
44831      */\r
44832     /**\r
44833      * @method getComponent\r
44834      * @hide\r
44835      */\r
44836     /**\r
44837      * @method getLayout\r
44838      * @hide\r
44839      */\r
44840     /**\r
44841      * @method getUpdater\r
44842      * @hide\r
44843      */\r
44844     /**\r
44845      * @method insert\r
44846      * @hide\r
44847      */\r
44848     /**\r
44849      * @method load\r
44850      * @hide\r
44851      */\r
44852     /**\r
44853      * @method remove\r
44854      * @hide\r
44855      */\r
44856     /**\r
44857      * @event add\r
44858      * @hide\r
44859      */\r
44860     /**\r
44861      * @method removeAll\r
44862      * @hide\r
44863      */\r
44864     /**\r
44865      * @event afterLayout\r
44866      * @hide\r
44867      */\r
44868     /**\r
44869      * @event beforeadd\r
44870      * @hide\r
44871      */\r
44872     /**\r
44873      * @event beforeremove\r
44874      * @hide\r
44875      */\r
44876     /**\r
44877      * @event remove\r
44878      * @hide\r
44879      */\r
44880 \r
44881 \r
44882 \r
44883     /**\r
44884      * @cfg {String} allowDomMove  @hide\r
44885      */\r
44886     /**\r
44887      * @cfg {String} autoEl @hide\r
44888      */\r
44889     /**\r
44890      * @cfg {String} applyTo  @hide\r
44891      */\r
44892     /**\r
44893      * @cfg {String} contentEl  @hide\r
44894      */\r
44895     /**\r
44896      * @cfg {String} disabledClass  @hide\r
44897      */\r
44898     /**\r
44899      * @cfg {String} elements  @hide\r
44900      */\r
44901     /**\r
44902      * @cfg {String} html  @hide\r
44903      */\r
44904     /**\r
44905      * @cfg {Boolean} preventBodyReset\r
44906      * @hide\r
44907      */\r
44908     /**\r
44909      * @property disabled\r
44910      * @hide\r
44911      */\r
44912     /**\r
44913      * @method applyToMarkup\r
44914      * @hide\r
44915      */\r
44916     /**\r
44917      * @method enable\r
44918      * @hide\r
44919      */\r
44920     /**\r
44921      * @method disable\r
44922      * @hide\r
44923      */\r
44924     /**\r
44925      * @method setDisabled\r
44926      * @hide\r
44927      */\r
44928 });\r
44929 \r
44930 Ext.tree.TreePanel.nodeTypes = {};\r
44931 \r
44932 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){\r
44933     this.tree = tree;\r
44934     this.tree.on('render', this.initEvents, this);\r
44935 }\r
44936 \r
44937 Ext.tree.TreeEventModel.prototype = {\r
44938     initEvents : function(){\r
44939         var el = this.tree.getTreeEl();\r
44940         el.on('click', this.delegateClick, this);\r
44941         if(this.tree.trackMouseOver !== false){\r
44942             this.tree.innerCt.on('mouseover', this.delegateOver, this);\r
44943             this.tree.innerCt.on('mouseout', this.delegateOut, this);\r
44944         }\r
44945         el.on('dblclick', this.delegateDblClick, this);\r
44946         el.on('contextmenu', this.delegateContextMenu, this);\r
44947     },\r
44948 \r
44949     getNode : function(e){\r
44950         var t;\r
44951         if(t = e.getTarget('.x-tree-node-el', 10)){\r
44952             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');\r
44953             if(id){\r
44954                 return this.tree.getNodeById(id);\r
44955             }\r
44956         }\r
44957         return null;\r
44958     },\r
44959 \r
44960     getNodeTarget : function(e){\r
44961         var t = e.getTarget('.x-tree-node-icon', 1);\r
44962         if(!t){\r
44963             t = e.getTarget('.x-tree-node-el', 6);\r
44964         }\r
44965         return t;\r
44966     },\r
44967 \r
44968     delegateOut : function(e, t){\r
44969         if(!this.beforeEvent(e)){\r
44970             return;\r
44971         }\r
44972         if(e.getTarget('.x-tree-ec-icon', 1)){\r
44973             var n = this.getNode(e);\r
44974             this.onIconOut(e, n);\r
44975             if(n == this.lastEcOver){\r
44976                 delete this.lastEcOver;\r
44977             }\r
44978         }\r
44979         if((t = this.getNodeTarget(e)) && !e.within(t, true)){\r
44980             this.onNodeOut(e, this.getNode(e));\r
44981         }\r
44982     },\r
44983 \r
44984     delegateOver : function(e, t){\r
44985         if(!this.beforeEvent(e)){\r
44986             return;\r
44987         }\r
44988         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF\r
44989             Ext.getBody().on('mouseover', this.trackExit, this);\r
44990             this.trackingDoc = true;\r
44991         }\r
44992         if(this.lastEcOver){ // prevent hung highlight\r
44993             this.onIconOut(e, this.lastEcOver);\r
44994             delete this.lastEcOver;\r
44995         }\r
44996         if(e.getTarget('.x-tree-ec-icon', 1)){\r
44997             this.lastEcOver = this.getNode(e);\r
44998             this.onIconOver(e, this.lastEcOver);\r
44999         }\r
45000         if(t = this.getNodeTarget(e)){\r
45001             this.onNodeOver(e, this.getNode(e));\r
45002         }\r
45003     },\r
45004 \r
45005     trackExit : function(e){\r
45006         if(this.lastOverNode && !e.within(this.lastOverNode.ui.getEl())){\r
45007             this.onNodeOut(e, this.lastOverNode);\r
45008             delete this.lastOverNode;\r
45009             Ext.getBody().un('mouseover', this.trackExit, this);\r
45010             this.trackingDoc = false;\r
45011         }\r
45012     },\r
45013 \r
45014     delegateClick : function(e, t){\r
45015         if(!this.beforeEvent(e)){\r
45016             return;\r
45017         }\r
45018 \r
45019         if(e.getTarget('input[type=checkbox]', 1)){\r
45020             this.onCheckboxClick(e, this.getNode(e));\r
45021         }\r
45022         else if(e.getTarget('.x-tree-ec-icon', 1)){\r
45023             this.onIconClick(e, this.getNode(e));\r
45024         }\r
45025         else if(this.getNodeTarget(e)){\r
45026             this.onNodeClick(e, this.getNode(e));\r
45027         }\r
45028     },\r
45029 \r
45030     delegateDblClick : function(e, t){\r
45031         if(this.beforeEvent(e) && this.getNodeTarget(e)){\r
45032             this.onNodeDblClick(e, this.getNode(e));\r
45033         }\r
45034     },\r
45035 \r
45036     delegateContextMenu : function(e, t){\r
45037         if(this.beforeEvent(e) && this.getNodeTarget(e)){\r
45038             this.onNodeContextMenu(e, this.getNode(e));\r
45039         }\r
45040     },\r
45041 \r
45042     onNodeClick : function(e, node){\r
45043         node.ui.onClick(e);\r
45044     },\r
45045 \r
45046     onNodeOver : function(e, node){\r
45047         this.lastOverNode = node;\r
45048         node.ui.onOver(e);\r
45049     },\r
45050 \r
45051     onNodeOut : function(e, node){\r
45052         node.ui.onOut(e);\r
45053     },\r
45054 \r
45055     onIconOver : function(e, node){\r
45056         node.ui.addClass('x-tree-ec-over');\r
45057     },\r
45058 \r
45059     onIconOut : function(e, node){\r
45060         node.ui.removeClass('x-tree-ec-over');\r
45061     },\r
45062 \r
45063     onIconClick : function(e, node){\r
45064         node.ui.ecClick(e);\r
45065     },\r
45066 \r
45067     onCheckboxClick : function(e, node){\r
45068         node.ui.onCheckChange(e);\r
45069     },\r
45070 \r
45071     onNodeDblClick : function(e, node){\r
45072         node.ui.onDblClick(e);\r
45073     },\r
45074 \r
45075     onNodeContextMenu : function(e, node){\r
45076         node.ui.onContextMenu(e);\r
45077     },\r
45078 \r
45079     beforeEvent : function(e){\r
45080         if(this.disabled){\r
45081             e.stopEvent();\r
45082             return false;\r
45083         }\r
45084         return true;\r
45085     },\r
45086 \r
45087     disable: function(){\r
45088         this.disabled = true;\r
45089     },\r
45090 \r
45091     enable: function(){\r
45092         this.disabled = false;\r
45093     }\r
45094 };/**\r
45095  * @class Ext.tree.DefaultSelectionModel\r
45096  * @extends Ext.util.Observable\r
45097  * The default single selection for a TreePanel.\r
45098  */\r
45099 Ext.tree.DefaultSelectionModel = function(config){\r
45100    this.selNode = null;\r
45101    \r
45102    this.addEvents(\r
45103        /**\r
45104         * @event selectionchange\r
45105         * Fires when the selected node changes\r
45106         * @param {DefaultSelectionModel} this\r
45107         * @param {TreeNode} node the new selection\r
45108         */\r
45109        "selectionchange",\r
45110 \r
45111        /**\r
45112         * @event beforeselect\r
45113         * Fires before the selected node changes, return false to cancel the change\r
45114         * @param {DefaultSelectionModel} this\r
45115         * @param {TreeNode} node the new selection\r
45116         * @param {TreeNode} node the old selection\r
45117         */\r
45118        "beforeselect"\r
45119    );\r
45120 \r
45121     Ext.apply(this, config);\r
45122     Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);\r
45123 };\r
45124 \r
45125 Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, {\r
45126     init : function(tree){\r
45127         this.tree = tree;\r
45128         tree.getTreeEl().on("keydown", this.onKeyDown, this);\r
45129         tree.on("click", this.onNodeClick, this);\r
45130     },\r
45131     \r
45132     onNodeClick : function(node, e){\r
45133         this.select(node);\r
45134     },\r
45135     \r
45136     /**\r
45137      * Select a node.\r
45138      * @param {TreeNode} node The node to select\r
45139      * @return {TreeNode} The selected node\r
45140      */\r
45141     select : function(node){\r
45142         var last = this.selNode;\r
45143         if(node == last){\r
45144             node.ui.onSelectedChange(true);\r
45145         }else if(this.fireEvent('beforeselect', this, node, last) !== false){\r
45146             if(last){\r
45147                 last.ui.onSelectedChange(false);\r
45148             }\r
45149             this.selNode = node;\r
45150             node.ui.onSelectedChange(true);\r
45151             this.fireEvent("selectionchange", this, node, last);\r
45152         }\r
45153         return node;\r
45154     },\r
45155     \r
45156     /**\r
45157      * Deselect a node.\r
45158      * @param {TreeNode} node The node to unselect\r
45159      */\r
45160     unselect : function(node){\r
45161         if(this.selNode == node){\r
45162             this.clearSelections();\r
45163         }    \r
45164     },\r
45165     \r
45166     /**\r
45167      * Clear all selections\r
45168      */\r
45169     clearSelections : function(){\r
45170         var n = this.selNode;\r
45171         if(n){\r
45172             n.ui.onSelectedChange(false);\r
45173             this.selNode = null;\r
45174             this.fireEvent("selectionchange", this, null);\r
45175         }\r
45176         return n;\r
45177     },\r
45178     \r
45179     /**\r
45180      * Get the selected node\r
45181      * @return {TreeNode} The selected node\r
45182      */\r
45183     getSelectedNode : function(){\r
45184         return this.selNode;    \r
45185     },\r
45186     \r
45187     /**\r
45188      * Returns true if the node is selected\r
45189      * @param {TreeNode} node The node to check\r
45190      * @return {Boolean}\r
45191      */\r
45192     isSelected : function(node){\r
45193         return this.selNode == node;  \r
45194     },\r
45195 \r
45196     /**\r
45197      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
45198      * @return TreeNode The new selection\r
45199      */\r
45200     selectPrevious : function(){\r
45201         var s = this.selNode || this.lastSelNode;\r
45202         if(!s){\r
45203             return null;\r
45204         }\r
45205         var ps = s.previousSibling;\r
45206         if(ps){\r
45207             if(!ps.isExpanded() || ps.childNodes.length < 1){\r
45208                 return this.select(ps);\r
45209             } else{\r
45210                 var lc = ps.lastChild;\r
45211                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){\r
45212                     lc = lc.lastChild;\r
45213                 }\r
45214                 return this.select(lc);\r
45215             }\r
45216         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
45217             return this.select(s.parentNode);\r
45218         }\r
45219         return null;\r
45220     },\r
45221 \r
45222     /**\r
45223      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
45224      * @return TreeNode The new selection\r
45225      */\r
45226     selectNext : function(){\r
45227         var s = this.selNode || this.lastSelNode;\r
45228         if(!s){\r
45229             return null;\r
45230         }\r
45231         if(s.firstChild && s.isExpanded()){\r
45232              return this.select(s.firstChild);\r
45233          }else if(s.nextSibling){\r
45234              return this.select(s.nextSibling);\r
45235          }else if(s.parentNode){\r
45236             var newS = null;\r
45237             s.parentNode.bubble(function(){\r
45238                 if(this.nextSibling){\r
45239                     newS = this.getOwnerTree().selModel.select(this.nextSibling);\r
45240                     return false;\r
45241                 }\r
45242             });\r
45243             return newS;\r
45244          }\r
45245         return null;\r
45246     },\r
45247 \r
45248     onKeyDown : function(e){\r
45249         var s = this.selNode || this.lastSelNode;\r
45250         // undesirable, but required\r
45251         var sm = this;\r
45252         if(!s){\r
45253             return;\r
45254         }\r
45255         var k = e.getKey();\r
45256         switch(k){\r
45257              case e.DOWN:\r
45258                  e.stopEvent();\r
45259                  this.selectNext();\r
45260              break;\r
45261              case e.UP:\r
45262                  e.stopEvent();\r
45263                  this.selectPrevious();\r
45264              break;\r
45265              case e.RIGHT:\r
45266                  e.preventDefault();\r
45267                  if(s.hasChildNodes()){\r
45268                      if(!s.isExpanded()){\r
45269                          s.expand();\r
45270                      }else if(s.firstChild){\r
45271                          this.select(s.firstChild, e);\r
45272                      }\r
45273                  }\r
45274              break;\r
45275              case e.LEFT:\r
45276                  e.preventDefault();\r
45277                  if(s.hasChildNodes() && s.isExpanded()){\r
45278                      s.collapse();\r
45279                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
45280                      this.select(s.parentNode, e);\r
45281                  }\r
45282              break;\r
45283         };\r
45284     }\r
45285 });\r
45286 \r
45287 /**\r
45288  * @class Ext.tree.MultiSelectionModel\r
45289  * @extends Ext.util.Observable\r
45290  * Multi selection for a TreePanel.\r
45291  */\r
45292 Ext.tree.MultiSelectionModel = function(config){\r
45293    this.selNodes = [];\r
45294    this.selMap = {};\r
45295    this.addEvents(\r
45296        /**\r
45297         * @event selectionchange\r
45298         * Fires when the selected nodes change\r
45299         * @param {MultiSelectionModel} this\r
45300         * @param {Array} nodes Array of the selected nodes\r
45301         */\r
45302        "selectionchange"\r
45303    );\r
45304     Ext.apply(this, config);\r
45305     Ext.tree.MultiSelectionModel.superclass.constructor.call(this);\r
45306 };\r
45307 \r
45308 Ext.extend(Ext.tree.MultiSelectionModel, Ext.util.Observable, {\r
45309     init : function(tree){\r
45310         this.tree = tree;\r
45311         tree.getTreeEl().on("keydown", this.onKeyDown, this);\r
45312         tree.on("click", this.onNodeClick, this);\r
45313     },\r
45314     \r
45315     onNodeClick : function(node, e){\r
45316         if(e.ctrlKey && this.isSelected(node)){\r
45317             this.unselect(node);\r
45318         }else{\r
45319             this.select(node, e, e.ctrlKey);\r
45320         }\r
45321     },\r
45322     \r
45323     /**\r
45324      * Select a node.\r
45325      * @param {TreeNode} node The node to select\r
45326      * @param {EventObject} e (optional) An event associated with the selection\r
45327      * @param {Boolean} keepExisting True to retain existing selections\r
45328      * @return {TreeNode} The selected node\r
45329      */\r
45330     select : function(node, e, keepExisting){\r
45331         if(keepExisting !== true){\r
45332             this.clearSelections(true);\r
45333         }\r
45334         if(this.isSelected(node)){\r
45335             this.lastSelNode = node;\r
45336             return node;\r
45337         }\r
45338         this.selNodes.push(node);\r
45339         this.selMap[node.id] = node;\r
45340         this.lastSelNode = node;\r
45341         node.ui.onSelectedChange(true);\r
45342         this.fireEvent("selectionchange", this, this.selNodes);\r
45343         return node;\r
45344     },\r
45345     \r
45346     /**\r
45347      * Deselect a node.\r
45348      * @param {TreeNode} node The node to unselect\r
45349      */\r
45350     unselect : function(node){\r
45351         if(this.selMap[node.id]){\r
45352             node.ui.onSelectedChange(false);\r
45353             var sn = this.selNodes;\r
45354             var index = sn.indexOf(node);\r
45355             if(index != -1){\r
45356                 this.selNodes.splice(index, 1);\r
45357             }\r
45358             delete this.selMap[node.id];\r
45359             this.fireEvent("selectionchange", this, this.selNodes);\r
45360         }\r
45361     },\r
45362     \r
45363     /**\r
45364      * Clear all selections\r
45365      */\r
45366     clearSelections : function(suppressEvent){\r
45367         var sn = this.selNodes;\r
45368         if(sn.length > 0){\r
45369             for(var i = 0, len = sn.length; i < len; i++){\r
45370                 sn[i].ui.onSelectedChange(false);\r
45371             }\r
45372             this.selNodes = [];\r
45373             this.selMap = {};\r
45374             if(suppressEvent !== true){\r
45375                 this.fireEvent("selectionchange", this, this.selNodes);\r
45376             }\r
45377         }\r
45378     },\r
45379     \r
45380     /**\r
45381      * Returns true if the node is selected\r
45382      * @param {TreeNode} node The node to check\r
45383      * @return {Boolean}\r
45384      */\r
45385     isSelected : function(node){\r
45386         return this.selMap[node.id] ? true : false;  \r
45387     },\r
45388     \r
45389     /**\r
45390      * Returns an array of the selected nodes\r
45391      * @return {Array}\r
45392      */\r
45393     getSelectedNodes : function(){\r
45394         return this.selNodes;    \r
45395     },\r
45396 \r
45397     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,\r
45398 \r
45399     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,\r
45400 \r
45401     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious\r
45402 });/**\r
45403  * @class Ext.data.Tree\r
45404  * @extends Ext.util.Observable\r
45405  * Represents a tree data structure and bubbles all the events for its nodes. The nodes\r
45406  * in the tree have most standard DOM functionality.\r
45407  * @constructor\r
45408  * @param {Node} root (optional) The root node\r
45409  */\r
45410 Ext.data.Tree = function(root){\r
45411    this.nodeHash = {};\r
45412    /**\r
45413     * The root node for this tree\r
45414     * @type Node\r
45415     */\r
45416    this.root = null;\r
45417    if(root){\r
45418        this.setRootNode(root);\r
45419    }\r
45420    this.addEvents(\r
45421        /**\r
45422         * @event append\r
45423         * Fires when a new child node is appended to a node in this tree.\r
45424         * @param {Tree} tree The owner tree\r
45425         * @param {Node} parent The parent node\r
45426         * @param {Node} node The newly appended node\r
45427         * @param {Number} index The index of the newly appended node\r
45428         */\r
45429        "append",\r
45430        /**\r
45431         * @event remove\r
45432         * Fires when a child node is removed from a node in this tree.\r
45433         * @param {Tree} tree The owner tree\r
45434         * @param {Node} parent The parent node\r
45435         * @param {Node} node The child node removed\r
45436         */\r
45437        "remove",\r
45438        /**\r
45439         * @event move\r
45440         * Fires when a node is moved to a new location in the tree\r
45441         * @param {Tree} tree The owner tree\r
45442         * @param {Node} node The node moved\r
45443         * @param {Node} oldParent The old parent of this node\r
45444         * @param {Node} newParent The new parent of this node\r
45445         * @param {Number} index The index it was moved to\r
45446         */\r
45447        "move",\r
45448        /**\r
45449         * @event insert\r
45450         * Fires when a new child node is inserted in a node in this tree.\r
45451         * @param {Tree} tree The owner tree\r
45452         * @param {Node} parent The parent node\r
45453         * @param {Node} node The child node inserted\r
45454         * @param {Node} refNode The child node the node was inserted before\r
45455         */\r
45456        "insert",\r
45457        /**\r
45458         * @event beforeappend\r
45459         * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
45460         * @param {Tree} tree The owner tree\r
45461         * @param {Node} parent The parent node\r
45462         * @param {Node} node The child node to be appended\r
45463         */\r
45464        "beforeappend",\r
45465        /**\r
45466         * @event beforeremove\r
45467         * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
45468         * @param {Tree} tree The owner tree\r
45469         * @param {Node} parent The parent node\r
45470         * @param {Node} node The child node to be removed\r
45471         */\r
45472        "beforeremove",\r
45473        /**\r
45474         * @event beforemove\r
45475         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
45476         * @param {Tree} tree The owner tree\r
45477         * @param {Node} node The node being moved\r
45478         * @param {Node} oldParent The parent of the node\r
45479         * @param {Node} newParent The new parent the node is moving to\r
45480         * @param {Number} index The index it is being moved to\r
45481         */\r
45482        "beforemove",\r
45483        /**\r
45484         * @event beforeinsert\r
45485         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
45486         * @param {Tree} tree The owner tree\r
45487         * @param {Node} parent The parent node\r
45488         * @param {Node} node The child node to be inserted\r
45489         * @param {Node} refNode The child node the node is being inserted before\r
45490         */\r
45491        "beforeinsert"\r
45492    );\r
45493 \r
45494     Ext.data.Tree.superclass.constructor.call(this);\r
45495 };\r
45496 \r
45497 Ext.extend(Ext.data.Tree, Ext.util.Observable, {\r
45498     /**\r
45499      * @cfg {String} pathSeparator\r
45500      * The token used to separate paths in node ids (defaults to '/').\r
45501      */\r
45502     pathSeparator: "/",\r
45503 \r
45504     // private\r
45505     proxyNodeEvent : function(){\r
45506         return this.fireEvent.apply(this, arguments);\r
45507     },\r
45508 \r
45509     /**\r
45510      * Returns the root node for this tree.\r
45511      * @return {Node}\r
45512      */\r
45513     getRootNode : function(){\r
45514         return this.root;\r
45515     },\r
45516 \r
45517     /**\r
45518      * Sets the root node for this tree.\r
45519      * @param {Node} node\r
45520      * @return {Node}\r
45521      */\r
45522     setRootNode : function(node){\r
45523         this.root = node;\r
45524         node.ownerTree = this;\r
45525         node.isRoot = true;\r
45526         this.registerNode(node);\r
45527         return node;\r
45528     },\r
45529 \r
45530     /**\r
45531      * Gets a node in this tree by its id.\r
45532      * @param {String} id\r
45533      * @return {Node}\r
45534      */\r
45535     getNodeById : function(id){\r
45536         return this.nodeHash[id];\r
45537     },\r
45538 \r
45539     // private\r
45540     registerNode : function(node){\r
45541         this.nodeHash[node.id] = node;\r
45542     },\r
45543 \r
45544     // private\r
45545     unregisterNode : function(node){\r
45546         delete this.nodeHash[node.id];\r
45547     },\r
45548 \r
45549     toString : function(){\r
45550         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
45551     }\r
45552 });\r
45553 \r
45554 /**\r
45555  * @class Ext.data.Node\r
45556  * @extends Ext.util.Observable\r
45557  * @cfg {Boolean} leaf true if this node is a leaf and does not have children\r
45558  * @cfg {String} id The id for this node. If one is not specified, one is generated.\r
45559  * @constructor\r
45560  * @param {Object} attributes The attributes/config for the node\r
45561  */\r
45562 Ext.data.Node = function(attributes){\r
45563     /**\r
45564      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.\r
45565      * @type {Object}\r
45566      */\r
45567     this.attributes = attributes || {};\r
45568     this.leaf = this.attributes.leaf;\r
45569     /**\r
45570      * The node id. @type String\r
45571      */\r
45572     this.id = this.attributes.id;\r
45573     if(!this.id){\r
45574         this.id = Ext.id(null, "xnode-");\r
45575         this.attributes.id = this.id;\r
45576     }\r
45577     /**\r
45578      * All child nodes of this node. @type Array\r
45579      */\r
45580     this.childNodes = [];\r
45581     if(!this.childNodes.indexOf){ // indexOf is a must\r
45582         this.childNodes.indexOf = function(o){\r
45583             for(var i = 0, len = this.length; i < len; i++){\r
45584                 if(this[i] == o){\r
45585                     return i;\r
45586                 }\r
45587             }\r
45588             return -1;\r
45589         };\r
45590     }\r
45591     /**\r
45592      * The parent node for this node. @type Node\r
45593      */\r
45594     this.parentNode = null;\r
45595     /**\r
45596      * The first direct child node of this node, or null if this node has no child nodes. @type Node\r
45597      */\r
45598     this.firstChild = null;\r
45599     /**\r
45600      * The last direct child node of this node, or null if this node has no child nodes. @type Node\r
45601      */\r
45602     this.lastChild = null;\r
45603     /**\r
45604      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node\r
45605      */\r
45606     this.previousSibling = null;\r
45607     /**\r
45608      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node\r
45609      */\r
45610     this.nextSibling = null;\r
45611 \r
45612     this.addEvents({\r
45613        /**\r
45614         * @event append\r
45615         * Fires when a new child node is appended\r
45616         * @param {Tree} tree The owner tree\r
45617         * @param {Node} this This node\r
45618         * @param {Node} node The newly appended node\r
45619         * @param {Number} index The index of the newly appended node\r
45620         */\r
45621        "append" : true,\r
45622        /**\r
45623         * @event remove\r
45624         * Fires when a child node is removed\r
45625         * @param {Tree} tree The owner tree\r
45626         * @param {Node} this This node\r
45627         * @param {Node} node The removed node\r
45628         */\r
45629        "remove" : true,\r
45630        /**\r
45631         * @event move\r
45632         * Fires when this node is moved to a new location in the tree\r
45633         * @param {Tree} tree The owner tree\r
45634         * @param {Node} this This node\r
45635         * @param {Node} oldParent The old parent of this node\r
45636         * @param {Node} newParent The new parent of this node\r
45637         * @param {Number} index The index it was moved to\r
45638         */\r
45639        "move" : true,\r
45640        /**\r
45641         * @event insert\r
45642         * Fires when a new child node is inserted.\r
45643         * @param {Tree} tree The owner tree\r
45644         * @param {Node} this This node\r
45645         * @param {Node} node The child node inserted\r
45646         * @param {Node} refNode The child node the node was inserted before\r
45647         */\r
45648        "insert" : true,\r
45649        /**\r
45650         * @event beforeappend\r
45651         * Fires before a new child is appended, return false to cancel the append.\r
45652         * @param {Tree} tree The owner tree\r
45653         * @param {Node} this This node\r
45654         * @param {Node} node The child node to be appended\r
45655         */\r
45656        "beforeappend" : true,\r
45657        /**\r
45658         * @event beforeremove\r
45659         * Fires before a child is removed, return false to cancel the remove.\r
45660         * @param {Tree} tree The owner tree\r
45661         * @param {Node} this This node\r
45662         * @param {Node} node The child node to be removed\r
45663         */\r
45664        "beforeremove" : true,\r
45665        /**\r
45666         * @event beforemove\r
45667         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.\r
45668         * @param {Tree} tree The owner tree\r
45669         * @param {Node} this This node\r
45670         * @param {Node} oldParent The parent of this node\r
45671         * @param {Node} newParent The new parent this node is moving to\r
45672         * @param {Number} index The index it is being moved to\r
45673         */\r
45674        "beforemove" : true,\r
45675        /**\r
45676         * @event beforeinsert\r
45677         * Fires before a new child is inserted, return false to cancel the insert.\r
45678         * @param {Tree} tree The owner tree\r
45679         * @param {Node} this This node\r
45680         * @param {Node} node The child node to be inserted\r
45681         * @param {Node} refNode The child node the node is being inserted before\r
45682         */\r
45683        "beforeinsert" : true\r
45684    });\r
45685     this.listeners = this.attributes.listeners;\r
45686     Ext.data.Node.superclass.constructor.call(this);\r
45687 };\r
45688 \r
45689 Ext.extend(Ext.data.Node, Ext.util.Observable, {\r
45690     // private\r
45691     fireEvent : function(evtName){\r
45692         // first do standard event for this node\r
45693         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){\r
45694             return false;\r
45695         }\r
45696         // then bubble it up to the tree if the event wasn't cancelled\r
45697         var ot = this.getOwnerTree();\r
45698         if(ot){\r
45699             if(ot.proxyNodeEvent.apply(ot, arguments) === false){\r
45700                 return false;\r
45701             }\r
45702         }\r
45703         return true;\r
45704     },\r
45705 \r
45706     /**\r
45707      * Returns true if this node is a leaf\r
45708      * @return {Boolean}\r
45709      */\r
45710     isLeaf : function(){\r
45711         return this.leaf === true;\r
45712     },\r
45713 \r
45714     // private\r
45715     setFirstChild : function(node){\r
45716         this.firstChild = node;\r
45717     },\r
45718 \r
45719     //private\r
45720     setLastChild : function(node){\r
45721         this.lastChild = node;\r
45722     },\r
45723 \r
45724 \r
45725     /**\r
45726      * Returns true if this node is the last child of its parent\r
45727      * @return {Boolean}\r
45728      */\r
45729     isLast : function(){\r
45730        return (!this.parentNode ? true : this.parentNode.lastChild == this);\r
45731     },\r
45732 \r
45733     /**\r
45734      * Returns true if this node is the first child of its parent\r
45735      * @return {Boolean}\r
45736      */\r
45737     isFirst : function(){\r
45738        return (!this.parentNode ? true : this.parentNode.firstChild == this);\r
45739     },\r
45740 \r
45741     /**\r
45742      * Returns true if this node has one or more child nodes, else false.\r
45743      * @return {Boolean}\r
45744      */\r
45745     hasChildNodes : function(){\r
45746         return !this.isLeaf() && this.childNodes.length > 0;\r
45747     },\r
45748     \r
45749     /**\r
45750      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>\r
45751      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.\r
45752      * @return {Boolean}\r
45753      */\r
45754     isExpandable : function(){\r
45755         return this.attributes.expandable || this.hasChildNodes();\r
45756     },\r
45757 \r
45758     /**\r
45759      * Insert node(s) as the last child node of this node.\r
45760      * @param {Node/Array} node The node or Array of nodes to append\r
45761      * @return {Node} The appended node if single append, or null if an array was passed\r
45762      */\r
45763     appendChild : function(node){\r
45764         var multi = false;\r
45765         if(Ext.isArray(node)){\r
45766             multi = node;\r
45767         }else if(arguments.length > 1){\r
45768             multi = arguments;\r
45769         }\r
45770         // if passed an array or multiple args do them one by one\r
45771         if(multi){\r
45772             for(var i = 0, len = multi.length; i < len; i++) {\r
45773                 this.appendChild(multi[i]);\r
45774             }\r
45775         }else{\r
45776             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){\r
45777                 return false;\r
45778             }\r
45779             var index = this.childNodes.length;\r
45780             var oldParent = node.parentNode;\r
45781             // it's a move, make sure we move it cleanly\r
45782             if(oldParent){\r
45783                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){\r
45784                     return false;\r
45785                 }\r
45786                 oldParent.removeChild(node);\r
45787             }\r
45788             index = this.childNodes.length;\r
45789             if(index === 0){\r
45790                 this.setFirstChild(node);\r
45791             }\r
45792             this.childNodes.push(node);\r
45793             node.parentNode = this;\r
45794             var ps = this.childNodes[index-1];\r
45795             if(ps){\r
45796                 node.previousSibling = ps;\r
45797                 ps.nextSibling = node;\r
45798             }else{\r
45799                 node.previousSibling = null;\r
45800             }\r
45801             node.nextSibling = null;\r
45802             this.setLastChild(node);\r
45803             node.setOwnerTree(this.getOwnerTree());\r
45804             this.fireEvent("append", this.ownerTree, this, node, index);\r
45805             if(oldParent){\r
45806                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);\r
45807             }\r
45808             return node;\r
45809         }\r
45810     },\r
45811 \r
45812     /**\r
45813      * Removes a child node from this node.\r
45814      * @param {Node} node The node to remove\r
45815      * @return {Node} The removed node\r
45816      */\r
45817     removeChild : function(node){\r
45818         var index = this.childNodes.indexOf(node);\r
45819         if(index == -1){\r
45820             return false;\r
45821         }\r
45822         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){\r
45823             return false;\r
45824         }\r
45825 \r
45826         // remove it from childNodes collection\r
45827         this.childNodes.splice(index, 1);\r
45828 \r
45829         // update siblings\r
45830         if(node.previousSibling){\r
45831             node.previousSibling.nextSibling = node.nextSibling;\r
45832         }\r
45833         if(node.nextSibling){\r
45834             node.nextSibling.previousSibling = node.previousSibling;\r
45835         }\r
45836 \r
45837         // update child refs\r
45838         if(this.firstChild == node){\r
45839             this.setFirstChild(node.nextSibling);\r
45840         }\r
45841         if(this.lastChild == node){\r
45842             this.setLastChild(node.previousSibling);\r
45843         }\r
45844 \r
45845         node.setOwnerTree(null);\r
45846         // clear any references from the node\r
45847         node.parentNode = null;\r
45848         node.previousSibling = null;\r
45849         node.nextSibling = null;\r
45850         this.fireEvent("remove", this.ownerTree, this, node);\r
45851         return node;\r
45852     },\r
45853 \r
45854     /**\r
45855      * Inserts the first node before the second node in this nodes childNodes collection.\r
45856      * @param {Node} node The node to insert\r
45857      * @param {Node} refNode The node to insert before (if null the node is appended)\r
45858      * @return {Node} The inserted node\r
45859      */\r
45860     insertBefore : function(node, refNode){\r
45861         if(!refNode){ // like standard Dom, refNode can be null for append\r
45862             return this.appendChild(node);\r
45863         }\r
45864         // nothing to do\r
45865         if(node == refNode){\r
45866             return false;\r
45867         }\r
45868 \r
45869         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){\r
45870             return false;\r
45871         }\r
45872         var index = this.childNodes.indexOf(refNode);\r
45873         var oldParent = node.parentNode;\r
45874         var refIndex = index;\r
45875 \r
45876         // when moving internally, indexes will change after remove\r
45877         if(oldParent == this && this.childNodes.indexOf(node) < index){\r
45878             refIndex--;\r
45879         }\r
45880 \r
45881         // it's a move, make sure we move it cleanly\r
45882         if(oldParent){\r
45883             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){\r
45884                 return false;\r
45885             }\r
45886             oldParent.removeChild(node);\r
45887         }\r
45888         if(refIndex === 0){\r
45889             this.setFirstChild(node);\r
45890         }\r
45891         this.childNodes.splice(refIndex, 0, node);\r
45892         node.parentNode = this;\r
45893         var ps = this.childNodes[refIndex-1];\r
45894         if(ps){\r
45895             node.previousSibling = ps;\r
45896             ps.nextSibling = node;\r
45897         }else{\r
45898             node.previousSibling = null;\r
45899         }\r
45900         node.nextSibling = refNode;\r
45901         refNode.previousSibling = node;\r
45902         node.setOwnerTree(this.getOwnerTree());\r
45903         this.fireEvent("insert", this.ownerTree, this, node, refNode);\r
45904         if(oldParent){\r
45905             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);\r
45906         }\r
45907         return node;\r
45908     },\r
45909 \r
45910     /**\r
45911      * Removes this node from its parent\r
45912      * @return {Node} this\r
45913      */\r
45914     remove : function(){\r
45915         this.parentNode.removeChild(this);\r
45916         return this;\r
45917     },\r
45918 \r
45919     /**\r
45920      * Returns the child node at the specified index.\r
45921      * @param {Number} index\r
45922      * @return {Node}\r
45923      */\r
45924     item : function(index){\r
45925         return this.childNodes[index];\r
45926     },\r
45927 \r
45928     /**\r
45929      * Replaces one child node in this node with another.\r
45930      * @param {Node} newChild The replacement node\r
45931      * @param {Node} oldChild The node to replace\r
45932      * @return {Node} The replaced node\r
45933      */\r
45934     replaceChild : function(newChild, oldChild){\r
45935         var s = oldChild ? oldChild.nextSibling : null;\r
45936         this.removeChild(oldChild);\r
45937         this.insertBefore(newChild, s);\r
45938         return oldChild;\r
45939     },\r
45940 \r
45941     /**\r
45942      * Returns the index of a child node\r
45943      * @param {Node} node\r
45944      * @return {Number} The index of the node or -1 if it was not found\r
45945      */\r
45946     indexOf : function(child){\r
45947         return this.childNodes.indexOf(child);\r
45948     },\r
45949 \r
45950     /**\r
45951      * Returns the tree this node is in.\r
45952      * @return {Tree}\r
45953      */\r
45954     getOwnerTree : function(){\r
45955         // if it doesn't have one, look for one\r
45956         if(!this.ownerTree){\r
45957             var p = this;\r
45958             while(p){\r
45959                 if(p.ownerTree){\r
45960                     this.ownerTree = p.ownerTree;\r
45961                     break;\r
45962                 }\r
45963                 p = p.parentNode;\r
45964             }\r
45965         }\r
45966         return this.ownerTree;\r
45967     },\r
45968 \r
45969     /**\r
45970      * Returns depth of this node (the root node has a depth of 0)\r
45971      * @return {Number}\r
45972      */\r
45973     getDepth : function(){\r
45974         var depth = 0;\r
45975         var p = this;\r
45976         while(p.parentNode){\r
45977             ++depth;\r
45978             p = p.parentNode;\r
45979         }\r
45980         return depth;\r
45981     },\r
45982 \r
45983     // private\r
45984     setOwnerTree : function(tree){\r
45985         // if it is a move, we need to update everyone\r
45986         if(tree != this.ownerTree){\r
45987             if(this.ownerTree){\r
45988                 this.ownerTree.unregisterNode(this);\r
45989             }\r
45990             this.ownerTree = tree;\r
45991             var cs = this.childNodes;\r
45992             for(var i = 0, len = cs.length; i < len; i++) {\r
45993                 cs[i].setOwnerTree(tree);\r
45994             }\r
45995             if(tree){\r
45996                 tree.registerNode(this);\r
45997             }\r
45998         }\r
45999     },\r
46000     \r
46001     /**\r
46002      * Changes the id of this node.\r
46003      * @param {String} id The new id for the node.\r
46004      */\r
46005     setId: function(id){\r
46006         if(id !== this.id){\r
46007             var t = this.ownerTree;\r
46008             if(t){\r
46009                 t.unregisterNode(this);\r
46010             }\r
46011             this.id = id;\r
46012             if(t){\r
46013                 t.registerNode(this);\r
46014             }\r
46015             this.onIdChange(id);\r
46016         }\r
46017     },\r
46018     \r
46019     // private\r
46020     onIdChange: Ext.emptyFn,\r
46021 \r
46022     /**\r
46023      * Returns the path for this node. The path can be used to expand or select this node programmatically.\r
46024      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)\r
46025      * @return {String} The path\r
46026      */\r
46027     getPath : function(attr){\r
46028         attr = attr || "id";\r
46029         var p = this.parentNode;\r
46030         var b = [this.attributes[attr]];\r
46031         while(p){\r
46032             b.unshift(p.attributes[attr]);\r
46033             p = p.parentNode;\r
46034         }\r
46035         var sep = this.getOwnerTree().pathSeparator;\r
46036         return sep + b.join(sep);\r
46037     },\r
46038 \r
46039     /**\r
46040      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of\r
46041      * function call will be the scope provided or the current node. The arguments to the function\r
46042      * will be the args provided or the current node. If the function returns false at any point,\r
46043      * the bubble is stopped.\r
46044      * @param {Function} fn The function to call\r
46045      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
46046      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
46047      */\r
46048     bubble : function(fn, scope, args){\r
46049         var p = this;\r
46050         while(p){\r
46051             if(fn.apply(scope || p, args || [p]) === false){\r
46052                 break;\r
46053             }\r
46054             p = p.parentNode;\r
46055         }\r
46056     },\r
46057 \r
46058     /**\r
46059      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of\r
46060      * function call will be the scope provided or the current node. The arguments to the function\r
46061      * will be the args provided or the current node. If the function returns false at any point,\r
46062      * the cascade is stopped on that branch.\r
46063      * @param {Function} fn The function to call\r
46064      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
46065      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
46066      */\r
46067     cascade : function(fn, scope, args){\r
46068         if(fn.apply(scope || this, args || [this]) !== false){\r
46069             var cs = this.childNodes;\r
46070             for(var i = 0, len = cs.length; i < len; i++) {\r
46071                 cs[i].cascade(fn, scope, args);\r
46072             }\r
46073         }\r
46074     },\r
46075 \r
46076     /**\r
46077      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of\r
46078      * function call will be the scope provided or the current node. The arguments to the function\r
46079      * will be the args provided or the current node. If the function returns false at any point,\r
46080      * the iteration stops.\r
46081      * @param {Function} fn The function to call\r
46082      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
46083      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
46084      */\r
46085     eachChild : function(fn, scope, args){\r
46086         var cs = this.childNodes;\r
46087         for(var i = 0, len = cs.length; i < len; i++) {\r
46088                 if(fn.apply(scope || this, args || [cs[i]]) === false){\r
46089                     break;\r
46090                 }\r
46091         }\r
46092     },\r
46093 \r
46094     /**\r
46095      * Finds the first child that has the attribute with the specified value.\r
46096      * @param {String} attribute The attribute name\r
46097      * @param {Mixed} value The value to search for\r
46098      * @return {Node} The found child or null if none was found\r
46099      */\r
46100     findChild : function(attribute, value){\r
46101         var cs = this.childNodes;\r
46102         for(var i = 0, len = cs.length; i < len; i++) {\r
46103                 if(cs[i].attributes[attribute] == value){\r
46104                     return cs[i];\r
46105                 }\r
46106         }\r
46107         return null;\r
46108     },\r
46109 \r
46110     /**\r
46111      * Finds the first child by a custom function. The child matches if the function passed\r
46112      * returns true.\r
46113      * @param {Function} fn\r
46114      * @param {Object} scope (optional)\r
46115      * @return {Node} The found child or null if none was found\r
46116      */\r
46117     findChildBy : function(fn, scope){\r
46118         var cs = this.childNodes;\r
46119         for(var i = 0, len = cs.length; i < len; i++) {\r
46120                 if(fn.call(scope||cs[i], cs[i]) === true){\r
46121                     return cs[i];\r
46122                 }\r
46123         }\r
46124         return null;\r
46125     },\r
46126 \r
46127     /**\r
46128      * Sorts this nodes children using the supplied sort function\r
46129      * @param {Function} fn\r
46130      * @param {Object} scope (optional)\r
46131      */\r
46132     sort : function(fn, scope){\r
46133         var cs = this.childNodes;\r
46134         var len = cs.length;\r
46135         if(len > 0){\r
46136             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;\r
46137             cs.sort(sortFn);\r
46138             for(var i = 0; i < len; i++){\r
46139                 var n = cs[i];\r
46140                 n.previousSibling = cs[i-1];\r
46141                 n.nextSibling = cs[i+1];\r
46142                 if(i === 0){\r
46143                     this.setFirstChild(n);\r
46144                 }\r
46145                 if(i == len-1){\r
46146                     this.setLastChild(n);\r
46147                 }\r
46148             }\r
46149         }\r
46150     },\r
46151 \r
46152     /**\r
46153      * Returns true if this node is an ancestor (at any point) of the passed node.\r
46154      * @param {Node} node\r
46155      * @return {Boolean}\r
46156      */\r
46157     contains : function(node){\r
46158         return node.isAncestor(this);\r
46159     },\r
46160 \r
46161     /**\r
46162      * Returns true if the passed node is an ancestor (at any point) of this node.\r
46163      * @param {Node} node\r
46164      * @return {Boolean}\r
46165      */\r
46166     isAncestor : function(node){\r
46167         var p = this.parentNode;\r
46168         while(p){\r
46169             if(p == node){\r
46170                 return true;\r
46171             }\r
46172             p = p.parentNode;\r
46173         }\r
46174         return false;\r
46175     },\r
46176 \r
46177     toString : function(){\r
46178         return "[Node"+(this.id?" "+this.id:"")+"]";\r
46179     }\r
46180 });/**\r
46181  * @class Ext.tree.TreeNode\r
46182  * @extends Ext.data.Node\r
46183  * @cfg {String} text The text for this node\r
46184  * @cfg {Boolean} expanded true to start the node expanded\r
46185  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)\r
46186  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)\r
46187  * @cfg {Boolean} disabled true to start the node disabled\r
46188  * @cfg {String} icon The path to an icon for the node. The preferred way to do this\r
46189  * is to use the cls or iconCls attributes and add the icon via a CSS background image.\r
46190  * @cfg {String} cls A css class to be added to the node\r
46191  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images\r
46192  * @cfg {String} href URL of the link used for the node (defaults to #)\r
46193  * @cfg {String} hrefTarget target frame for the link\r
46194  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).\r
46195  * @cfg {String} qtip An Ext QuickTip for the node\r
46196  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty\r
46197  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)\r
46198  * @cfg {Boolean} singleClickExpand True for single click expand on this node\r
46199  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)\r
46200  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox\r
46201  * (defaults to undefined with no checkbox rendered)\r
46202  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)\r
46203  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)\r
46204  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)\r
46205  * @cfg {Boolean} editable False to not allow this node to be edited by an (@link Ext.tree.TreeEditor} (defaults to true)\r
46206  * @constructor\r
46207  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node\r
46208  */\r
46209 Ext.tree.TreeNode = function(attributes){\r
46210     attributes = attributes || {};\r
46211     if(typeof attributes == "string"){\r
46212         attributes = {text: attributes};\r
46213     }\r
46214     this.childrenRendered = false;\r
46215     this.rendered = false;\r
46216     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);\r
46217     this.expanded = attributes.expanded === true;\r
46218     this.isTarget = attributes.isTarget !== false;\r
46219     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;\r
46220     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;\r
46221 \r
46222     /**\r
46223      * Read-only. The text for this node. To change it use setText().\r
46224      * @type String\r
46225      */\r
46226     this.text = attributes.text;\r
46227     /**\r
46228      * True if this node is disabled.\r
46229      * @type Boolean\r
46230      */\r
46231     this.disabled = attributes.disabled === true;\r
46232     /**\r
46233      * True if this node is hidden.\r
46234      * @type Boolean\r
46235      */\r
46236     this.hidden = attributes.hidden === true;\r
46237 \r
46238     this.addEvents(\r
46239         /**\r
46240         * @event textchange\r
46241         * Fires when the text for this node is changed\r
46242         * @param {Node} this This node\r
46243         * @param {String} text The new text\r
46244         * @param {String} oldText The old text\r
46245         */\r
46246         "textchange",\r
46247         /**\r
46248         * @event beforeexpand\r
46249         * Fires before this node is expanded, return false to cancel.\r
46250         * @param {Node} this This node\r
46251         * @param {Boolean} deep\r
46252         * @param {Boolean} anim\r
46253         */\r
46254         "beforeexpand",\r
46255         /**\r
46256         * @event beforecollapse\r
46257         * Fires before this node is collapsed, return false to cancel.\r
46258         * @param {Node} this This node\r
46259         * @param {Boolean} deep\r
46260         * @param {Boolean} anim\r
46261         */\r
46262         "beforecollapse",\r
46263         /**\r
46264         * @event expand\r
46265         * Fires when this node is expanded\r
46266         * @param {Node} this This node\r
46267         */\r
46268         "expand",\r
46269         /**\r
46270         * @event disabledchange\r
46271         * Fires when the disabled status of this node changes\r
46272         * @param {Node} this This node\r
46273         * @param {Boolean} disabled\r
46274         */\r
46275         "disabledchange",\r
46276         /**\r
46277         * @event collapse\r
46278         * Fires when this node is collapsed\r
46279         * @param {Node} this This node\r
46280         */\r
46281         "collapse",\r
46282         /**\r
46283         * @event beforeclick\r
46284         * Fires before click processing. Return false to cancel the default action.\r
46285         * @param {Node} this This node\r
46286         * @param {Ext.EventObject} e The event object\r
46287         */\r
46288         "beforeclick",\r
46289         /**\r
46290         * @event click\r
46291         * Fires when this node is clicked\r
46292         * @param {Node} this This node\r
46293         * @param {Ext.EventObject} e The event object\r
46294         */\r
46295         "click",\r
46296         /**\r
46297         * @event checkchange\r
46298         * Fires when a node with a checkbox's checked property changes\r
46299         * @param {Node} this This node\r
46300         * @param {Boolean} checked\r
46301         */\r
46302         "checkchange",\r
46303         /**\r
46304         * @event dblclick\r
46305         * Fires when this node is double clicked\r
46306         * @param {Node} this This node\r
46307         * @param {Ext.EventObject} e The event object\r
46308         */\r
46309         "dblclick",\r
46310         /**\r
46311         * @event contextmenu\r
46312         * Fires when this node is right clicked\r
46313         * @param {Node} this This node\r
46314         * @param {Ext.EventObject} e The event object\r
46315         */\r
46316         "contextmenu",\r
46317         /**\r
46318         * @event beforechildrenrendered\r
46319         * Fires right before the child nodes for this node are rendered\r
46320         * @param {Node} this This node\r
46321         */\r
46322         "beforechildrenrendered"\r
46323     );\r
46324 \r
46325     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;\r
46326 \r
46327     /**\r
46328      * Read-only. The UI for this node\r
46329      * @type TreeNodeUI\r
46330      */\r
46331     this.ui = new uiClass(this);\r
46332 };\r
46333 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {\r
46334     preventHScroll: true,\r
46335     /**\r
46336      * Returns true if this node is expanded\r
46337      * @return {Boolean}\r
46338      */\r
46339     isExpanded : function(){\r
46340         return this.expanded;\r
46341     },\r
46342 \r
46343 /**\r
46344  * Returns the UI object for this node.\r
46345  * @return {TreeNodeUI} The object which is providing the user interface for this tree\r
46346  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance\r
46347  * of {@link Ext.tree.TreeNodeUI}\r
46348  */\r
46349     getUI : function(){\r
46350         return this.ui;\r
46351     },\r
46352 \r
46353     getLoader : function(){\r
46354         var owner;\r
46355         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : new Ext.tree.TreeLoader());\r
46356     },\r
46357 \r
46358     // private override\r
46359     setFirstChild : function(node){\r
46360         var of = this.firstChild;\r
46361         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);\r
46362         if(this.childrenRendered && of && node != of){\r
46363             of.renderIndent(true, true);\r
46364         }\r
46365         if(this.rendered){\r
46366             this.renderIndent(true, true);\r
46367         }\r
46368     },\r
46369 \r
46370     // private override\r
46371     setLastChild : function(node){\r
46372         var ol = this.lastChild;\r
46373         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);\r
46374         if(this.childrenRendered && ol && node != ol){\r
46375             ol.renderIndent(true, true);\r
46376         }\r
46377         if(this.rendered){\r
46378             this.renderIndent(true, true);\r
46379         }\r
46380     },\r
46381 \r
46382     // these methods are overridden to provide lazy rendering support\r
46383     // private override\r
46384     appendChild : function(n){\r
46385         if(!n.render && !Ext.isArray(n)){\r
46386             n = this.getLoader().createNode(n);\r
46387         }\r
46388         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);\r
46389         if(node && this.childrenRendered){\r
46390             node.render();\r
46391         }\r
46392         this.ui.updateExpandIcon();\r
46393         return node;\r
46394     },\r
46395 \r
46396     // private override\r
46397     removeChild : function(node){\r
46398         this.ownerTree.getSelectionModel().unselect(node);\r
46399         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);\r
46400         // if it's been rendered remove dom node\r
46401         if(this.childrenRendered){\r
46402             node.ui.remove();\r
46403         }\r
46404         if(this.childNodes.length < 1){\r
46405             this.collapse(false, false);\r
46406         }else{\r
46407             this.ui.updateExpandIcon();\r
46408         }\r
46409         if(!this.firstChild && !this.isHiddenRoot()) {\r
46410             this.childrenRendered = false;\r
46411         }\r
46412         return node;\r
46413     },\r
46414 \r
46415     // private override\r
46416     insertBefore : function(node, refNode){\r
46417         if(!node.render){ \r
46418             node = this.getLoader().createNode(node);\r
46419         }\r
46420         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);\r
46421         if(newNode && refNode && this.childrenRendered){\r
46422             node.render();\r
46423         }\r
46424         this.ui.updateExpandIcon();\r
46425         return newNode;\r
46426     },\r
46427 \r
46428     /**\r
46429      * Sets the text for this node\r
46430      * @param {String} text\r
46431      */\r
46432     setText : function(text){\r
46433         var oldText = this.text;\r
46434         this.text = text;\r
46435         this.attributes.text = text;\r
46436         if(this.rendered){ // event without subscribing\r
46437             this.ui.onTextChange(this, text, oldText);\r
46438         }\r
46439         this.fireEvent("textchange", this, text, oldText);\r
46440     },\r
46441 \r
46442     /**\r
46443      * Triggers selection of this node\r
46444      */\r
46445     select : function(){\r
46446         this.getOwnerTree().getSelectionModel().select(this);\r
46447     },\r
46448 \r
46449     /**\r
46450      * Triggers deselection of this node\r
46451      */\r
46452     unselect : function(){\r
46453         this.getOwnerTree().getSelectionModel().unselect(this);\r
46454     },\r
46455 \r
46456     /**\r
46457      * Returns true if this node is selected\r
46458      * @return {Boolean}\r
46459      */\r
46460     isSelected : function(){\r
46461         return this.getOwnerTree().getSelectionModel().isSelected(this);\r
46462     },\r
46463 \r
46464     /**\r
46465      * Expand this node.\r
46466      * @param {Boolean} deep (optional) True to expand all children as well\r
46467      * @param {Boolean} anim (optional) false to cancel the default animation\r
46468      * @param {Function} callback (optional) A callback to be called when\r
46469      * expanding this node completes (does not wait for deep expand to complete).\r
46470      * Called with 1 parameter, this node.\r
46471      * @param {Object} scope (optional) The scope in which to execute the callback.\r
46472      */\r
46473     expand : function(deep, anim, callback, scope){\r
46474         if(!this.expanded){\r
46475             if(this.fireEvent("beforeexpand", this, deep, anim) === false){\r
46476                 return;\r
46477             }\r
46478             if(!this.childrenRendered){\r
46479                 this.renderChildren();\r
46480             }\r
46481             this.expanded = true;\r
46482             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){\r
46483                 this.ui.animExpand(function(){\r
46484                     this.fireEvent("expand", this);\r
46485                     this.runCallback(callback, scope || this, [this]);\r
46486                     if(deep === true){\r
46487                         this.expandChildNodes(true);\r
46488                     }\r
46489                 }.createDelegate(this));\r
46490                 return;\r
46491             }else{\r
46492                 this.ui.expand();\r
46493                 this.fireEvent("expand", this);\r
46494                 this.runCallback(callback, scope || this, [this]);\r
46495             }\r
46496         }else{\r
46497            this.runCallback(callback, scope || this, [this]);\r
46498         }\r
46499         if(deep === true){\r
46500             this.expandChildNodes(true);\r
46501         }\r
46502     },\r
46503     \r
46504     runCallback: function(cb, scope, args){\r
46505         if(Ext.isFunction(cb)){\r
46506             cb.apply(scope, args);\r
46507         }\r
46508     },\r
46509 \r
46510     isHiddenRoot : function(){\r
46511         return this.isRoot && !this.getOwnerTree().rootVisible;\r
46512     },\r
46513 \r
46514     /**\r
46515      * Collapse this node.\r
46516      * @param {Boolean} deep (optional) True to collapse all children as well\r
46517      * @param {Boolean} anim (optional) false to cancel the default animation\r
46518      * @param {Function} callback (optional) A callback to be called when\r
46519      * expanding this node completes (does not wait for deep expand to complete).\r
46520      * Called with 1 parameter, this node.\r
46521      * @param {Object} scope (optional) The scope in which to execute the callback.\r
46522      */\r
46523     collapse : function(deep, anim, callback, scope){\r
46524         if(this.expanded && !this.isHiddenRoot()){\r
46525             if(this.fireEvent("beforecollapse", this, deep, anim) === false){\r
46526                 return;\r
46527             }\r
46528             this.expanded = false;\r
46529             if((this.getOwnerTree().animate && anim !== false) || anim){\r
46530                 this.ui.animCollapse(function(){\r
46531                     this.fireEvent("collapse", this);\r
46532                     this.runCallback(callback, scope || this, [this]);\r
46533                     if(deep === true){\r
46534                         this.collapseChildNodes(true);\r
46535                     }\r
46536                 }.createDelegate(this));\r
46537                 return;\r
46538             }else{\r
46539                 this.ui.collapse();\r
46540                 this.fireEvent("collapse", this);\r
46541                 this.runCallback(callback, scope || this, [this]);\r
46542             }\r
46543         }else if(!this.expanded){\r
46544             this.runCallback(callback, scope || this, [this]);\r
46545         }\r
46546         if(deep === true){\r
46547             var cs = this.childNodes;\r
46548             for(var i = 0, len = cs.length; i < len; i++) {\r
46549                 cs[i].collapse(true, false);\r
46550             }\r
46551         }\r
46552     },\r
46553 \r
46554     // private\r
46555     delayedExpand : function(delay){\r
46556         if(!this.expandProcId){\r
46557             this.expandProcId = this.expand.defer(delay, this);\r
46558         }\r
46559     },\r
46560 \r
46561     // private\r
46562     cancelExpand : function(){\r
46563         if(this.expandProcId){\r
46564             clearTimeout(this.expandProcId);\r
46565         }\r
46566         this.expandProcId = false;\r
46567     },\r
46568 \r
46569     /**\r
46570      * Toggles expanded/collapsed state of the node\r
46571      */\r
46572     toggle : function(){\r
46573         if(this.expanded){\r
46574             this.collapse();\r
46575         }else{\r
46576             this.expand();\r
46577         }\r
46578     },\r
46579 \r
46580     /**\r
46581      * Ensures all parent nodes are expanded, and if necessary, scrolls\r
46582      * the node into view.\r
46583      * @param {Function} callback (optional) A function to call when the node has been made visible.\r
46584      * @param {Object} scope (optional) The scope in which to execute the callback.\r
46585      */\r
46586     ensureVisible : function(callback, scope){\r
46587         var tree = this.getOwnerTree();\r
46588         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){\r
46589             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime\r
46590             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);\r
46591             this.runCallback(callback, scope || this, [this]);\r
46592         }.createDelegate(this));\r
46593     },\r
46594 \r
46595     /**\r
46596      * Expand all child nodes\r
46597      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes\r
46598      */\r
46599     expandChildNodes : function(deep){\r
46600         var cs = this.childNodes;\r
46601         for(var i = 0, len = cs.length; i < len; i++) {\r
46602                 cs[i].expand(deep);\r
46603         }\r
46604     },\r
46605 \r
46606     /**\r
46607      * Collapse all child nodes\r
46608      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes\r
46609      */\r
46610     collapseChildNodes : function(deep){\r
46611         var cs = this.childNodes;\r
46612         for(var i = 0, len = cs.length; i < len; i++) {\r
46613                 cs[i].collapse(deep);\r
46614         }\r
46615     },\r
46616 \r
46617     /**\r
46618      * Disables this node\r
46619      */\r
46620     disable : function(){\r
46621         this.disabled = true;\r
46622         this.unselect();\r
46623         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
46624             this.ui.onDisableChange(this, true);\r
46625         }\r
46626         this.fireEvent("disabledchange", this, true);\r
46627     },\r
46628 \r
46629     /**\r
46630      * Enables this node\r
46631      */\r
46632     enable : function(){\r
46633         this.disabled = false;\r
46634         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
46635             this.ui.onDisableChange(this, false);\r
46636         }\r
46637         this.fireEvent("disabledchange", this, false);\r
46638     },\r
46639 \r
46640     // private\r
46641     renderChildren : function(suppressEvent){\r
46642         if(suppressEvent !== false){\r
46643             this.fireEvent("beforechildrenrendered", this);\r
46644         }\r
46645         var cs = this.childNodes;\r
46646         for(var i = 0, len = cs.length; i < len; i++){\r
46647             cs[i].render(true);\r
46648         }\r
46649         this.childrenRendered = true;\r
46650     },\r
46651 \r
46652     // private\r
46653     sort : function(fn, scope){\r
46654         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);\r
46655         if(this.childrenRendered){\r
46656             var cs = this.childNodes;\r
46657             for(var i = 0, len = cs.length; i < len; i++){\r
46658                 cs[i].render(true);\r
46659             }\r
46660         }\r
46661     },\r
46662 \r
46663     // private\r
46664     render : function(bulkRender){\r
46665         this.ui.render(bulkRender);\r
46666         if(!this.rendered){\r
46667             // make sure it is registered\r
46668             this.getOwnerTree().registerNode(this);\r
46669             this.rendered = true;\r
46670             if(this.expanded){\r
46671                 this.expanded = false;\r
46672                 this.expand(false, false);\r
46673             }\r
46674         }\r
46675     },\r
46676 \r
46677     // private\r
46678     renderIndent : function(deep, refresh){\r
46679         if(refresh){\r
46680             this.ui.childIndent = null;\r
46681         }\r
46682         this.ui.renderIndent();\r
46683         if(deep === true && this.childrenRendered){\r
46684             var cs = this.childNodes;\r
46685             for(var i = 0, len = cs.length; i < len; i++){\r
46686                 cs[i].renderIndent(true, refresh);\r
46687             }\r
46688         }\r
46689     },\r
46690 \r
46691     beginUpdate : function(){\r
46692         this.childrenRendered = false;\r
46693     },\r
46694 \r
46695     endUpdate : function(){\r
46696         if(this.expanded && this.rendered){\r
46697             this.renderChildren();\r
46698         }\r
46699     },\r
46700 \r
46701     destroy : function(){\r
46702         if(this.childNodes){\r
46703             for(var i = 0,l = this.childNodes.length; i < l; i++){\r
46704                 this.childNodes[i].destroy();\r
46705             }\r
46706             this.childNodes = null;\r
46707         }\r
46708         if(this.ui.destroy){\r
46709             this.ui.destroy();\r
46710         }\r
46711     },\r
46712     \r
46713     // private\r
46714     onIdChange: function(id){\r
46715         this.ui.onIdChange(id);\r
46716     }\r
46717 });\r
46718 \r
46719 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**\r
46720  * @class Ext.tree.AsyncTreeNode\r
46721  * @extends Ext.tree.TreeNode\r
46722  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)\r
46723  * @constructor\r
46724  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node \r
46725  */\r
46726  Ext.tree.AsyncTreeNode = function(config){\r
46727     this.loaded = config && config.loaded === true;\r
46728     this.loading = false;\r
46729     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);\r
46730     /**\r
46731     * @event beforeload\r
46732     * Fires before this node is loaded, return false to cancel\r
46733     * @param {Node} this This node\r
46734     */\r
46735     this.addEvents('beforeload', 'load');\r
46736     /**\r
46737     * @event load\r
46738     * Fires when this node is loaded\r
46739     * @param {Node} this This node\r
46740     */\r
46741     /**\r
46742      * The loader used by this node (defaults to using the tree's defined loader)\r
46743      * @type TreeLoader\r
46744      * @property loader\r
46745      */\r
46746 };\r
46747 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {\r
46748     expand : function(deep, anim, callback, scope){\r
46749         if(this.loading){ // if an async load is already running, waiting til it's done\r
46750             var timer;\r
46751             var f = function(){\r
46752                 if(!this.loading){ // done loading\r
46753                     clearInterval(timer);\r
46754                     this.expand(deep, anim, callback, scope);\r
46755                 }\r
46756             }.createDelegate(this);\r
46757             timer = setInterval(f, 200);\r
46758             return;\r
46759         }\r
46760         if(!this.loaded){\r
46761             if(this.fireEvent("beforeload", this) === false){\r
46762                 return;\r
46763             }\r
46764             this.loading = true;\r
46765             this.ui.beforeLoad(this);\r
46766             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();\r
46767             if(loader){\r
46768                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);\r
46769                 return;\r
46770             }\r
46771         }\r
46772         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);\r
46773     },\r
46774     \r
46775     /**\r
46776      * Returns true if this node is currently loading\r
46777      * @return {Boolean}\r
46778      */\r
46779     isLoading : function(){\r
46780         return this.loading;  \r
46781     },\r
46782     \r
46783     loadComplete : function(deep, anim, callback, scope){\r
46784         this.loading = false;\r
46785         this.loaded = true;\r
46786         this.ui.afterLoad(this);\r
46787         this.fireEvent("load", this);\r
46788         this.expand(deep, anim, callback, scope);\r
46789     },\r
46790     \r
46791     /**\r
46792      * Returns true if this node has been loaded\r
46793      * @return {Boolean}\r
46794      */\r
46795     isLoaded : function(){\r
46796         return this.loaded;\r
46797     },\r
46798     \r
46799     hasChildNodes : function(){\r
46800         if(!this.isLeaf() && !this.loaded){\r
46801             return true;\r
46802         }else{\r
46803             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);\r
46804         }\r
46805     },\r
46806 \r
46807     /**\r
46808      * Trigger a reload for this node\r
46809      * @param {Function} callback\r
46810      * @param {Object} scope (optional) The scope in which to execute the callback.\r
46811      */\r
46812     reload : function(callback, scope){\r
46813         this.collapse(false, false);\r
46814         while(this.firstChild){\r
46815             this.removeChild(this.firstChild).destroy();\r
46816         }\r
46817         this.childrenRendered = false;\r
46818         this.loaded = false;\r
46819         if(this.isHiddenRoot()){\r
46820             this.expanded = false;\r
46821         }\r
46822         this.expand(false, false, callback, scope);\r
46823     }\r
46824 });\r
46825 \r
46826 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**\r
46827  * @class Ext.tree.TreeNodeUI\r
46828  * This class provides the default UI implementation for Ext TreeNodes.\r
46829  * The TreeNode UI implementation is separate from the\r
46830  * tree implementation, and allows customizing of the appearance of\r
46831  * tree nodes.<br>\r
46832  * <p>\r
46833  * If you are customizing the Tree's user interface, you\r
46834  * may need to extend this class, but you should never need to instantiate this class.<br>\r
46835  * <p>\r
46836  * This class provides access to the user interface components of an Ext TreeNode, through\r
46837  * {@link Ext.tree.TreeNode#getUI}\r
46838  */\r
46839 Ext.tree.TreeNodeUI = function(node){\r
46840     this.node = node;\r
46841     this.rendered = false;\r
46842     this.animating = false;\r
46843     this.wasLeaf = true;\r
46844     this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
46845     this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
46846 };\r
46847 \r
46848 Ext.tree.TreeNodeUI.prototype = {\r
46849     // private\r
46850     removeChild : function(node){\r
46851         if(this.rendered){\r
46852             this.ctNode.removeChild(node.ui.getEl());\r
46853         } \r
46854     },\r
46855 \r
46856     // private\r
46857     beforeLoad : function(){\r
46858          this.addClass("x-tree-node-loading");\r
46859     },\r
46860 \r
46861     // private\r
46862     afterLoad : function(){\r
46863          this.removeClass("x-tree-node-loading");\r
46864     },\r
46865 \r
46866     // private\r
46867     onTextChange : function(node, text, oldText){\r
46868         if(this.rendered){\r
46869             this.textNode.innerHTML = text;\r
46870         }\r
46871     },\r
46872 \r
46873     // private\r
46874     onDisableChange : function(node, state){\r
46875         this.disabled = state;\r
46876                 if (this.checkbox) {\r
46877                         this.checkbox.disabled = state;\r
46878                 }        \r
46879         if(state){\r
46880             this.addClass("x-tree-node-disabled");\r
46881         }else{\r
46882             this.removeClass("x-tree-node-disabled");\r
46883         } \r
46884     },\r
46885 \r
46886     // private\r
46887     onSelectedChange : function(state){\r
46888         if(state){\r
46889             this.focus();\r
46890             this.addClass("x-tree-selected");\r
46891         }else{\r
46892             //this.blur();\r
46893             this.removeClass("x-tree-selected");\r
46894         }\r
46895     },\r
46896 \r
46897     // private\r
46898     onMove : function(tree, node, oldParent, newParent, index, refNode){\r
46899         this.childIndent = null;\r
46900         if(this.rendered){\r
46901             var targetNode = newParent.ui.getContainer();\r
46902             if(!targetNode){//target not rendered\r
46903                 this.holder = document.createElement("div");\r
46904                 this.holder.appendChild(this.wrap);\r
46905                 return;\r
46906             }\r
46907             var insertBefore = refNode ? refNode.ui.getEl() : null;\r
46908             if(insertBefore){\r
46909                 targetNode.insertBefore(this.wrap, insertBefore);\r
46910             }else{\r
46911                 targetNode.appendChild(this.wrap);\r
46912             }\r
46913             this.node.renderIndent(true, oldParent != newParent);\r
46914         }\r
46915     },\r
46916 \r
46917 /**\r
46918  * Adds one or more CSS classes to the node's UI element.\r
46919  * Duplicate classes are automatically filtered out.\r
46920  * @param {String/Array} className The CSS class to add, or an array of classes\r
46921  */\r
46922     addClass : function(cls){\r
46923         if(this.elNode){\r
46924             Ext.fly(this.elNode).addClass(cls);\r
46925         }\r
46926     },\r
46927 \r
46928 /**\r
46929  * Removes one or more CSS classes from the node's UI element.\r
46930  * @param {String/Array} className The CSS class to remove, or an array of classes\r
46931  */\r
46932     removeClass : function(cls){\r
46933         if(this.elNode){\r
46934             Ext.fly(this.elNode).removeClass(cls);  \r
46935         }\r
46936     },\r
46937 \r
46938     // private\r
46939     remove : function(){\r
46940         if(this.rendered){\r
46941             this.holder = document.createElement("div");\r
46942             this.holder.appendChild(this.wrap);\r
46943         }  \r
46944     },\r
46945 \r
46946     // private\r
46947     fireEvent : function(){\r
46948         return this.node.fireEvent.apply(this.node, arguments);  \r
46949     },\r
46950 \r
46951     // private\r
46952     initEvents : function(){\r
46953         this.node.on("move", this.onMove, this);\r
46954 \r
46955         if(this.node.disabled){\r
46956             this.addClass("x-tree-node-disabled");\r
46957                         if (this.checkbox) {\r
46958                                 this.checkbox.disabled = true;\r
46959                         }            \r
46960         }\r
46961         if(this.node.hidden){\r
46962             this.hide();\r
46963         }\r
46964         var ot = this.node.getOwnerTree();\r
46965         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
46966         if(dd && (!this.node.isRoot || ot.rootVisible)){\r
46967             Ext.dd.Registry.register(this.elNode, {\r
46968                 node: this.node,\r
46969                 handles: this.getDDHandles(),\r
46970                 isHandle: false\r
46971             });\r
46972         }\r
46973     },\r
46974 \r
46975     // private\r
46976     getDDHandles : function(){\r
46977         return [this.iconNode, this.textNode, this.elNode];\r
46978     },\r
46979 \r
46980 /**\r
46981  * Hides this node.\r
46982  */\r
46983     hide : function(){\r
46984         this.node.hidden = true;\r
46985         if(this.wrap){\r
46986             this.wrap.style.display = "none";\r
46987         }\r
46988     },\r
46989 \r
46990 /**\r
46991  * Shows this node.\r
46992  */\r
46993     show : function(){\r
46994         this.node.hidden = false;\r
46995         if(this.wrap){\r
46996             this.wrap.style.display = "";\r
46997         } \r
46998     },\r
46999 \r
47000     // private\r
47001     onContextMenu : function(e){\r
47002         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
47003             e.preventDefault();\r
47004             this.focus();\r
47005             this.fireEvent("contextmenu", this.node, e);\r
47006         }\r
47007     },\r
47008 \r
47009     // private\r
47010     onClick : function(e){\r
47011         if(this.dropping){\r
47012             e.stopEvent();\r
47013             return;\r
47014         }\r
47015         if(this.fireEvent("beforeclick", this.node, e) !== false){\r
47016             var a = e.getTarget('a');\r
47017             if(!this.disabled && this.node.attributes.href && a){\r
47018                 this.fireEvent("click", this.node, e);\r
47019                 return;\r
47020             }else if(a && e.ctrlKey){\r
47021                 e.stopEvent();\r
47022             }\r
47023             e.preventDefault();\r
47024             if(this.disabled){\r
47025                 return;\r
47026             }\r
47027 \r
47028             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
47029                 this.node.toggle();\r
47030             }\r
47031 \r
47032             this.fireEvent("click", this.node, e);\r
47033         }else{\r
47034             e.stopEvent();\r
47035         }\r
47036     },\r
47037 \r
47038     // private\r
47039     onDblClick : function(e){\r
47040         e.preventDefault();\r
47041         if(this.disabled){\r
47042             return;\r
47043         }\r
47044         if(this.checkbox){\r
47045             this.toggleCheck();\r
47046         }\r
47047         if(!this.animating && this.node.isExpandable()){\r
47048             this.node.toggle();\r
47049         }\r
47050         this.fireEvent("dblclick", this.node, e);\r
47051     },\r
47052 \r
47053     onOver : function(e){\r
47054         this.addClass('x-tree-node-over');\r
47055     },\r
47056 \r
47057     onOut : function(e){\r
47058         this.removeClass('x-tree-node-over');\r
47059     },\r
47060 \r
47061     // private\r
47062     onCheckChange : function(){\r
47063         var checked = this.checkbox.checked;\r
47064                 // fix for IE6\r
47065                 this.checkbox.defaultChecked = checked;         \r
47066         this.node.attributes.checked = checked;\r
47067         this.fireEvent('checkchange', this.node, checked);\r
47068     },\r
47069 \r
47070     // private\r
47071     ecClick : function(e){\r
47072         if(!this.animating && this.node.isExpandable()){\r
47073             this.node.toggle();\r
47074         }\r
47075     },\r
47076 \r
47077     // private\r
47078     startDrop : function(){\r
47079         this.dropping = true;\r
47080     },\r
47081     \r
47082     // delayed drop so the click event doesn't get fired on a drop\r
47083     endDrop : function(){ \r
47084        setTimeout(function(){\r
47085            this.dropping = false;\r
47086        }.createDelegate(this), 50); \r
47087     },\r
47088 \r
47089     // private\r
47090     expand : function(){\r
47091         this.updateExpandIcon();\r
47092         this.ctNode.style.display = "";\r
47093     },\r
47094 \r
47095     // private\r
47096     focus : function(){\r
47097         if(!this.node.preventHScroll){\r
47098             try{this.anchor.focus();\r
47099             }catch(e){}\r
47100         }else{\r
47101             try{\r
47102                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;\r
47103                 var l = noscroll.scrollLeft;\r
47104                 this.anchor.focus();\r
47105                 noscroll.scrollLeft = l;\r
47106             }catch(e){}\r
47107         }\r
47108     },\r
47109 \r
47110 /**\r
47111  * Sets the checked status of the tree node to the passed value, or, if no value was passed,\r
47112  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.\r
47113  * @param {Boolean} (optional) The new checked status.\r
47114  */\r
47115     toggleCheck : function(value){\r
47116         var cb = this.checkbox;\r
47117         if(cb){\r
47118             cb.checked = (value === undefined ? !cb.checked : value);\r
47119             this.onCheckChange();\r
47120         }\r
47121     },\r
47122 \r
47123     // private\r
47124     blur : function(){\r
47125         try{\r
47126             this.anchor.blur();\r
47127         }catch(e){} \r
47128     },\r
47129 \r
47130     // private\r
47131     animExpand : function(callback){\r
47132         var ct = Ext.get(this.ctNode);\r
47133         ct.stopFx();\r
47134         if(!this.node.isExpandable()){\r
47135             this.updateExpandIcon();\r
47136             this.ctNode.style.display = "";\r
47137             Ext.callback(callback);\r
47138             return;\r
47139         }\r
47140         this.animating = true;\r
47141         this.updateExpandIcon();\r
47142         \r
47143         ct.slideIn('t', {\r
47144            callback : function(){\r
47145                this.animating = false;\r
47146                Ext.callback(callback);\r
47147             },\r
47148             scope: this,\r
47149             duration: this.node.ownerTree.duration || .25\r
47150         });\r
47151     },\r
47152 \r
47153     // private\r
47154     highlight : function(){\r
47155         var tree = this.node.getOwnerTree();\r
47156         Ext.fly(this.wrap).highlight(\r
47157             tree.hlColor || "C3DAF9",\r
47158             {endColor: tree.hlBaseColor}\r
47159         );\r
47160     },\r
47161 \r
47162     // private\r
47163     collapse : function(){\r
47164         this.updateExpandIcon();\r
47165         this.ctNode.style.display = "none";\r
47166     },\r
47167 \r
47168     // private\r
47169     animCollapse : function(callback){\r
47170         var ct = Ext.get(this.ctNode);\r
47171         ct.enableDisplayMode('block');\r
47172         ct.stopFx();\r
47173 \r
47174         this.animating = true;\r
47175         this.updateExpandIcon();\r
47176 \r
47177         ct.slideOut('t', {\r
47178             callback : function(){\r
47179                this.animating = false;\r
47180                Ext.callback(callback);\r
47181             },\r
47182             scope: this,\r
47183             duration: this.node.ownerTree.duration || .25\r
47184         });\r
47185     },\r
47186 \r
47187     // private\r
47188     getContainer : function(){\r
47189         return this.ctNode;  \r
47190     },\r
47191 \r
47192     // private\r
47193     getEl : function(){\r
47194         return this.wrap;  \r
47195     },\r
47196 \r
47197     // private\r
47198     appendDDGhost : function(ghostNode){\r
47199         ghostNode.appendChild(this.elNode.cloneNode(true));\r
47200     },\r
47201 \r
47202     // private\r
47203     getDDRepairXY : function(){\r
47204         return Ext.lib.Dom.getXY(this.iconNode);\r
47205     },\r
47206 \r
47207     // private\r
47208     onRender : function(){\r
47209         this.render();    \r
47210     },\r
47211 \r
47212     // private\r
47213     render : function(bulkRender){\r
47214         var n = this.node, a = n.attributes;\r
47215         var targetNode = n.parentNode ? \r
47216               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
47217         \r
47218         if(!this.rendered){\r
47219             this.rendered = true;\r
47220 \r
47221             this.renderElements(n, a, targetNode, bulkRender);\r
47222 \r
47223             if(a.qtip){\r
47224                if(this.textNode.setAttributeNS){\r
47225                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
47226                    if(a.qtipTitle){\r
47227                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
47228                    }\r
47229                }else{\r
47230                    this.textNode.setAttribute("ext:qtip", a.qtip);\r
47231                    if(a.qtipTitle){\r
47232                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
47233                    }\r
47234                } \r
47235             }else if(a.qtipCfg){\r
47236                 a.qtipCfg.target = Ext.id(this.textNode);\r
47237                 Ext.QuickTips.register(a.qtipCfg);\r
47238             }\r
47239             this.initEvents();\r
47240             if(!this.node.expanded){\r
47241                 this.updateExpandIcon(true);\r
47242             }\r
47243         }else{\r
47244             if(bulkRender === true) {\r
47245                 targetNode.appendChild(this.wrap);\r
47246             }\r
47247         }\r
47248     },\r
47249 \r
47250     // private\r
47251     renderElements : function(n, a, targetNode, bulkRender){\r
47252         // add some indent caching, this helps performance when rendering a large tree\r
47253         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
47254 \r
47255         var cb = typeof a.checked == 'boolean';\r
47256 \r
47257         var href = a.href ? a.href : Ext.isGecko ? "" : "#";\r
47258         var 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">',\r
47259             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
47260             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
47261             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',\r
47262             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
47263             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
47264              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
47265             '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
47266             "</li>"].join('');\r
47267 \r
47268         var nel;\r
47269         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
47270             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
47271         }else{\r
47272             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
47273         }\r
47274         \r
47275         this.elNode = this.wrap.childNodes[0];\r
47276         this.ctNode = this.wrap.childNodes[1];\r
47277         var cs = this.elNode.childNodes;\r
47278         this.indentNode = cs[0];\r
47279         this.ecNode = cs[1];\r
47280         this.iconNode = cs[2];\r
47281         var index = 3;\r
47282         if(cb){\r
47283             this.checkbox = cs[3];\r
47284                         // fix for IE6\r
47285                         this.checkbox.defaultChecked = this.checkbox.checked;                                           \r
47286             index++;\r
47287         }\r
47288         this.anchor = cs[index];\r
47289         this.textNode = cs[index].firstChild;\r
47290     },\r
47291 \r
47292 /**\r
47293  * Returns the &lt;a> element that provides focus for the node's UI.\r
47294  * @return {HtmlElement} The DOM anchor element.\r
47295  */\r
47296     getAnchor : function(){\r
47297         return this.anchor;\r
47298     },\r
47299     \r
47300 /**\r
47301  * Returns the text node.\r
47302  * @return {HtmlNode} The DOM text node.\r
47303  */\r
47304     getTextEl : function(){\r
47305         return this.textNode;\r
47306     },\r
47307     \r
47308 /**\r
47309  * Returns the icon &lt;img> element.\r
47310  * @return {HtmlElement} The DOM image element.\r
47311  */\r
47312     getIconEl : function(){\r
47313         return this.iconNode;\r
47314     },\r
47315 \r
47316 /**\r
47317  * Returns the checked status of the node. If the node was rendered with no\r
47318  * checkbox, it returns false.\r
47319  * @return {Boolean} The checked flag.\r
47320  */\r
47321     isChecked : function(){\r
47322         return this.checkbox ? this.checkbox.checked : false; \r
47323     },\r
47324 \r
47325     // private\r
47326     updateExpandIcon : function(){\r
47327         if(this.rendered){\r
47328             var n = this.node, c1, c2;\r
47329             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";\r
47330             var hasChild = n.hasChildNodes();\r
47331             if(hasChild || n.attributes.expandable){\r
47332                 if(n.expanded){\r
47333                     cls += "-minus";\r
47334                     c1 = "x-tree-node-collapsed";\r
47335                     c2 = "x-tree-node-expanded";\r
47336                 }else{\r
47337                     cls += "-plus";\r
47338                     c1 = "x-tree-node-expanded";\r
47339                     c2 = "x-tree-node-collapsed";\r
47340                 }\r
47341                 if(this.wasLeaf){\r
47342                     this.removeClass("x-tree-node-leaf");\r
47343                     this.wasLeaf = false;\r
47344                 }\r
47345                 if(this.c1 != c1 || this.c2 != c2){\r
47346                     Ext.fly(this.elNode).replaceClass(c1, c2);\r
47347                     this.c1 = c1; this.c2 = c2;\r
47348                 }\r
47349             }else{\r
47350                 if(!this.wasLeaf){\r
47351                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");\r
47352                     delete this.c1;\r
47353                     delete this.c2;\r
47354                     this.wasLeaf = true;\r
47355                 }\r
47356             }\r
47357             var ecc = "x-tree-ec-icon "+cls;\r
47358             if(this.ecc != ecc){\r
47359                 this.ecNode.className = ecc;\r
47360                 this.ecc = ecc;\r
47361             }\r
47362         }\r
47363     },\r
47364     \r
47365     // private\r
47366     onIdChange: function(id){\r
47367         if(this.rendered){\r
47368             this.elNode.setAttribute('ext:tree-node-id', id);\r
47369         }\r
47370     },\r
47371 \r
47372     // private\r
47373     getChildIndent : function(){\r
47374         if(!this.childIndent){\r
47375             var buf = [];\r
47376             var p = this.node;\r
47377             while(p){\r
47378                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
47379                     if(!p.isLast()) {\r
47380                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
47381                     } else {\r
47382                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
47383                     }\r
47384                 }\r
47385                 p = p.parentNode;\r
47386             }\r
47387             this.childIndent = buf.join("");\r
47388         }\r
47389         return this.childIndent;\r
47390     },\r
47391 \r
47392     // private\r
47393     renderIndent : function(){\r
47394         if(this.rendered){\r
47395             var indent = "";\r
47396             var p = this.node.parentNode;\r
47397             if(p){\r
47398                 indent = p.ui.getChildIndent();\r
47399             }\r
47400             if(this.indentMarkup != indent){ // don't rerender if not required\r
47401                 this.indentNode.innerHTML = indent;\r
47402                 this.indentMarkup = indent;\r
47403             }\r
47404             this.updateExpandIcon();\r
47405         }\r
47406     },\r
47407 \r
47408     destroy : function(){\r
47409         if(this.elNode){\r
47410             Ext.dd.Registry.unregister(this.elNode.id);\r
47411         }\r
47412         delete this.elNode;\r
47413         delete this.ctNode;\r
47414         delete this.indentNode;\r
47415         delete this.ecNode;\r
47416         delete this.iconNode;\r
47417         delete this.checkbox;\r
47418         delete this.anchor;\r
47419         delete this.textNode;\r
47420         \r
47421         if (this.holder){\r
47422              delete this.wrap;\r
47423              Ext.removeNode(this.holder);\r
47424              delete this.holder;\r
47425         }else{\r
47426             Ext.removeNode(this.wrap);\r
47427             delete this.wrap;\r
47428         }\r
47429     }\r
47430 };\r
47431 \r
47432 /**\r
47433  * @class Ext.tree.RootTreeNodeUI\r
47434  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
47435  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
47436  * <p>\r
47437  * If you are customizing the Tree's user interface, you\r
47438  * may need to extend this class, but you should never need to instantiate this class.<br>\r
47439  */\r
47440 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
47441     // private\r
47442     render : function(){\r
47443         if(!this.rendered){\r
47444             var targetNode = this.node.ownerTree.innerCt.dom;\r
47445             this.node.expanded = true;\r
47446             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
47447             this.wrap = this.ctNode = targetNode.firstChild;\r
47448         }\r
47449     },\r
47450     collapse : Ext.emptyFn,\r
47451     expand : Ext.emptyFn\r
47452 });/**\r
47453  * @class Ext.tree.TreeLoader\r
47454  * @extends Ext.util.Observable\r
47455  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child\r
47456  * nodes from a specified URL. The response must be a JavaScript Array definition\r
47457  * whose elements are node definition objects. e.g.:\r
47458  * <pre><code>\r
47459     [{\r
47460         id: 1,\r
47461         text: 'A leaf Node',\r
47462         leaf: true\r
47463     },{\r
47464         id: 2,\r
47465         text: 'A folder Node',\r
47466         children: [{\r
47467             id: 3,\r
47468             text: 'A child Node',\r
47469             leaf: true\r
47470         }]\r
47471    }]\r
47472 </code></pre>\r
47473  * <br><br>\r
47474  * A server request is sent, and child nodes are loaded only when a node is expanded.\r
47475  * The loading node's id is passed to the server under the parameter name "node" to\r
47476  * enable the server to produce the correct child nodes.\r
47477  * <br><br>\r
47478  * To pass extra parameters, an event handler may be attached to the "beforeload"\r
47479  * event, and the parameters specified in the TreeLoader's baseParams property:\r
47480  * <pre><code>\r
47481     myTreeLoader.on("beforeload", function(treeLoader, node) {\r
47482         this.baseParams.category = node.attributes.category;\r
47483     }, this);\r
47484 </code></pre>\r
47485  * This would pass an HTTP parameter called "category" to the server containing\r
47486  * the value of the Node's "category" attribute.\r
47487  * @constructor\r
47488  * Creates a new Treeloader.\r
47489  * @param {Object} config A config object containing config properties.\r
47490  */\r
47491 Ext.tree.TreeLoader = function(config){\r
47492     this.baseParams = {};\r
47493     Ext.apply(this, config);\r
47494 \r
47495     this.addEvents(\r
47496         /**\r
47497          * @event beforeload\r
47498          * Fires before a network request is made to retrieve the Json text which specifies a node's children.\r
47499          * @param {Object} This TreeLoader object.\r
47500          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
47501          * @param {Object} callback The callback function specified in the {@link #load} call.\r
47502          */\r
47503         "beforeload",\r
47504         /**\r
47505          * @event load\r
47506          * Fires when the node has been successfuly loaded.\r
47507          * @param {Object} This TreeLoader object.\r
47508          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
47509          * @param {Object} response The response object containing the data from the server.\r
47510          */\r
47511         "load",\r
47512         /**\r
47513          * @event loadexception\r
47514          * Fires if the network request failed.\r
47515          * @param {Object} This TreeLoader object.\r
47516          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
47517          * @param {Object} response The response object containing the data from the server.\r
47518          */\r
47519         "loadexception"\r
47520     );\r
47521     Ext.tree.TreeLoader.superclass.constructor.call(this);\r
47522     if(typeof this.paramOrder == 'string'){\r
47523         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
47524     }\r
47525 };\r
47526 \r
47527 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {\r
47528     /**\r
47529     * @cfg {String} dataUrl The URL from which to request a Json string which\r
47530     * specifies an array of node definition objects representing the child nodes\r
47531     * to be loaded.\r
47532     */\r
47533     /**\r
47534      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
47535      */\r
47536     /**\r
47537      * @cfg {String} url Equivalent to {@link #dataUrl}.\r
47538      */\r
47539     /**\r
47540      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.\r
47541      */\r
47542     /**\r
47543     * @cfg {Object} baseParams (optional) An object containing properties which\r
47544     * specify HTTP parameters to be passed to each request for child nodes.\r
47545     */\r
47546     /**\r
47547     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes\r
47548     * created by this loader. If the attributes sent by the server have an attribute in this object,\r
47549     * they take priority.\r
47550     */\r
47551     /**\r
47552     * @cfg {Object} uiProviders (optional) An object containing properties which\r
47553     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional\r
47554     * <i>uiProvider</i> attribute of a returned child node is a string rather\r
47555     * than a reference to a TreeNodeUI implementation, then that string value\r
47556     * is used as a property name in the uiProviders object.\r
47557     */\r
47558     uiProviders : {},\r
47559 \r
47560     /**\r
47561     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing\r
47562     * child nodes before loading.\r
47563     */\r
47564     clearOnLoad : true,\r
47565 \r
47566     /**\r
47567      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.\r
47568      * A list of params to be executed\r
47569      * server side.  Specify the params in the order in which they must be executed on the server-side\r
47570      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
47571      * comma, or pipe. For example,\r
47572      * any of the following would be acceptable:<pre><code>\r
47573 paramOrder: ['param1','param2','param3']\r
47574 paramOrder: 'param1 param2 param3'\r
47575 paramOrder: 'param1,param2,param3'\r
47576 paramOrder: 'param1|param2|param'\r
47577      </code></pre>\r
47578      */\r
47579     paramOrder: undefined,\r
47580 \r
47581     /**\r
47582      * @cfg {Boolean} paramsAsHash Only used when using directFn.\r
47583      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a\r
47584      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
47585      */\r
47586     paramsAsHash: false,\r
47587 \r
47588     /**\r
47589      * @cfg {Function} directFn\r
47590      * Function to call when executing a request.\r
47591      */\r
47592     directFn : undefined,\r
47593 \r
47594     /**\r
47595      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.\r
47596      * This is called automatically when a node is expanded, but may be used to reload\r
47597      * a node (or append new children if the {@link #clearOnLoad} option is false.)\r
47598      * @param {Ext.tree.TreeNode} node\r
47599      * @param {Function} callback\r
47600      * @param (Object) scope\r
47601      */\r
47602     load : function(node, callback, scope){\r
47603         if(this.clearOnLoad){\r
47604             while(node.firstChild){\r
47605                 node.removeChild(node.firstChild);\r
47606             }\r
47607         }\r
47608         if(this.doPreload(node)){ // preloaded json children\r
47609             this.runCallback(callback, scope || node, []);\r
47610         }else if(this.directFn || this.dataUrl || this.url){\r
47611             this.requestData(node, callback, scope || node);\r
47612         }\r
47613     },\r
47614 \r
47615     doPreload : function(node){\r
47616         if(node.attributes.children){\r
47617             if(node.childNodes.length < 1){ // preloaded?\r
47618                 var cs = node.attributes.children;\r
47619                 node.beginUpdate();\r
47620                 for(var i = 0, len = cs.length; i < len; i++){\r
47621                     var cn = node.appendChild(this.createNode(cs[i]));\r
47622                     if(this.preloadChildren){\r
47623                         this.doPreload(cn);\r
47624                     }\r
47625                 }\r
47626                 node.endUpdate();\r
47627             }\r
47628             return true;\r
47629         }\r
47630         return false;\r
47631     },\r
47632 \r
47633     getParams: function(node){\r
47634         var buf = [], bp = this.baseParams;\r
47635         if(this.directFn){\r
47636             buf.push(node.id);\r
47637             if(bp){\r
47638                 if(this.paramOrder){\r
47639                     for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
47640                         buf.push(bp[this.paramOrder[i]]);\r
47641                     }\r
47642                 }else if(this.paramsAsHash){\r
47643                     buf.push(bp);\r
47644                 }\r
47645             }\r
47646             return buf;\r
47647         }else{\r
47648             for(var key in bp){\r
47649                 if(!Ext.isFunction(bp[key])){\r
47650                     buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");\r
47651                 }\r
47652             }\r
47653             buf.push("node=", encodeURIComponent(node.id));\r
47654             return buf.join("");\r
47655         }\r
47656     },\r
47657 \r
47658     requestData : function(node, callback, scope){\r
47659         if(this.fireEvent("beforeload", this, node, callback) !== false){\r
47660             if(this.directFn){\r
47661                 var args = this.getParams(node);\r
47662                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));\r
47663                 this.directFn.apply(window, args);\r
47664             }else{\r
47665                 this.transId = Ext.Ajax.request({\r
47666                     method:this.requestMethod,\r
47667                     url: this.dataUrl||this.url,\r
47668                     success: this.handleResponse,\r
47669                     failure: this.handleFailure,\r
47670                     scope: this,\r
47671                     argument: {callback: callback, node: node, scope: scope},\r
47672                     params: this.getParams(node)\r
47673                 });\r
47674             }\r
47675         }else{\r
47676             // if the load is cancelled, make sure we notify\r
47677             // the node that we are done\r
47678             this.runCallback(callback, scope || node, []);\r
47679         }\r
47680     },\r
47681 \r
47682     processDirectResponse: function(result, response, args){\r
47683         if(response.status){\r
47684             this.handleResponse({\r
47685                 responseData: Ext.isArray(result) ? result : null,\r
47686                 responseText: result,\r
47687                 argument: args\r
47688             });\r
47689         }else{\r
47690             this.handleFailure({\r
47691                 argument: args\r
47692             });\r
47693         }\r
47694     },\r
47695 \r
47696     // private\r
47697     runCallback: function(cb, scope, args){\r
47698         if(Ext.isFunction(cb)){\r
47699             cb.apply(scope, args);\r
47700         }\r
47701     },\r
47702 \r
47703     isLoading : function(){\r
47704         return !!this.transId;\r
47705     },\r
47706 \r
47707     abort : function(){\r
47708         if(this.isLoading()){\r
47709             Ext.Ajax.abort(this.transId);\r
47710         }\r
47711     },\r
47712 \r
47713     /**\r
47714     * <p>Override this function for custom TreeNode node implementation, or to\r
47715     * modify the attributes at creation time.</p>\r
47716     * Example:<pre><code>\r
47717 new Ext.tree.TreePanel({\r
47718     ...\r
47719     new Ext.tree.TreeLoader({\r
47720         url: 'dataUrl',\r
47721         createNode: function(attr) {\r
47722 //          Allow consolidation consignments to have\r
47723 //          consignments dropped into them.\r
47724             if (attr.isConsolidation) {\r
47725                 attr.iconCls = 'x-consol',\r
47726                 attr.allowDrop = true;\r
47727             }\r
47728             return Ext.tree.TreeLoader.prototype.call(this, attr);\r
47729         }\r
47730     }),\r
47731     ...\r
47732 });\r
47733 </code></pre>\r
47734     * @param attr {Object} The attributes from which to create the new node.\r
47735     */\r
47736     createNode : function(attr){\r
47737         // apply baseAttrs, nice idea Corey!\r
47738         if(this.baseAttrs){\r
47739             Ext.applyIf(attr, this.baseAttrs);\r
47740         }\r
47741         if(this.applyLoader !== false){\r
47742             attr.loader = this;\r
47743         }\r
47744         if(typeof attr.uiProvider == 'string'){\r
47745            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);\r
47746         }\r
47747         if(attr.nodeType){\r
47748             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);\r
47749         }else{\r
47750             return attr.leaf ?\r
47751                         new Ext.tree.TreeNode(attr) :\r
47752                         new Ext.tree.AsyncTreeNode(attr);\r
47753         }\r
47754     },\r
47755 \r
47756     processResponse : function(response, node, callback, scope){\r
47757         var json = response.responseText;\r
47758         try {\r
47759             var o = response.responseData || Ext.decode(json);\r
47760             node.beginUpdate();\r
47761             for(var i = 0, len = o.length; i < len; i++){\r
47762                 var n = this.createNode(o[i]);\r
47763                 if(n){\r
47764                     node.appendChild(n);\r
47765                 }\r
47766             }\r
47767             node.endUpdate();\r
47768             this.runCallback(callback, scope || node, [node]);\r
47769         }catch(e){\r
47770             this.handleFailure(response);\r
47771         }\r
47772     },\r
47773 \r
47774     handleResponse : function(response){\r
47775         this.transId = false;\r
47776         var a = response.argument;\r
47777         this.processResponse(response, a.node, a.callback, a.scope);\r
47778         this.fireEvent("load", this, a.node, response);\r
47779     },\r
47780 \r
47781     handleFailure : function(response){\r
47782         this.transId = false;\r
47783         var a = response.argument;\r
47784         this.fireEvent("loadexception", this, a.node, response);\r
47785         this.runCallback(a.callback, a.scope || a.node, [a.node]);\r
47786     }\r
47787 });/**
47788  * @class Ext.tree.TreeFilter
47789  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
47790  * @param {TreePanel} tree
47791  * @param {Object} config (optional)
47792  */
47793 Ext.tree.TreeFilter = function(tree, config){
47794     this.tree = tree;
47795     this.filtered = {};
47796     Ext.apply(this, config);
47797 };
47798
47799 Ext.tree.TreeFilter.prototype = {
47800     clearBlank:false,
47801     reverse:false,
47802     autoClear:false,
47803     remove:false,
47804
47805      /**
47806      * Filter the data by a specific attribute.
47807      * @param {String/RegExp} value Either string that the attribute value
47808      * should start with or a RegExp to test against the attribute
47809      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
47810      * @param {TreeNode} startNode (optional) The node to start the filter at.
47811      */
47812     filter : function(value, attr, startNode){
47813         attr = attr || "text";
47814         var f;
47815         if(typeof value == "string"){
47816             var vlen = value.length;
47817             // auto clear empty filter
47818             if(vlen == 0 && this.clearBlank){
47819                 this.clear();
47820                 return;
47821             }
47822             value = value.toLowerCase();
47823             f = function(n){
47824                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
47825             };
47826         }else if(value.exec){ // regex?
47827             f = function(n){
47828                 return value.test(n.attributes[attr]);
47829             };
47830         }else{
47831             throw 'Illegal filter type, must be string or regex';
47832         }
47833         this.filterBy(f, null, startNode);
47834         },
47835
47836     /**
47837      * Filter by a function. The passed function will be called with each
47838      * node in the tree (or from the startNode). If the function returns true, the node is kept
47839      * otherwise it is filtered. If a node is filtered, its children are also filtered.
47840      * @param {Function} fn The filter function
47841      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
47842      */
47843     filterBy : function(fn, scope, startNode){
47844         startNode = startNode || this.tree.root;
47845         if(this.autoClear){
47846             this.clear();
47847         }
47848         var af = this.filtered, rv = this.reverse;
47849         var f = function(n){
47850             if(n == startNode){
47851                 return true;
47852             }
47853             if(af[n.id]){
47854                 return false;
47855             }
47856             var m = fn.call(scope || n, n);
47857             if(!m || rv){
47858                 af[n.id] = n;
47859                 n.ui.hide();
47860                 return false;
47861             }
47862             return true;
47863         };
47864         startNode.cascade(f);
47865         if(this.remove){
47866            for(var id in af){
47867                if(typeof id != "function"){
47868                    var n = af[id];
47869                    if(n && n.parentNode){
47870                        n.parentNode.removeChild(n);
47871                    }
47872                }
47873            }
47874         }
47875     },
47876
47877     /**
47878      * Clears the current filter. Note: with the "remove" option
47879      * set a filter cannot be cleared.
47880      */
47881     clear : function(){
47882         var t = this.tree;
47883         var af = this.filtered;
47884         for(var id in af){
47885             if(typeof id != "function"){
47886                 var n = af[id];
47887                 if(n){
47888                     n.ui.show();
47889                 }
47890             }
47891         }
47892         this.filtered = {};
47893     }
47894 };
47895 /**\r
47896  * @class Ext.tree.TreeSorter\r
47897  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the \r
47898  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).\r
47899  * Example usage:<br />\r
47900  * <pre><code>\r
47901 new Ext.tree.TreeSorter(myTree, {\r
47902     folderSort: true,\r
47903     dir: "desc",\r
47904     sortType: function(node) {\r
47905         // sort by a custom, typed attribute:\r
47906         return parseInt(node.id, 10);\r
47907     }\r
47908 });\r
47909 </code></pre>\r
47910  * @constructor\r
47911  * @param {TreePanel} tree\r
47912  * @param {Object} config\r
47913  */\r
47914 Ext.tree.TreeSorter = function(tree, config){\r
47915     /**\r
47916      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)\r
47917      */\r
47918     /** \r
47919      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this \r
47920      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.\r
47921      */\r
47922     /** \r
47923      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")\r
47924      */\r
47925     /** \r
47926      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")\r
47927      */\r
47928     /** \r
47929      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)\r
47930      */\r
47931     /** \r
47932      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function\r
47933      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return\r
47934      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when\r
47935      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for \r
47936      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.\r
47937      */\r
47938     \r
47939     Ext.apply(this, config);\r
47940     tree.on("beforechildrenrendered", this.doSort, this);\r
47941     tree.on("append", this.updateSort, this);\r
47942     tree.on("insert", this.updateSort, this);\r
47943     tree.on("textchange", this.updateSortParent, this);\r
47944     \r
47945     var dsc = this.dir && this.dir.toLowerCase() == "desc";\r
47946     var p = this.property || "text";\r
47947     var sortType = this.sortType;\r
47948     var fs = this.folderSort;\r
47949     var cs = this.caseSensitive === true;\r
47950     var leafAttr = this.leafAttr || 'leaf';\r
47951 \r
47952     this.sortFn = function(n1, n2){\r
47953         if(fs){\r
47954             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){\r
47955                 return 1;\r
47956             }\r
47957             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){\r
47958                 return -1;\r
47959             }\r
47960         }\r
47961         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());\r
47962         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());\r
47963         if(v1 < v2){\r
47964                         return dsc ? +1 : -1;\r
47965                 }else if(v1 > v2){\r
47966                         return dsc ? -1 : +1;\r
47967         }else{\r
47968                 return 0;\r
47969         }\r
47970     };\r
47971 };\r
47972 \r
47973 Ext.tree.TreeSorter.prototype = {\r
47974     doSort : function(node){\r
47975         node.sort(this.sortFn);\r
47976     },\r
47977     \r
47978     compareNodes : function(n1, n2){\r
47979         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);\r
47980     },\r
47981     \r
47982     updateSort : function(tree, node){\r
47983         if(node.childrenRendered){\r
47984             this.doSort.defer(1, this, [node]);\r
47985         }\r
47986     },\r
47987     \r
47988     updateSortParent : function(node){\r
47989                 var p = node.parentNode;\r
47990                 if(p && p.childrenRendered){\r
47991             this.doSort.defer(1, this, [p]);\r
47992         }\r
47993     }\r
47994 };/**\r
47995  * @class Ext.tree.TreeDropZone\r
47996  * @extends Ext.dd.DropZone\r
47997  * @constructor\r
47998  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping\r
47999  * @param {Object} config\r
48000  */\r
48001 if(Ext.dd.DropZone){\r
48002     \r
48003 Ext.tree.TreeDropZone = function(tree, config){\r
48004     /**\r
48005      * @cfg {Boolean} allowParentInsert\r
48006      * Allow inserting a dragged node between an expanded parent node and its first child that will become a\r
48007      * sibling of the parent when dropped (defaults to false)\r
48008      */\r
48009     this.allowParentInsert = config.allowParentInsert || false;\r
48010     /**\r
48011      * @cfg {String} allowContainerDrop\r
48012      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)\r
48013      */\r
48014     this.allowContainerDrop = config.allowContainerDrop || false;\r
48015     /**\r
48016      * @cfg {String} appendOnly\r
48017      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)\r
48018      */\r
48019     this.appendOnly = config.appendOnly || false;\r
48020 \r
48021     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);\r
48022     /**\r
48023     * The TreePanel for this drop zone\r
48024     * @type Ext.tree.TreePanel\r
48025     * @property\r
48026     */\r
48027     this.tree = tree;\r
48028     /**\r
48029     * Arbitrary data that can be associated with this tree and will be included in the event object that gets\r
48030     * passed to any nodedragover event handler (defaults to {})\r
48031     * @type Ext.tree.TreePanel\r
48032     * @property\r
48033     */\r
48034     this.dragOverData = {};\r
48035     // private\r
48036     this.lastInsertClass = "x-tree-no-status";\r
48037 };\r
48038 \r
48039 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {\r
48040     /**\r
48041      * @cfg {String} ddGroup\r
48042      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
48043      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
48044      */\r
48045     ddGroup : "TreeDD",\r
48046 \r
48047     /**\r
48048      * @cfg {String} expandDelay\r
48049      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node\r
48050      * over the target (defaults to 1000)\r
48051      */\r
48052     expandDelay : 1000,\r
48053 \r
48054     // private\r
48055     expandNode : function(node){\r
48056         if(node.hasChildNodes() && !node.isExpanded()){\r
48057             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));\r
48058         }\r
48059     },\r
48060 \r
48061     // private\r
48062     queueExpand : function(node){\r
48063         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);\r
48064     },\r
48065 \r
48066     // private\r
48067     cancelExpand : function(){\r
48068         if(this.expandProcId){\r
48069             clearTimeout(this.expandProcId);\r
48070             this.expandProcId = false;\r
48071         }\r
48072     },\r
48073 \r
48074     // private\r
48075     isValidDropPoint : function(n, pt, dd, e, data){\r
48076         if(!n || !data){ return false; }\r
48077         var targetNode = n.node;\r
48078         var dropNode = data.node;\r
48079         // default drop rules\r
48080         if(!(targetNode && targetNode.isTarget && pt)){\r
48081             return false;\r
48082         }\r
48083         if(pt == "append" && targetNode.allowChildren === false){\r
48084             return false;\r
48085         }\r
48086         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){\r
48087             return false;\r
48088         }\r
48089         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){\r
48090             return false;\r
48091         }\r
48092         // reuse the object\r
48093         var overEvent = this.dragOverData;\r
48094         overEvent.tree = this.tree;\r
48095         overEvent.target = targetNode;\r
48096         overEvent.data = data;\r
48097         overEvent.point = pt;\r
48098         overEvent.source = dd;\r
48099         overEvent.rawEvent = e;\r
48100         overEvent.dropNode = dropNode;\r
48101         overEvent.cancel = false;  \r
48102         var result = this.tree.fireEvent("nodedragover", overEvent);\r
48103         return overEvent.cancel === false && result !== false;\r
48104     },\r
48105 \r
48106     // private\r
48107     getDropPoint : function(e, n, dd){\r
48108         var tn = n.node;\r
48109         if(tn.isRoot){\r
48110             return tn.allowChildren !== false ? "append" : false; // always append for root\r
48111         }\r
48112         var dragEl = n.ddel;\r
48113         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;\r
48114         var y = Ext.lib.Event.getPageY(e);\r
48115         var noAppend = tn.allowChildren === false || tn.isLeaf();\r
48116         if(this.appendOnly || tn.parentNode.allowChildren === false){\r
48117             return noAppend ? false : "append";\r
48118         }\r
48119         var noBelow = false;\r
48120         if(!this.allowParentInsert){\r
48121             noBelow = tn.hasChildNodes() && tn.isExpanded();\r
48122         }\r
48123         var q = (b - t) / (noAppend ? 2 : 3);\r
48124         if(y >= t && y < (t + q)){\r
48125             return "above";\r
48126         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){\r
48127             return "below";\r
48128         }else{\r
48129             return "append";\r
48130         }\r
48131     },\r
48132 \r
48133     // private\r
48134     onNodeEnter : function(n, dd, e, data){\r
48135         this.cancelExpand();\r
48136     },\r
48137     \r
48138     onContainerOver : function(dd, e, data) {\r
48139         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
48140             return this.dropAllowed;\r
48141         }\r
48142         return this.dropNotAllowed;\r
48143     },\r
48144 \r
48145     // private\r
48146     onNodeOver : function(n, dd, e, data){\r
48147         var pt = this.getDropPoint(e, n, dd);\r
48148         var node = n.node;\r
48149         \r
48150         // auto node expand check\r
48151         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){\r
48152             this.queueExpand(node);\r
48153         }else if(pt != "append"){\r
48154             this.cancelExpand();\r
48155         }\r
48156         \r
48157         // set the insert point style on the target node\r
48158         var returnCls = this.dropNotAllowed;\r
48159         if(this.isValidDropPoint(n, pt, dd, e, data)){\r
48160            if(pt){\r
48161                var el = n.ddel;\r
48162                var cls;\r
48163                if(pt == "above"){\r
48164                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";\r
48165                    cls = "x-tree-drag-insert-above";\r
48166                }else if(pt == "below"){\r
48167                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";\r
48168                    cls = "x-tree-drag-insert-below";\r
48169                }else{\r
48170                    returnCls = "x-tree-drop-ok-append";\r
48171                    cls = "x-tree-drag-append";\r
48172                }\r
48173                if(this.lastInsertClass != cls){\r
48174                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);\r
48175                    this.lastInsertClass = cls;\r
48176                }\r
48177            }\r
48178        }\r
48179        return returnCls;\r
48180     },\r
48181 \r
48182     // private\r
48183     onNodeOut : function(n, dd, e, data){\r
48184         this.cancelExpand();\r
48185         this.removeDropIndicators(n);\r
48186     },\r
48187 \r
48188     // private\r
48189     onNodeDrop : function(n, dd, e, data){\r
48190         var point = this.getDropPoint(e, n, dd);\r
48191         var targetNode = n.node;\r
48192         targetNode.ui.startDrop();\r
48193         if(!this.isValidDropPoint(n, point, dd, e, data)){\r
48194             targetNode.ui.endDrop();\r
48195             return false;\r
48196         }\r
48197         // first try to find the drop node\r
48198         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);\r
48199         return this.processDrop(targetNode, data, point, dd, e, dropNode);\r
48200     },\r
48201     \r
48202     onContainerDrop : function(dd, e, data){\r
48203         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
48204             var targetNode = this.tree.getRootNode();       \r
48205             targetNode.ui.startDrop();\r
48206             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);\r
48207             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);\r
48208         }\r
48209         return false;\r
48210     },\r
48211     \r
48212     // private\r
48213     processDrop: function(target, data, point, dd, e, dropNode){\r
48214         var dropEvent = {\r
48215             tree : this.tree,\r
48216             target: target,\r
48217             data: data,\r
48218             point: point,\r
48219             source: dd,\r
48220             rawEvent: e,\r
48221             dropNode: dropNode,\r
48222             cancel: !dropNode,\r
48223             dropStatus: false\r
48224         };\r
48225         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);\r
48226         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){\r
48227             target.ui.endDrop();\r
48228             return dropEvent.dropStatus;\r
48229         }\r
48230     \r
48231         target = dropEvent.target;\r
48232         if(point == 'append' && !target.isExpanded()){\r
48233             target.expand(false, null, function(){\r
48234                 this.completeDrop(dropEvent);\r
48235             }.createDelegate(this));\r
48236         }else{\r
48237             this.completeDrop(dropEvent);\r
48238         }\r
48239         return true;\r
48240     },\r
48241 \r
48242     // private\r
48243     completeDrop : function(de){\r
48244         var ns = de.dropNode, p = de.point, t = de.target;\r
48245         if(!Ext.isArray(ns)){\r
48246             ns = [ns];\r
48247         }\r
48248         var n;\r
48249         for(var i = 0, len = ns.length; i < len; i++){\r
48250             n = ns[i];\r
48251             if(p == "above"){\r
48252                 t.parentNode.insertBefore(n, t);\r
48253             }else if(p == "below"){\r
48254                 t.parentNode.insertBefore(n, t.nextSibling);\r
48255             }else{\r
48256                 t.appendChild(n);\r
48257             }\r
48258         }\r
48259         n.ui.focus();\r
48260         if(Ext.enableFx && this.tree.hlDrop){\r
48261             n.ui.highlight();\r
48262         }\r
48263         t.ui.endDrop();\r
48264         this.tree.fireEvent("nodedrop", de);\r
48265     },\r
48266 \r
48267     // private\r
48268     afterNodeMoved : function(dd, data, e, targetNode, dropNode){\r
48269         if(Ext.enableFx && this.tree.hlDrop){\r
48270             dropNode.ui.focus();\r
48271             dropNode.ui.highlight();\r
48272         }\r
48273         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);\r
48274     },\r
48275 \r
48276     // private\r
48277     getTree : function(){\r
48278         return this.tree;\r
48279     },\r
48280 \r
48281     // private\r
48282     removeDropIndicators : function(n){\r
48283         if(n && n.ddel){\r
48284             var el = n.ddel;\r
48285             Ext.fly(el).removeClass([\r
48286                     "x-tree-drag-insert-above",\r
48287                     "x-tree-drag-insert-below",\r
48288                     "x-tree-drag-append"]);\r
48289             this.lastInsertClass = "_noclass";\r
48290         }\r
48291     },\r
48292 \r
48293     // private\r
48294     beforeDragDrop : function(target, e, id){\r
48295         this.cancelExpand();\r
48296         return true;\r
48297     },\r
48298 \r
48299     // private\r
48300     afterRepair : function(data){\r
48301         if(data && Ext.enableFx){\r
48302             data.node.ui.highlight();\r
48303         }\r
48304         this.hideProxy();\r
48305     }    \r
48306 });\r
48307 \r
48308 }/**\r
48309  * @class Ext.tree.TreeDragZone\r
48310  * @extends Ext.dd.DragZone\r
48311  * @constructor\r
48312  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging\r
48313  * @param {Object} config\r
48314  */\r
48315 if(Ext.dd.DragZone){\r
48316 Ext.tree.TreeDragZone = function(tree, config){\r
48317     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);\r
48318     /**\r
48319     * The TreePanel for this drag zone\r
48320     * @type Ext.tree.TreePanel\r
48321     * @property\r
48322     */\r
48323     this.tree = tree;\r
48324 };\r
48325 \r
48326 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {\r
48327     /**\r
48328      * @cfg {String} ddGroup\r
48329      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
48330      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
48331      */\r
48332     ddGroup : "TreeDD",\r
48333 \r
48334     // private\r
48335     onBeforeDrag : function(data, e){\r
48336         var n = data.node;\r
48337         return n && n.draggable && !n.disabled;\r
48338     },\r
48339 \r
48340     // private\r
48341     onInitDrag : function(e){\r
48342         var data = this.dragData;\r
48343         this.tree.getSelectionModel().select(data.node);\r
48344         this.tree.eventModel.disable();\r
48345         this.proxy.update("");\r
48346         data.node.ui.appendDDGhost(this.proxy.ghost.dom);\r
48347         this.tree.fireEvent("startdrag", this.tree, data.node, e);\r
48348     },\r
48349 \r
48350     // private\r
48351     getRepairXY : function(e, data){\r
48352         return data.node.ui.getDDRepairXY();\r
48353     },\r
48354 \r
48355     // private\r
48356     onEndDrag : function(data, e){\r
48357         this.tree.eventModel.enable.defer(100, this.tree.eventModel);\r
48358         this.tree.fireEvent("enddrag", this.tree, data.node, e);\r
48359     },\r
48360 \r
48361     // private\r
48362     onValidDrop : function(dd, e, id){\r
48363         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);\r
48364         this.hideProxy();\r
48365     },\r
48366 \r
48367     // private\r
48368     beforeInvalidDrop : function(e, id){\r
48369         // this scrolls the original position back into view\r
48370         var sm = this.tree.getSelectionModel();\r
48371         sm.clearSelections();\r
48372         sm.select(this.dragData.node);\r
48373     },\r
48374     \r
48375     // private\r
48376     afterRepair : function(){\r
48377         if (Ext.enableFx && this.tree.hlDrop) {\r
48378             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
48379         }\r
48380         this.dragging = false;\r
48381     }\r
48382 });\r
48383 }/**
48384  * @class Ext.tree.TreeEditor
48385  * @extends Ext.Editor
48386  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
48387  * as the editor field.
48388  * @constructor
48389  * @param {TreePanel} tree
48390  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
48391  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
48392  * @param {Object} config (optional) A TreeEditor config object
48393  */
48394 Ext.tree.TreeEditor = function(tree, fc, config){
48395     fc = fc || {};
48396     var field = fc.events ? fc : new Ext.form.TextField(fc);
48397     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
48398
48399     this.tree = tree;
48400
48401     if(!tree.rendered){
48402         tree.on('render', this.initEditor, this);
48403     }else{
48404         this.initEditor(tree);
48405     }
48406 };
48407
48408 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
48409     /**
48410      * @cfg {String} alignment
48411      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
48412      */
48413     alignment: "l-l",
48414     // inherit
48415     autoSize: false,
48416     /**
48417      * @cfg {Boolean} hideEl
48418      * True to hide the bound element while the editor is displayed (defaults to false)
48419      */
48420     hideEl : false,
48421     /**
48422      * @cfg {String} cls
48423      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
48424      */
48425     cls: "x-small-editor x-tree-editor",
48426     /**
48427      * @cfg {Boolean} shim
48428      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
48429      */
48430     shim:false,
48431     // inherit
48432     shadow:"frame",
48433     /**
48434      * @cfg {Number} maxWidth
48435      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
48436      * the containing tree element's size, it will be automatically limited for you to the container width, taking
48437      * scroll and client offsets into account prior to each edit.
48438      */
48439     maxWidth: 250,
48440     /**
48441      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
48442      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
48443      * the editor for the node will display, otherwise it will be processed as a regular click.
48444      */
48445     editDelay : 350,
48446
48447     initEditor : function(tree){
48448         tree.on('beforeclick', this.beforeNodeClick, this);
48449         tree.on('dblclick', this.onNodeDblClick, this);
48450         this.on('complete', this.updateNode, this);
48451         this.on('beforestartedit', this.fitToTree, this);
48452         this.on('startedit', this.bindScroll, this, {delay:10});
48453         this.on('specialkey', this.onSpecialKey, this);
48454     },
48455
48456     // private
48457     fitToTree : function(ed, el){
48458         var td = this.tree.getTreeEl().dom, nd = el.dom;
48459         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
48460             td.scrollLeft = nd.offsetLeft;
48461         }
48462         var w = Math.min(
48463                 this.maxWidth,
48464                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
48465         this.setSize(w, '');
48466     },
48467
48468     /**
48469      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
48470      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
48471      */
48472     triggerEdit : function(node, defer){
48473         this.completeEdit();
48474                 if(node.attributes.editable !== false){
48475            /**
48476             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
48477             * @type Ext.tree.TreeNode
48478             * @property editNode
48479             */
48480                         this.editNode = node;
48481             if(this.tree.autoScroll){
48482                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
48483             }
48484             var value = node.text || '';
48485             if (!Ext.isGecko && Ext.isEmpty(node.text)){
48486                 node.setText('&#160;');
48487             }
48488             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
48489             return false;
48490         }
48491     },
48492
48493     // private
48494     bindScroll : function(){
48495         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
48496     },
48497
48498     // private
48499     beforeNodeClick : function(node, e){
48500         clearTimeout(this.autoEditTimer);
48501         if(this.tree.getSelectionModel().isSelected(node)){
48502             e.stopEvent();
48503             return this.triggerEdit(node);
48504         }
48505     },
48506
48507     onNodeDblClick : function(node, e){
48508         clearTimeout(this.autoEditTimer);
48509     },
48510
48511     // private
48512     updateNode : function(ed, value){
48513         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
48514         this.editNode.setText(value);
48515     },
48516
48517     // private
48518     onHide : function(){
48519         Ext.tree.TreeEditor.superclass.onHide.call(this);
48520         if(this.editNode){
48521             this.editNode.ui.focus.defer(50, this.editNode.ui);
48522         }
48523     },
48524
48525     // private
48526     onSpecialKey : function(field, e){
48527         var k = e.getKey();
48528         if(k == e.ESC){
48529             e.stopEvent();
48530             this.cancelEdit();
48531         }else if(k == e.ENTER && !e.hasModifier()){
48532             e.stopEvent();
48533             this.completeEdit();
48534         }
48535     }
48536 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> \r
48537     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> \r
48538 */\r
48539 \r
48540 var swfobject = function() {\r
48541     \r
48542     var UNDEF = "undefined",\r
48543         OBJECT = "object",\r
48544         SHOCKWAVE_FLASH = "Shockwave Flash",\r
48545         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",\r
48546         FLASH_MIME_TYPE = "application/x-shockwave-flash",\r
48547         EXPRESS_INSTALL_ID = "SWFObjectExprInst",\r
48548         ON_READY_STATE_CHANGE = "onreadystatechange",\r
48549         \r
48550         win = window,\r
48551         doc = document,\r
48552         nav = navigator,\r
48553         \r
48554         plugin = false,\r
48555         domLoadFnArr = [main],\r
48556         regObjArr = [],\r
48557         objIdArr = [],\r
48558         listenersArr = [],\r
48559         storedAltContent,\r
48560         storedAltContentId,\r
48561         storedCallbackFn,\r
48562         storedCallbackObj,\r
48563         isDomLoaded = false,\r
48564         isExpressInstallActive = false,\r
48565         dynamicStylesheet,\r
48566         dynamicStylesheetMedia,\r
48567         autoHideShow = true,\r
48568     \r
48569     /* Centralized function for browser feature detection\r
48570         - User agent string detection is only used when no good alternative is possible\r
48571         - Is executed directly for optimal performance\r
48572     */  \r
48573     ua = function() {\r
48574         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,\r
48575             u = nav.userAgent.toLowerCase(),\r
48576             p = nav.platform.toLowerCase(),\r
48577             windows = p ? /win/.test(p) : /win/.test(u),\r
48578             mac = p ? /mac/.test(p) : /mac/.test(u),\r
48579             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit\r
48580             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\r
48581             playerVersion = [0,0,0],\r
48582             d = null;\r
48583         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {\r
48584             d = nav.plugins[SHOCKWAVE_FLASH].description;\r
48585             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+\r
48586                 plugin = true;\r
48587                 ie = false; // cascaded feature detection for Internet Explorer\r
48588                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");\r
48589                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);\r
48590                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);\r
48591                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;\r
48592             }\r
48593         }\r
48594         else if (typeof win.ActiveXObject != UNDEF) {\r
48595             try {\r
48596                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);\r
48597                 if (a) { // a will return null when ActiveX is disabled\r
48598                     d = a.GetVariable("$version");\r
48599                     if (d) {\r
48600                         ie = true; // cascaded feature detection for Internet Explorer\r
48601                         d = d.split(" ")[1].split(",");\r
48602                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
48603                     }\r
48604                 }\r
48605             }\r
48606             catch(e) {}\r
48607         }\r
48608         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };\r
48609     }(),\r
48610     \r
48611     /* Cross-browser onDomLoad\r
48612         - Will fire an event as soon as the DOM of a web page is loaded\r
48613         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/\r
48614         - Regular onload serves as fallback\r
48615     */ \r
48616     onDomLoad = function() {\r
48617         if (!ua.w3) { return; }\r
48618         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 \r
48619             callDomLoadFunctions();\r
48620         }\r
48621         if (!isDomLoaded) {\r
48622             if (typeof doc.addEventListener != UNDEF) {\r
48623                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);\r
48624             }       \r
48625             if (ua.ie && ua.win) {\r
48626                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {\r
48627                     if (doc.readyState == "complete") {\r
48628                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);\r
48629                         callDomLoadFunctions();\r
48630                     }\r
48631                 });\r
48632                 if (win == top) { // if not inside an iframe\r
48633                     (function(){\r
48634                         if (isDomLoaded) { return; }\r
48635                         try {\r
48636                             doc.documentElement.doScroll("left");\r
48637                         }\r
48638                         catch(e) {\r
48639                             setTimeout(arguments.callee, 0);\r
48640                             return;\r
48641                         }\r
48642                         callDomLoadFunctions();\r
48643                     })();\r
48644                 }\r
48645             }\r
48646             if (ua.wk) {\r
48647                 (function(){\r
48648                     if (isDomLoaded) { return; }\r
48649                     if (!/loaded|complete/.test(doc.readyState)) {\r
48650                         setTimeout(arguments.callee, 0);\r
48651                         return;\r
48652                     }\r
48653                     callDomLoadFunctions();\r
48654                 })();\r
48655             }\r
48656             addLoadEvent(callDomLoadFunctions);\r
48657         }\r
48658     }();\r
48659     \r
48660     function callDomLoadFunctions() {\r
48661         if (isDomLoaded) { return; }\r
48662         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early\r
48663             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));\r
48664             t.parentNode.removeChild(t);\r
48665         }\r
48666         catch (e) { return; }\r
48667         isDomLoaded = true;\r
48668         var dl = domLoadFnArr.length;\r
48669         for (var i = 0; i < dl; i++) {\r
48670             domLoadFnArr[i]();\r
48671         }\r
48672     }\r
48673     \r
48674     function addDomLoadEvent(fn) {\r
48675         if (isDomLoaded) {\r
48676             fn();\r
48677         }\r
48678         else { \r
48679             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+\r
48680         }\r
48681     }\r
48682     \r
48683     /* Cross-browser onload\r
48684         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/\r
48685         - Will fire an event as soon as a web page including all of its assets are loaded \r
48686      */\r
48687     function addLoadEvent(fn) {\r
48688         if (typeof win.addEventListener != UNDEF) {\r
48689             win.addEventListener("load", fn, false);\r
48690         }\r
48691         else if (typeof doc.addEventListener != UNDEF) {\r
48692             doc.addEventListener("load", fn, false);\r
48693         }\r
48694         else if (typeof win.attachEvent != UNDEF) {\r
48695             addListener(win, "onload", fn);\r
48696         }\r
48697         else if (typeof win.onload == "function") {\r
48698             var fnOld = win.onload;\r
48699             win.onload = function() {\r
48700                 fnOld();\r
48701                 fn();\r
48702             };\r
48703         }\r
48704         else {\r
48705             win.onload = fn;\r
48706         }\r
48707     }\r
48708     \r
48709     /* Main function\r
48710         - Will preferably execute onDomLoad, otherwise onload (as a fallback)\r
48711     */\r
48712     function main() { \r
48713         if (plugin) {\r
48714             testPlayerVersion();\r
48715         }\r
48716         else {\r
48717             matchVersions();\r
48718         }\r
48719     }\r
48720     \r
48721     /* Detect the Flash Player version for non-Internet Explorer browsers\r
48722         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:\r
48723           a. Both release and build numbers can be detected\r
48724           b. Avoid wrong descriptions by corrupt installers provided by Adobe\r
48725           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports\r
48726         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available\r
48727     */\r
48728     function testPlayerVersion() {\r
48729         var b = doc.getElementsByTagName("body")[0];\r
48730         var o = createElement(OBJECT);\r
48731         o.setAttribute("type", FLASH_MIME_TYPE);\r
48732         var t = b.appendChild(o);\r
48733         if (t) {\r
48734             var counter = 0;\r
48735             (function(){\r
48736                 if (typeof t.GetVariable != UNDEF) {\r
48737                     var d = t.GetVariable("$version");\r
48738                     if (d) {\r
48739                         d = d.split(" ")[1].split(",");\r
48740                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
48741                     }\r
48742                 }\r
48743                 else if (counter < 10) {\r
48744                     counter++;\r
48745                     setTimeout(arguments.callee, 10);\r
48746                     return;\r
48747                 }\r
48748                 b.removeChild(o);\r
48749                 t = null;\r
48750                 matchVersions();\r
48751             })();\r
48752         }\r
48753         else {\r
48754             matchVersions();\r
48755         }\r
48756     }\r
48757     \r
48758     /* Perform Flash Player and SWF version matching; static publishing only\r
48759     */\r
48760     function matchVersions() {\r
48761         var rl = regObjArr.length;\r
48762         if (rl > 0) {\r
48763             for (var i = 0; i < rl; i++) { // for each registered object element\r
48764                 var id = regObjArr[i].id;\r
48765                 var cb = regObjArr[i].callbackFn;\r
48766                 var cbObj = {success:false, id:id};\r
48767                 if (ua.pv[0] > 0) {\r
48768                     var obj = getElementById(id);\r
48769                     if (obj) {\r
48770                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!\r
48771                             setVisibility(id, true);\r
48772                             if (cb) {\r
48773                                 cbObj.success = true;\r
48774                                 cbObj.ref = getObjectById(id);\r
48775                                 cb(cbObj);\r
48776                             }\r
48777                         }\r
48778                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported\r
48779                             var att = {};\r
48780                             att.data = regObjArr[i].expressInstall;\r
48781                             att.width = obj.getAttribute("width") || "0";\r
48782                             att.height = obj.getAttribute("height") || "0";\r
48783                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }\r
48784                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }\r
48785                             // parse HTML object param element's name-value pairs\r
48786                             var par = {};\r
48787                             var p = obj.getElementsByTagName("param");\r
48788                             var pl = p.length;\r
48789                             for (var j = 0; j < pl; j++) {\r
48790                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {\r
48791                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");\r
48792                                 }\r
48793                             }\r
48794                             showExpressInstall(att, par, id, cb);\r
48795                         }\r
48796                         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\r
48797                             displayAltContent(obj);\r
48798                             if (cb) { cb(cbObj); }\r
48799                         }\r
48800                     }\r
48801                 }\r
48802                 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)\r
48803                     setVisibility(id, true);\r
48804                     if (cb) {\r
48805                         var o = getObjectById(id); // test whether there is an HTML object element or not\r
48806                         if (o && typeof o.SetVariable != UNDEF) { \r
48807                             cbObj.success = true;\r
48808                             cbObj.ref = o;\r
48809                         }\r
48810                         cb(cbObj);\r
48811                     }\r
48812                 }\r
48813             }\r
48814         }\r
48815     }\r
48816     \r
48817     function getObjectById(objectIdStr) {\r
48818         var r = null;\r
48819         var o = getElementById(objectIdStr);\r
48820         if (o && o.nodeName == "OBJECT") {\r
48821             if (typeof o.SetVariable != UNDEF) {\r
48822                 r = o;\r
48823             }\r
48824             else {\r
48825                 var n = o.getElementsByTagName(OBJECT)[0];\r
48826                 if (n) {\r
48827                     r = n;\r
48828                 }\r
48829             }\r
48830         }\r
48831         return r;\r
48832     }\r
48833     \r
48834     /* Requirements for Adobe Express Install\r
48835         - only one instance can be active at a time\r
48836         - fp 6.0.65 or higher\r
48837         - Win/Mac OS only\r
48838         - no Webkit engines older than version 312\r
48839     */\r
48840     function canExpressInstall() {\r
48841         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);\r
48842     }\r
48843     \r
48844     /* Show the Adobe Express Install dialog\r
48845         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75\r
48846     */\r
48847     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {\r
48848         isExpressInstallActive = true;\r
48849         storedCallbackFn = callbackFn || null;\r
48850         storedCallbackObj = {success:false, id:replaceElemIdStr};\r
48851         var obj = getElementById(replaceElemIdStr);\r
48852         if (obj) {\r
48853             if (obj.nodeName == "OBJECT") { // static publishing\r
48854                 storedAltContent = abstractAltContent(obj);\r
48855                 storedAltContentId = null;\r
48856             }\r
48857             else { // dynamic publishing\r
48858                 storedAltContent = obj;\r
48859                 storedAltContentId = replaceElemIdStr;\r
48860             }\r
48861             att.id = EXPRESS_INSTALL_ID;\r
48862             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }\r
48863             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }\r
48864             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";\r
48865             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",\r
48866                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;\r
48867             if (typeof par.flashvars != UNDEF) {\r
48868                 par.flashvars += "&" + fv;\r
48869             }\r
48870             else {\r
48871                 par.flashvars = fv;\r
48872             }\r
48873             // 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,\r
48874             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
48875             if (ua.ie && ua.win && obj.readyState != 4) {\r
48876                 var newObj = createElement("div");\r
48877                 replaceElemIdStr += "SWFObjectNew";\r
48878                 newObj.setAttribute("id", replaceElemIdStr);\r
48879                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf\r
48880                 obj.style.display = "none";\r
48881                 (function(){\r
48882                     if (obj.readyState == 4) {\r
48883                         obj.parentNode.removeChild(obj);\r
48884                     }\r
48885                     else {\r
48886                         setTimeout(arguments.callee, 10);\r
48887                     }\r
48888                 })();\r
48889             }\r
48890             createSWF(att, par, replaceElemIdStr);\r
48891         }\r
48892     }\r
48893     \r
48894     /* Functions to abstract and display alternative content\r
48895     */\r
48896     function displayAltContent(obj) {\r
48897         if (ua.ie && ua.win && obj.readyState != 4) {\r
48898             // 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,\r
48899             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
48900             var el = createElement("div");\r
48901             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content\r
48902             el.parentNode.replaceChild(abstractAltContent(obj), el);\r
48903             obj.style.display = "none";\r
48904             (function(){\r
48905                 if (obj.readyState == 4) {\r
48906                     obj.parentNode.removeChild(obj);\r
48907                 }\r
48908                 else {\r
48909                     setTimeout(arguments.callee, 10);\r
48910                 }\r
48911             })();\r
48912         }\r
48913         else {\r
48914             obj.parentNode.replaceChild(abstractAltContent(obj), obj);\r
48915         }\r
48916     } \r
48917 \r
48918     function abstractAltContent(obj) {\r
48919         var ac = createElement("div");\r
48920         if (ua.win && ua.ie) {\r
48921             ac.innerHTML = obj.innerHTML;\r
48922         }\r
48923         else {\r
48924             var nestedObj = obj.getElementsByTagName(OBJECT)[0];\r
48925             if (nestedObj) {\r
48926                 var c = nestedObj.childNodes;\r
48927                 if (c) {\r
48928                     var cl = c.length;\r
48929                     for (var i = 0; i < cl; i++) {\r
48930                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {\r
48931                             ac.appendChild(c[i].cloneNode(true));\r
48932                         }\r
48933                     }\r
48934                 }\r
48935             }\r
48936         }\r
48937         return ac;\r
48938     }\r
48939     \r
48940     /* Cross-browser dynamic SWF creation\r
48941     */\r
48942     function createSWF(attObj, parObj, id) {\r
48943         var r, el = getElementById(id);\r
48944         if (ua.wk && ua.wk < 312) { return r; }\r
48945         if (el) {\r
48946             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content\r
48947                 attObj.id = id;\r
48948             }\r
48949             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML\r
48950                 var att = "";\r
48951                 for (var i in attObj) {\r
48952                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries\r
48953                         if (i.toLowerCase() == "data") {\r
48954                             parObj.movie = attObj[i];\r
48955                         }\r
48956                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
48957                             att += ' class="' + attObj[i] + '"';\r
48958                         }\r
48959                         else if (i.toLowerCase() != "classid") {\r
48960                             att += ' ' + i + '="' + attObj[i] + '"';\r
48961                         }\r
48962                     }\r
48963                 }\r
48964                 var par = "";\r
48965                 for (var j in parObj) {\r
48966                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries\r
48967                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';\r
48968                     }\r
48969                 }\r
48970                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';\r
48971                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)\r
48972                 r = getElementById(attObj.id);  \r
48973             }\r
48974             else { // well-behaving browsers\r
48975                 var o = createElement(OBJECT);\r
48976                 o.setAttribute("type", FLASH_MIME_TYPE);\r
48977                 for (var m in attObj) {\r
48978                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries\r
48979                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
48980                             o.setAttribute("class", attObj[m]);\r
48981                         }\r
48982                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute\r
48983                             o.setAttribute(m, attObj[m]);\r
48984                         }\r
48985                     }\r
48986                 }\r
48987                 for (var n in parObj) {\r
48988                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element\r
48989                         createObjParam(o, n, parObj[n]);\r
48990                     }\r
48991                 }\r
48992                 el.parentNode.replaceChild(o, el);\r
48993                 r = o;\r
48994             }\r
48995         }\r
48996         return r;\r
48997     }\r
48998     \r
48999     function createObjParam(el, pName, pValue) {\r
49000         var p = createElement("param");\r
49001         p.setAttribute("name", pName);  \r
49002         p.setAttribute("value", pValue);\r
49003         el.appendChild(p);\r
49004     }\r
49005     \r
49006     /* Cross-browser SWF removal\r
49007         - Especially needed to safely and completely remove a SWF in Internet Explorer\r
49008     */\r
49009     function removeSWF(id) {\r
49010         var obj = getElementById(id);\r
49011         if (obj && obj.nodeName == "OBJECT") {\r
49012             if (ua.ie && ua.win) {\r
49013                 obj.style.display = "none";\r
49014                 (function(){\r
49015                     if (obj.readyState == 4) {\r
49016                         removeObjectInIE(id);\r
49017                     }\r
49018                     else {\r
49019                         setTimeout(arguments.callee, 10);\r
49020                     }\r
49021                 })();\r
49022             }\r
49023             else {\r
49024                 obj.parentNode.removeChild(obj);\r
49025             }\r
49026         }\r
49027     }\r
49028     \r
49029     function removeObjectInIE(id) {\r
49030         var obj = getElementById(id);\r
49031         if (obj) {\r
49032             for (var i in obj) {\r
49033                 if (typeof obj[i] == "function") {\r
49034                     obj[i] = null;\r
49035                 }\r
49036             }\r
49037             obj.parentNode.removeChild(obj);\r
49038         }\r
49039     }\r
49040     \r
49041     /* Functions to optimize JavaScript compression\r
49042     */\r
49043     function getElementById(id) {\r
49044         var el = null;\r
49045         try {\r
49046             el = doc.getElementById(id);\r
49047         }\r
49048         catch (e) {}\r
49049         return el;\r
49050     }\r
49051     \r
49052     function createElement(el) {\r
49053         return doc.createElement(el);\r
49054     }\r
49055     \r
49056     /* Updated attachEvent function for Internet Explorer\r
49057         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks\r
49058     */  \r
49059     function addListener(target, eventType, fn) {\r
49060         target.attachEvent(eventType, fn);\r
49061         listenersArr[listenersArr.length] = [target, eventType, fn];\r
49062     }\r
49063     \r
49064     /* Flash Player and SWF content version matching\r
49065     */\r
49066     function hasPlayerVersion(rv) {\r
49067         var pv = ua.pv, v = rv.split(".");\r
49068         v[0] = parseInt(v[0], 10);\r
49069         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"\r
49070         v[2] = parseInt(v[2], 10) || 0;\r
49071         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;\r
49072     }\r
49073     \r
49074     /* Cross-browser dynamic CSS creation\r
49075         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php\r
49076     */  \r
49077     function createCSS(sel, decl, media, newStyle) {\r
49078         if (ua.ie && ua.mac) { return; }\r
49079         var h = doc.getElementsByTagName("head")[0];\r
49080         if (!h) { return; } // to also support badly authored HTML pages that lack a head element\r
49081         var m = (media && typeof media == "string") ? media : "screen";\r
49082         if (newStyle) {\r
49083             dynamicStylesheet = null;\r
49084             dynamicStylesheetMedia = null;\r
49085         }\r
49086         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { \r
49087             // create dynamic stylesheet + get a global reference to it\r
49088             var s = createElement("style");\r
49089             s.setAttribute("type", "text/css");\r
49090             s.setAttribute("media", m);\r
49091             dynamicStylesheet = h.appendChild(s);\r
49092             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {\r
49093                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];\r
49094             }\r
49095             dynamicStylesheetMedia = m;\r
49096         }\r
49097         // add style rule\r
49098         if (ua.ie && ua.win) {\r
49099             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {\r
49100                 dynamicStylesheet.addRule(sel, decl);\r
49101             }\r
49102         }\r
49103         else {\r
49104             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {\r
49105                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));\r
49106             }\r
49107         }\r
49108     }\r
49109     \r
49110     function setVisibility(id, isVisible) {\r
49111         if (!autoHideShow) { return; }\r
49112         var v = isVisible ? "visible" : "hidden";\r
49113         if (isDomLoaded && getElementById(id)) {\r
49114             getElementById(id).style.visibility = v;\r
49115         }\r
49116         else {\r
49117             createCSS("#" + id, "visibility:" + v);\r
49118         }\r
49119     }\r
49120 \r
49121     /* Filter to avoid XSS attacks\r
49122     */\r
49123     function urlEncodeIfNecessary(s) {\r
49124         var regex = /[\\\"<>\.;]/;\r
49125         var hasBadChars = regex.exec(s) != null;\r
49126         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;\r
49127     }\r
49128     \r
49129     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)\r
49130     */\r
49131     var cleanup = function() {\r
49132         if (ua.ie && ua.win) {\r
49133             window.attachEvent("onunload", function() {\r
49134                 // remove listeners to avoid memory leaks\r
49135                 var ll = listenersArr.length;\r
49136                 for (var i = 0; i < ll; i++) {\r
49137                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);\r
49138                 }\r
49139                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect\r
49140                 var il = objIdArr.length;\r
49141                 for (var j = 0; j < il; j++) {\r
49142                     removeSWF(objIdArr[j]);\r
49143                 }\r
49144                 // cleanup library's main closures to avoid memory leaks\r
49145                 for (var k in ua) {\r
49146                     ua[k] = null;\r
49147                 }\r
49148                 ua = null;\r
49149                 for (var l in swfobject) {\r
49150                     swfobject[l] = null;\r
49151                 }\r
49152                 swfobject = null;\r
49153             });\r
49154         }\r
49155     }();\r
49156     \r
49157     return {\r
49158         /* Public API\r
49159             - Reference: http://code.google.com/p/swfobject/wiki/documentation\r
49160         */ \r
49161         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {\r
49162             if (ua.w3 && objectIdStr && swfVersionStr) {\r
49163                 var regObj = {};\r
49164                 regObj.id = objectIdStr;\r
49165                 regObj.swfVersion = swfVersionStr;\r
49166                 regObj.expressInstall = xiSwfUrlStr;\r
49167                 regObj.callbackFn = callbackFn;\r
49168                 regObjArr[regObjArr.length] = regObj;\r
49169                 setVisibility(objectIdStr, false);\r
49170             }\r
49171             else if (callbackFn) {\r
49172                 callbackFn({success:false, id:objectIdStr});\r
49173             }\r
49174         },\r
49175         \r
49176         getObjectById: function(objectIdStr) {\r
49177             if (ua.w3) {\r
49178                 return getObjectById(objectIdStr);\r
49179             }\r
49180         },\r
49181         \r
49182         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {\r
49183             var callbackObj = {success:false, id:replaceElemIdStr};\r
49184             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {\r
49185                 setVisibility(replaceElemIdStr, false);\r
49186                 addDomLoadEvent(function() {\r
49187                     widthStr += ""; // auto-convert to string\r
49188                     heightStr += "";\r
49189                     var att = {};\r
49190                     if (attObj && typeof attObj === OBJECT) {\r
49191                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs\r
49192                             att[i] = attObj[i];\r
49193                         }\r
49194                     }\r
49195                     att.data = swfUrlStr;\r
49196                     att.width = widthStr;\r
49197                     att.height = heightStr;\r
49198                     var par = {}; \r
49199                     if (parObj && typeof parObj === OBJECT) {\r
49200                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs\r
49201                             par[j] = parObj[j];\r
49202                         }\r
49203                     }\r
49204                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {\r
49205                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs\r
49206                             if (typeof par.flashvars != UNDEF) {\r
49207                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];\r
49208                             }\r
49209                             else {\r
49210                                 par.flashvars = k + "=" + flashvarsObj[k];\r
49211                             }\r
49212                         }\r
49213                     }\r
49214                     if (hasPlayerVersion(swfVersionStr)) { // create SWF\r
49215                         var obj = createSWF(att, par, replaceElemIdStr);\r
49216                         if (att.id == replaceElemIdStr) {\r
49217                             setVisibility(replaceElemIdStr, true);\r
49218                         }\r
49219                         callbackObj.success = true;\r
49220                         callbackObj.ref = obj;\r
49221                     }\r
49222                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install\r
49223                         att.data = xiSwfUrlStr;\r
49224                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
49225                         return;\r
49226                     }\r
49227                     else { // show alternative content\r
49228                         setVisibility(replaceElemIdStr, true);\r
49229                     }\r
49230                     if (callbackFn) { callbackFn(callbackObj); }\r
49231                 });\r
49232             }\r
49233             else if (callbackFn) { callbackFn(callbackObj); }\r
49234         },\r
49235         \r
49236         switchOffAutoHideShow: function() {\r
49237             autoHideShow = false;\r
49238         },\r
49239         \r
49240         ua: ua,\r
49241         \r
49242         getFlashPlayerVersion: function() {\r
49243             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };\r
49244         },\r
49245         \r
49246         hasFlashPlayerVersion: hasPlayerVersion,\r
49247         \r
49248         createSWF: function(attObj, parObj, replaceElemIdStr) {\r
49249             if (ua.w3) {\r
49250                 return createSWF(attObj, parObj, replaceElemIdStr);\r
49251             }\r
49252             else {\r
49253                 return undefined;\r
49254             }\r
49255         },\r
49256         \r
49257         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {\r
49258             if (ua.w3 && canExpressInstall()) {\r
49259                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
49260             }\r
49261         },\r
49262         \r
49263         removeSWF: function(objElemIdStr) {\r
49264             if (ua.w3) {\r
49265                 removeSWF(objElemIdStr);\r
49266             }\r
49267         },\r
49268         \r
49269         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {\r
49270             if (ua.w3) {\r
49271                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);\r
49272             }\r
49273         },\r
49274         \r
49275         addDomLoadEvent: addDomLoadEvent,\r
49276         \r
49277         addLoadEvent: addLoadEvent,\r
49278         \r
49279         getQueryParamValue: function(param) {\r
49280             var q = doc.location.search || doc.location.hash;\r
49281             if (q) {\r
49282                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark\r
49283                 if (param == null) {\r
49284                     return urlEncodeIfNecessary(q);\r
49285                 }\r
49286                 var pairs = q.split("&");\r
49287                 for (var i = 0; i < pairs.length; i++) {\r
49288                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {\r
49289                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));\r
49290                     }\r
49291                 }\r
49292             }\r
49293             return "";\r
49294         },\r
49295         \r
49296         // For internal usage only\r
49297         expressInstallCallback: function() {\r
49298             if (isExpressInstallActive) {\r
49299                 var obj = getElementById(EXPRESS_INSTALL_ID);\r
49300                 if (obj && storedAltContent) {\r
49301                     obj.parentNode.replaceChild(storedAltContent, obj);\r
49302                     if (storedAltContentId) {\r
49303                         setVisibility(storedAltContentId, true);\r
49304                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }\r
49305                     }\r
49306                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }\r
49307                 }\r
49308                 isExpressInstallActive = false;\r
49309             } \r
49310         }\r
49311     };\r
49312 }();\r
49313 /**
49314  * @class Ext.FlashComponent
49315  * @extends Ext.BoxComponent
49316  * @constructor
49317  * @xtype flash
49318  */
49319 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
49320     /**
49321      * @cfg {String} flashVersion
49322      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.45'</tt>.
49323      */
49324     flashVersion : '9.0.45',
49325
49326     /**
49327      * @cfg {String} backgroundColor
49328      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
49329      */
49330     backgroundColor: '#ffffff',
49331
49332     /**
49333      * @cfg {String} wmode
49334      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
49335      */
49336     wmode: 'opaque',
49337
49338     /**
49339      * @cfg {String} url
49340      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
49341      */
49342     url: undefined,
49343     swfId : undefined,
49344     swfWidth: '100%',
49345     swfHeight: '100%',
49346
49347     /**
49348      * @cfg {Boolean} expressInstall
49349      * True to prompt the user to install flash if not installed. Note that this uses
49350      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
49351      */
49352     expressInstall: false,
49353
49354     initComponent : function(){
49355         Ext.FlashComponent.superclass.initComponent.call(this);
49356
49357         this.addEvents('initialize');
49358     },
49359
49360     onRender : function(){
49361         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
49362
49363         var params = {
49364             allowScriptAccess: 'always',
49365             bgcolor: this.backgroundColor,
49366             wmode: this.wmode
49367         }, vars = {
49368             allowedDomain: document.location.hostname,
49369             elementID: this.getId(),
49370             eventHandler: 'Ext.FlashEventProxy.onEvent'
49371         };
49372
49373         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
49374             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
49375
49376         this.swf = Ext.getDom(this.id);
49377         this.el = Ext.get(this.swf);
49378     },
49379
49380     getSwfId : function(){
49381         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
49382     },
49383
49384     getId : function(){
49385         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
49386     },
49387
49388     onFlashEvent : function(e){
49389         switch(e.type){
49390             case "swfReady":
49391                 this.initSwf();
49392                 return;
49393             case "log":
49394                 return;
49395         }
49396         e.component = this;
49397         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
49398     },
49399
49400     initSwf : function(){
49401         this.onSwfReady(!!this.isInitialized);
49402         this.isInitialized = true;
49403         this.fireEvent('initialize', this);
49404     },
49405
49406     beforeDestroy: function(){
49407         if(this.rendered){
49408             swfobject.removeSWF(this.swf.id);
49409         }
49410         Ext.FlashComponent.superclass.beforeDestroy.call(this);
49411     },
49412
49413     onSwfReady : Ext.emptyFn
49414 });
49415
49416 /**
49417  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
49418  * @static
49419  * @type String
49420  */
49421 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
49422
49423 Ext.reg('flash', Ext.FlashComponent);/**\r
49424  * @class Ext.FlashProxy\r
49425  * @singleton\r
49426  */\r
49427 Ext.FlashEventProxy = {\r
49428     onEvent : function(id, e){\r
49429         var fp = Ext.getCmp(id);\r
49430         if(fp){\r
49431             fp.onFlashEvent(e);\r
49432         }else{\r
49433             arguments.callee.defer(10, this, [id, e]);\r
49434         }\r
49435     }\r
49436 }/**\r
49437  * @class Ext.chart.Chart\r
49438  * @extends Ext.FlashComponent\r
49439  * The Ext.chart package provides the capability to visualize data with flash based charting.\r
49440  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.\r
49441  * @constructor\r
49442  * @xtype chart\r
49443  */\r
49444  \r
49445  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {\r
49446     refreshBuffer: 100,\r
49447 \r
49448     /**\r
49449      * @cfg {Object} chartStyle\r
49450      * Sets styles for this chart. Contains a number of default values. Modifying this property will override\r
49451      * the base styles on the chart.\r
49452      */\r
49453     chartStyle: {\r
49454         padding: 10,\r
49455         animationEnabled: true,\r
49456         font: {\r
49457             name: 'Tahoma',\r
49458             color: 0x444444,\r
49459             size: 11\r
49460         },\r
49461         dataTip: {\r
49462             padding: 5,\r
49463             border: {\r
49464                 color: 0x99bbe8,\r
49465                 size:1\r
49466             },\r
49467             background: {\r
49468                 color: 0xDAE7F6,\r
49469                 alpha: .9\r
49470             },\r
49471             font: {\r
49472                 name: 'Tahoma',\r
49473                 color: 0x15428B,\r
49474                 size: 10,\r
49475                 bold: true\r
49476             }\r
49477         }\r
49478     },\r
49479     \r
49480     /**\r
49481      * @cfg {String} url\r
49482      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should\r
49483      * be modified to point to the local charts resource.\r
49484      */\r
49485     \r
49486     /**\r
49487      * @cfg {Object} extraStyle\r
49488      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.\r
49489      */\r
49490     extraStyle: null,\r
49491     \r
49492     /**\r
49493      * @cfg {Boolean} disableCaching\r
49494      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.\r
49495      */\r
49496     disableCaching: Ext.isIE || Ext.isOpera,\r
49497     disableCacheParam: '_dc',\r
49498 \r
49499     initComponent : function(){\r
49500         Ext.chart.Chart.superclass.initComponent.call(this);\r
49501         if(!this.url){\r
49502             this.url = Ext.chart.Chart.CHART_URL;\r
49503         }\r
49504         if(this.disableCaching){\r
49505             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));\r
49506         }\r
49507         this.addEvents(\r
49508             'itemmouseover',\r
49509             'itemmouseout',\r
49510             'itemclick',\r
49511             'itemdoubleclick',\r
49512             'itemdragstart',\r
49513             'itemdrag',\r
49514             'itemdragend'\r
49515         );\r
49516         this.store = Ext.StoreMgr.lookup(this.store);\r
49517     },\r
49518 \r
49519     /**\r
49520      * Sets a single style value on the Chart instance.\r
49521      *\r
49522      * @param name {String} Name of the Chart style value to change.\r
49523      * @param value {Object} New value to pass to the Chart style.\r
49524      */\r
49525      setStyle: function(name, value){\r
49526          this.swf.setStyle(name, Ext.encode(value));\r
49527      },\r
49528 \r
49529     /**\r
49530      * Resets all styles on the Chart instance.\r
49531      *\r
49532      * @param styles {Object} Initializer for all Chart styles.\r
49533      */\r
49534     setStyles: function(styles){\r
49535         this.swf.setStyles(Ext.encode(styles));\r
49536     },\r
49537 \r
49538     /**\r
49539      * Sets the styles on all series in the Chart.\r
49540      *\r
49541      * @param styles {Array} Initializer for all Chart series styles.\r
49542      */\r
49543     setSeriesStyles: function(styles){\r
49544         var s = [];\r
49545         Ext.each(styles, function(style){\r
49546             s.push(Ext.encode(style));\r
49547         });\r
49548         this.swf.setSeriesStyles(s);\r
49549     },\r
49550 \r
49551     setCategoryNames : function(names){\r
49552         this.swf.setCategoryNames(names);\r
49553     },\r
49554 \r
49555     setTipRenderer : function(fn){\r
49556         var chart = this;\r
49557         this.tipFnName = this.createFnProxy(function(item, index, series){\r
49558             var record = chart.store.getAt(index);\r
49559             return fn(chart, record, index, series);\r
49560         }, this.tipFnName);\r
49561         this.swf.setDataTipFunction(this.tipFnName);\r
49562     },\r
49563 \r
49564     setSeries : function(series){\r
49565         this.series = series;\r
49566         this.refresh();\r
49567     },\r
49568 \r
49569     /**\r
49570      * Changes the data store bound to this chart and refreshes it.\r
49571      * @param {Store} store The store to bind to this chart\r
49572      */\r
49573     bindStore : function(store, initial){\r
49574         if(!initial && this.store){\r
49575             this.store.un("datachanged", this.refresh, this);\r
49576             this.store.un("add", this.delayRefresh, this);\r
49577             this.store.un("remove", this.delayRefresh, this);\r
49578             this.store.un("update", this.delayRefresh, this);\r
49579             this.store.un("clear", this.refresh, this);\r
49580             if(store !== this.store && this.store.autoDestroy){\r
49581                 this.store.destroy();\r
49582             }\r
49583         }\r
49584         if(store){\r
49585             store = Ext.StoreMgr.lookup(store);\r
49586             store.on({\r
49587                 scope: this,\r
49588                 datachanged: this.refresh,\r
49589                 add: this.delayRefresh,\r
49590                 remove: this.delayRefresh,\r
49591                 update: this.delayRefresh,\r
49592                 clear: this.refresh\r
49593             });\r
49594         }\r
49595         this.store = store;\r
49596         if(store && !initial){\r
49597             this.refresh();\r
49598         }\r
49599     },\r
49600 \r
49601     onSwfReady : function(isReset){\r
49602         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);\r
49603         this.swf.setType(this.type);\r
49604 \r
49605         if(this.chartStyle){\r
49606             this.setStyles(Ext.apply(this.extraStyle || {}, this.chartStyle));\r
49607         }\r
49608 \r
49609         if(this.categoryNames){\r
49610             this.setCategoryNames(this.categoryNames);\r
49611         }\r
49612 \r
49613         if(this.tipRenderer){\r
49614             this.setTipRenderer(this.tipRenderer);\r
49615         }\r
49616         if(!isReset){\r
49617             this.bindStore(this.store, true);\r
49618         }\r
49619         this.refresh.defer(10, this);\r
49620     },\r
49621 \r
49622     delayRefresh : function(){\r
49623         if(!this.refreshTask){\r
49624             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);\r
49625         }\r
49626         this.refreshTask.delay(this.refreshBuffer);\r
49627     },\r
49628 \r
49629     refresh : function(){\r
49630         var styleChanged = false;\r
49631         // convert the store data into something YUI charts can understand\r
49632         var data = [], rs = this.store.data.items;\r
49633         for(var j = 0, len = rs.length; j < len; j++){\r
49634             data[j] = rs[j].data;\r
49635         }\r
49636         //make a copy of the series definitions so that we aren't\r
49637         //editing them directly.\r
49638         var dataProvider = [];\r
49639         var seriesCount = 0;\r
49640         var currentSeries = null;\r
49641         var i = 0;\r
49642         if(this.series){\r
49643             seriesCount = this.series.length;\r
49644             for(i = 0; i < seriesCount; i++){\r
49645                 currentSeries = this.series[i];\r
49646                 var clonedSeries = {};\r
49647                 for(var prop in currentSeries){\r
49648                     if(prop == "style" && currentSeries.style !== null){\r
49649                         clonedSeries.style = Ext.encode(currentSeries.style);\r
49650                         styleChanged = true;\r
49651                         //we don't want to modify the styles again next time\r
49652                         //so null out the style property.\r
49653                         // this causes issues\r
49654                         // currentSeries.style = null;\r
49655                     } else{\r
49656                         clonedSeries[prop] = currentSeries[prop];\r
49657                     }\r
49658                 }\r
49659                 dataProvider.push(clonedSeries);\r
49660             }\r
49661         }\r
49662 \r
49663         if(seriesCount > 0){\r
49664             for(i = 0; i < seriesCount; i++){\r
49665                 currentSeries = dataProvider[i];\r
49666                 if(!currentSeries.type){\r
49667                     currentSeries.type = this.type;\r
49668                 }\r
49669                 currentSeries.dataProvider = data;\r
49670             }\r
49671         } else{\r
49672             dataProvider.push({type: this.type, dataProvider: data});\r
49673         }\r
49674         this.swf.setDataProvider(dataProvider);\r
49675     },\r
49676 \r
49677     createFnProxy : function(fn, old){\r
49678         if(old){\r
49679             delete window[old];\r
49680         }\r
49681         var fnName = "extFnProxy" + (++Ext.chart.Chart.PROXY_FN_ID);\r
49682         window[fnName] = fn;\r
49683         return fnName;\r
49684     },\r
49685     \r
49686     onDestroy: function(){\r
49687         Ext.chart.Chart.superclass.onDestroy.call(this);\r
49688         delete window[this.tipFnName];\r
49689     }\r
49690 });\r
49691 Ext.reg('chart', Ext.chart.Chart);\r
49692 Ext.chart.Chart.PROXY_FN_ID = 0;\r
49693 \r
49694 /**\r
49695  * Sets the url to load the chart from. This should be set to a local resource.\r
49696  * @static\r
49697  * @type String\r
49698  */\r
49699 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.7.0/build/charts/assets/charts.swf';\r
49700 \r
49701 /**\r
49702  * @class Ext.chart.PieChart\r
49703  * @extends Ext.chart.Chart\r
49704  * @constructor\r
49705  * @xtype piechart\r
49706  */\r
49707 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {\r
49708     type: 'pie',\r
49709 \r
49710     onSwfReady : function(isReset){\r
49711         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);\r
49712 \r
49713         this.setDataField(this.dataField);\r
49714         this.setCategoryField(this.categoryField);\r
49715     },\r
49716 \r
49717     setDataField : function(field){\r
49718         this.dataField = field;\r
49719         this.swf.setDataField(field);\r
49720     },\r
49721 \r
49722     setCategoryField : function(field){\r
49723         this.categoryField = field;\r
49724         this.swf.setCategoryField(field);\r
49725     }\r
49726 });\r
49727 Ext.reg('piechart', Ext.chart.PieChart);\r
49728 \r
49729 /**\r
49730  * @class Ext.chart.CartesianChart\r
49731  * @extends Ext.chart.Chart\r
49732  * @constructor\r
49733  * @xtype cartesianchart\r
49734  */\r
49735 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {\r
49736     onSwfReady : function(isReset){\r
49737         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);\r
49738 \r
49739         if(this.xField){\r
49740             this.setXField(this.xField);\r
49741         }\r
49742         if(this.yField){\r
49743             this.setYField(this.yField);\r
49744         }\r
49745         if(this.xAxis){\r
49746             this.setXAxis(this.xAxis);\r
49747         }\r
49748         if(this.yAxis){\r
49749             this.setYAxis(this.yAxis);\r
49750         }\r
49751     },\r
49752 \r
49753     setXField : function(value){\r
49754         this.xField = value;\r
49755         this.swf.setHorizontalField(value);\r
49756     },\r
49757 \r
49758     setYField : function(value){\r
49759         this.yField = value;\r
49760         this.swf.setVerticalField(value);\r
49761     },\r
49762 \r
49763     setXAxis : function(value){\r
49764         this.xAxis = this.createAxis('xAxis', value);\r
49765         this.swf.setHorizontalAxis(this.xAxis);\r
49766     },\r
49767 \r
49768     setYAxis : function(value){\r
49769         this.yAxis = this.createAxis('yAxis', value);\r
49770         this.swf.setVerticalAxis(this.yAxis);\r
49771     },\r
49772 \r
49773     createAxis : function(axis, value){\r
49774         var o = Ext.apply({}, value), oldFn = null;\r
49775         if(this[axis]){\r
49776             oldFn = this[axis].labelFunction;\r
49777         }\r
49778         if(o.labelRenderer){\r
49779             var fn = o.labelRenderer;\r
49780             o.labelFunction = this.createFnProxy(function(v){\r
49781                 return fn(v);\r
49782             }, oldFn);\r
49783             delete o.labelRenderer;\r
49784         }\r
49785         return o;\r
49786     }\r
49787 });\r
49788 Ext.reg('cartesianchart', Ext.chart.CartesianChart);\r
49789 \r
49790 /**\r
49791  * @class Ext.chart.LineChart\r
49792  * @extends Ext.chart.CartesianChart\r
49793  * @constructor\r
49794  * @xtype linechart\r
49795  */\r
49796 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {\r
49797     type: 'line'\r
49798 });\r
49799 Ext.reg('linechart', Ext.chart.LineChart);\r
49800 \r
49801 /**\r
49802  * @class Ext.chart.ColumnChart\r
49803  * @extends Ext.chart.CartesianChart\r
49804  * @constructor\r
49805  * @xtype columnchart\r
49806  */\r
49807 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
49808     type: 'column'\r
49809 });\r
49810 Ext.reg('columnchart', Ext.chart.ColumnChart);\r
49811 \r
49812 /**\r
49813  * @class Ext.chart.StackedColumnChart\r
49814  * @extends Ext.chart.CartesianChart\r
49815  * @constructor\r
49816  * @xtype stackedcolumnchart\r
49817  */\r
49818 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
49819     type: 'stackcolumn'\r
49820 });\r
49821 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);\r
49822 \r
49823 /**\r
49824  * @class Ext.chart.BarChart\r
49825  * @extends Ext.chart.CartesianChart\r
49826  * @constructor\r
49827  * @xtype barchart\r
49828  */\r
49829 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {\r
49830     type: 'bar'\r
49831 });\r
49832 Ext.reg('barchart', Ext.chart.BarChart);\r
49833 \r
49834 /**\r
49835  * @class Ext.chart.StackedBarChart\r
49836  * @extends Ext.chart.CartesianChart\r
49837  * @constructor\r
49838  * @xtype stackedbarchart\r
49839  */\r
49840 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {\r
49841     type: 'stackbar'\r
49842 });\r
49843 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);\r
49844 \r
49845 \r
49846 \r
49847 /**\r
49848  * @class Ext.chart.Axis\r
49849  * Defines a CartesianChart's vertical or horizontal axis.\r
49850  * @constructor\r
49851  */\r
49852 Ext.chart.Axis = function(config){\r
49853     Ext.apply(this, config);\r
49854 };\r
49855 \r
49856 Ext.chart.Axis.prototype =\r
49857 {\r
49858     /**\r
49859      * The type of axis.\r
49860      *\r
49861      * @property type\r
49862      * @type String\r
49863      */\r
49864     type: null,\r
49865 \r
49866     /**\r
49867      * The direction in which the axis is drawn. May be "horizontal" or "vertical".\r
49868      *\r
49869      * @property orientation\r
49870      * @type String\r
49871      */\r
49872     orientation: "horizontal",\r
49873 \r
49874     /**\r
49875      * If true, the items on the axis will be drawn in opposite direction.\r
49876      *\r
49877      * @property reverse\r
49878      * @type Boolean\r
49879      */\r
49880     reverse: false,\r
49881 \r
49882     /**\r
49883      * A string reference to the globally-accessible function that may be called to\r
49884      * determine each of the label values for this axis.\r
49885      *\r
49886      * @property labelFunction\r
49887      * @type String\r
49888      */\r
49889     labelFunction: null,\r
49890 \r
49891     /**\r
49892      * If true, labels that overlap previously drawn labels on the axis will be hidden.\r
49893      *\r
49894      * @property hideOverlappingLabels\r
49895      * @type Boolean\r
49896      */\r
49897     hideOverlappingLabels: true\r
49898 };\r
49899 \r
49900 /**\r
49901  * @class Ext.chart.NumericAxis\r
49902  * @extends Ext.chart.Axis\r
49903  * A type of axis whose units are measured in numeric values.\r
49904  * @constructor\r
49905  */\r
49906 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {\r
49907     type: "numeric",\r
49908 \r
49909     /**\r
49910      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
49911      * will be calculated automatically.\r
49912      *\r
49913      * @property minimum\r
49914      * @type Number\r
49915      */\r
49916     minimum: NaN,\r
49917 \r
49918     /**\r
49919      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
49920      * will be calculated automatically.\r
49921      *\r
49922      * @property maximum\r
49923      * @type Number\r
49924      */\r
49925     maximum: NaN,\r
49926 \r
49927     /**\r
49928      * The spacing between major intervals on this axis.\r
49929      *\r
49930      * @property majorUnit\r
49931      * @type Number\r
49932      */\r
49933     majorUnit: NaN,\r
49934 \r
49935     /**\r
49936      * The spacing between minor intervals on this axis.\r
49937      *\r
49938      * @property minorUnit\r
49939      * @type Number\r
49940      */\r
49941     minorUnit: NaN,\r
49942 \r
49943     /**\r
49944      * If true, the labels, ticks, gridlines, and other objects will snap to\r
49945      * the nearest major or minor unit. If false, their position will be based\r
49946      * on the minimum value.\r
49947      *\r
49948      * @property snapToUnits\r
49949      * @type Boolean\r
49950      */\r
49951     snapToUnits: true,\r
49952 \r
49953     /**\r
49954      * If true, and the bounds are calculated automatically, either the minimum or\r
49955      * maximum will be set to zero.\r
49956      *\r
49957      * @property alwaysShowZero\r
49958      * @type Boolean\r
49959      */\r
49960     alwaysShowZero: true,\r
49961 \r
49962     /**\r
49963      * The scaling algorithm to use on this axis. May be "linear" or "logarithmic".\r
49964      *\r
49965      * @property scale\r
49966      * @type String\r
49967      */\r
49968     scale: "linear"\r
49969 });\r
49970 \r
49971 /**\r
49972  * @class Ext.chart.TimeAxis\r
49973  * @extends Ext.chart.Axis\r
49974  * A type of axis whose units are measured in time-based values.\r
49975  * @constructor\r
49976  */\r
49977 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {\r
49978     type: "time",\r
49979 \r
49980     /**\r
49981      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
49982      * will be calculated automatically.\r
49983      *\r
49984      * @property minimum\r
49985      * @type Date\r
49986      */\r
49987     minimum: null,\r
49988 \r
49989     /**\r
49990      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
49991      * will be calculated automatically.\r
49992      *\r
49993      * @property maximum\r
49994      * @type Number\r
49995      */\r
49996     maximum: null,\r
49997 \r
49998     /**\r
49999      * The spacing between major intervals on this axis.\r
50000      *\r
50001      * @property majorUnit\r
50002      * @type Number\r
50003      */\r
50004     majorUnit: NaN,\r
50005 \r
50006     /**\r
50007      * The time unit used by the majorUnit.\r
50008      *\r
50009      * @property majorTimeUnit\r
50010      * @type String\r
50011      */\r
50012     majorTimeUnit: null,\r
50013 \r
50014     /**\r
50015      * The spacing between minor intervals on this axis.\r
50016      *\r
50017      * @property majorUnit\r
50018      * @type Number\r
50019      */\r
50020     minorUnit: NaN,\r
50021 \r
50022     /**\r
50023      * The time unit used by the minorUnit.\r
50024      *\r
50025      * @property majorTimeUnit\r
50026      * @type String\r
50027      */\r
50028     minorTimeUnit: null,\r
50029 \r
50030     /**\r
50031      * If true, the labels, ticks, gridlines, and other objects will snap to\r
50032      * the nearest major or minor unit. If false, their position will be based\r
50033      * on the minimum value.\r
50034      *\r
50035      * @property snapToUnits\r
50036      * @type Boolean\r
50037      */\r
50038     snapToUnits: true\r
50039 });\r
50040 \r
50041 /**\r
50042  * @class Ext.chart.CategoryAxis\r
50043  * @extends Ext.chart.Axis\r
50044  * A type of axis that displays items in categories.\r
50045  * @constructor\r
50046  */\r
50047 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {\r
50048     type: "category",\r
50049 \r
50050     /**\r
50051      * A list of category names to display along this axis.\r
50052      *\r
50053      * @property categoryNames\r
50054      * @type Array\r
50055      */\r
50056     categoryNames: null\r
50057 });\r
50058 \r
50059 /**\r
50060  * @class Ext.chart.Series\r
50061  * Series class for the charts widget.\r
50062  * @constructor\r
50063  */\r
50064 Ext.chart.Series = function(config) { Ext.apply(this, config); };\r
50065 \r
50066 Ext.chart.Series.prototype =\r
50067 {\r
50068     /**\r
50069      * The type of series.\r
50070      *\r
50071      * @property type\r
50072      * @type String\r
50073      */\r
50074     type: null,\r
50075 \r
50076     /**\r
50077      * The human-readable name of the series.\r
50078      *\r
50079      * @property displayName\r
50080      * @type String\r
50081      */\r
50082     displayName: null\r
50083 };\r
50084 \r
50085 /**\r
50086  * @class Ext.chart.CartesianSeries\r
50087  * @extends Ext.chart.Series\r
50088  * CartesianSeries class for the charts widget.\r
50089  * @constructor\r
50090  */\r
50091 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {\r
50092     /**\r
50093      * The field used to access the x-axis value from the items from the data source.\r
50094      *\r
50095      * @property xField\r
50096      * @type String\r
50097      */\r
50098     xField: null,\r
50099 \r
50100     /**\r
50101      * The field used to access the y-axis value from the items from the data source.\r
50102      *\r
50103      * @property yField\r
50104      * @type String\r
50105      */\r
50106     yField: null\r
50107 });\r
50108 \r
50109 /**\r
50110  * @class Ext.chart.ColumnSeries\r
50111  * @extends Ext.chart.CartesianSeries\r
50112  * ColumnSeries class for the charts widget.\r
50113  * @constructor\r
50114  */\r
50115 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
50116     type: "column"\r
50117 });\r
50118 \r
50119 /**\r
50120  * @class Ext.chart.LineSeries\r
50121  * @extends Ext.chart.CartesianSeries\r
50122  * LineSeries class for the charts widget.\r
50123  * @constructor\r
50124  */\r
50125 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
50126     type: "line"\r
50127 });\r
50128 \r
50129 /**\r
50130  * @class Ext.chart.BarSeries\r
50131  * @extends Ext.chart.CartesianSeries\r
50132  * BarSeries class for the charts widget.\r
50133  * @constructor\r
50134  */\r
50135 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
50136     type: "bar"\r
50137 });\r
50138 \r
50139 \r
50140 /**\r
50141  * @class Ext.chart.PieSeries\r
50142  * @extends Ext.chart.Series\r
50143  * PieSeries class for the charts widget.\r
50144  * @constructor\r
50145  */\r
50146 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {\r
50147     type: "pie",\r
50148     dataField: null,\r
50149     categoryField: null\r
50150 });/**\r
50151  * @class Ext.layout.MenuLayout\r
50152  * @extends Ext.layout.ContainerLayout\r
50153  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>\r
50154  */\r
50155  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
50156     monitorResize: true,\r
50157 \r
50158     setContainer : function(ct){\r
50159         this.monitorResize = !ct.floating;\r
50160         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);\r
50161     },\r
50162 \r
50163     renderItem : function(c, position, target){\r
50164         if (!this.itemTpl) {\r
50165             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(\r
50166                 '<li id="{itemId}" class="{itemCls}">',\r
50167                     '<tpl if="needsIcon">',\r
50168                         '<img src="{icon}" class="{iconCls}"/>',\r
50169                     '</tpl>',\r
50170                 '</li>'\r
50171             );\r
50172         }\r
50173 \r
50174         if(c && !c.rendered){\r
50175             if(Ext.isNumber(position)){\r
50176                 position = target.dom.childNodes[position];\r
50177             }\r
50178             var a = this.getItemArgs(c);\r
50179 \r
50180 //          The Component's positionEl is the <li> it is rendered into\r
50181             c.render(c.positionEl = position ?\r
50182                 this.itemTpl.insertBefore(position, a, true) :\r
50183                 this.itemTpl.append(target, a, true));\r
50184 \r
50185 //          Link the containing <li> to the item.\r
50186             c.positionEl.menuItemId = c.itemId || c.id;\r
50187 \r
50188 //          If rendering a regular Component, and it needs an icon,\r
50189 //          move the Component rightwards.\r
50190             if (!a.isMenuItem && a.needsIcon) {\r
50191                 c.positionEl.addClass('x-menu-list-item-indent');\r
50192             }\r
50193         }else if(c && !this.isValidParent(c, target)){\r
50194             if(Ext.isNumber(position)){\r
50195                 position = target.dom.childNodes[position];\r
50196             }\r
50197             target.dom.insertBefore(c.getActionEl().dom, position || null);\r
50198         }\r
50199     },\r
50200 \r
50201     getItemArgs: function(c) {\r
50202         var isMenuItem = c instanceof Ext.menu.Item;\r
50203         return {\r
50204             isMenuItem: isMenuItem,\r
50205             needsIcon: !isMenuItem && (c.icon || c.iconCls),\r
50206             icon: c.icon || Ext.BLANK_IMAGE_URL,\r
50207             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),\r
50208             itemId: 'x-menu-el-' + c.id,\r
50209             itemCls: 'x-menu-list-item ' + (this.extraCls || '')\r
50210         };\r
50211     },\r
50212 \r
50213 //  Valid if the Component is in a <li> which is part of our target <ul>\r
50214     isValidParent: function(c, target) {\r
50215         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);\r
50216     },\r
50217 \r
50218     onLayout : function(ct, target){\r
50219         this.renderAll(ct, target);\r
50220         this.doAutoSize();\r
50221     },\r
50222 \r
50223     doAutoSize : function(){\r
50224         var ct = this.container, w = ct.width;\r
50225         if(ct.floating){\r
50226             if(w){\r
50227                 ct.setWidth(w);\r
50228             }else if(Ext.isIE){\r
50229                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);\r
50230                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc\r
50231                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));\r
50232             }\r
50233         }\r
50234     }\r
50235 });\r
50236 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;\r
50237 \r
50238 /**\r
50239  * @class Ext.menu.Menu\r
50240  * @extends Ext.Container\r
50241  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class\r
50242  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>\r
50243  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>\r
50244  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}\r
50245  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line\r
50246  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}\r
50247  * for an example.</p>\r
50248  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with\r
50249  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>\r
50250  *\r
50251  * @xtype menu\r
50252  */\r
50253 Ext.menu.Menu = Ext.extend(Ext.Container, {\r
50254     /**\r
50255      * @cfg {Object} defaults\r
50256      * A config object that will be applied to all items added to this container either via the {@link #items}\r
50257      * config or via the {@link #add} method.  The defaults config can contain any number of\r
50258      * name/value property pairs to be added to each item, and should be valid for the types of items\r
50259      * being added to the menu.\r
50260      */\r
50261     /**\r
50262      * @cfg {Mixed} items\r
50263      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},\r
50264      * or general {@link Ext.Component Component}s.\r
50265      */\r
50266     /**\r
50267      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)\r
50268      */\r
50269     minWidth : 120,\r
50270     /**\r
50271      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"\r
50272      * for bottom-right shadow (defaults to "sides")\r
50273      */\r
50274     shadow : "sides",\r
50275     /**\r
50276      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of\r
50277      * this menu (defaults to "tl-tr?")\r
50278      */\r
50279     subMenuAlign : "tl-tr?",\r
50280     /**\r
50281      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu\r
50282      * relative to its element of origin (defaults to "tl-bl?")\r
50283      */\r
50284     defaultAlign : "tl-bl?",\r
50285     /**\r
50286      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)\r
50287      */\r
50288     allowOtherMenus : false,\r
50289     /**\r
50290      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays\r
50291      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).\r
50292      */\r
50293     ignoreParentClicks : false,\r
50294     /**\r
50295      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).\r
50296      */\r
50297     enableScrolling: true,\r
50298     /**\r
50299      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).\r
50300      */\r
50301     maxHeight: null,\r
50302     /**\r
50303      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).\r
50304      */\r
50305     scrollIncrement: 24,\r
50306     /**\r
50307      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).\r
50308      */\r
50309     showSeparator: true,\r
50310     /**\r
50311      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to\r
50312      * change the default Menu popup position after aligning according to the {@link #defaultAlign}\r
50313      * configuration. Defaults to <tt>[0, 0]</tt>.\r
50314      */\r
50315     defaultOffsets : [0, 0],\r
50316 \r
50317 \r
50318     /**\r
50319      * @cfg {Boolean} floating\r
50320      * May be specified as false to create a Menu which may be used as a child item of another Container\r
50321      * instead of a free-floating {@link Ext.Layer Layer}. (defaults to true).\r
50322      */\r
50323     floating: true,         // Render as a Layer by default\r
50324 \r
50325     // private\r
50326     hidden: true,\r
50327     layout: 'menu',\r
50328     hideMode: 'offsets',    // Important for laying out Components\r
50329     scrollerHeight: 8,\r
50330     autoLayout: true,       // Provided for backwards compat\r
50331     defaultType: 'menuitem',\r
50332 \r
50333     initComponent: function(){\r
50334         if(Ext.isArray(this.initialConfig)){\r
50335             Ext.apply(this, {items:this.initialConfig});\r
50336         }\r
50337         this.addEvents(\r
50338             /**\r
50339              * @event click\r
50340              * Fires when this menu is clicked (or when the enter key is pressed while it is active)\r
50341              * @param {Ext.menu.Menu} this\r
50342             * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
50343              * @param {Ext.EventObject} e\r
50344              */\r
50345             'click',\r
50346             /**\r
50347              * @event mouseover\r
50348              * Fires when the mouse is hovering over this menu\r
50349              * @param {Ext.menu.Menu} this\r
50350              * @param {Ext.EventObject} e\r
50351              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
50352              */\r
50353             'mouseover',\r
50354             /**\r
50355              * @event mouseout\r
50356              * Fires when the mouse exits this menu\r
50357              * @param {Ext.menu.Menu} this\r
50358              * @param {Ext.EventObject} e\r
50359              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
50360              */\r
50361             'mouseout',\r
50362             /**\r
50363              * @event itemclick\r
50364              * Fires when a menu item contained in this menu is clicked\r
50365              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked\r
50366              * @param {Ext.EventObject} e\r
50367              */\r
50368             'itemclick'\r
50369         );\r
50370         Ext.menu.MenuMgr.register(this);\r
50371         if(this.floating){\r
50372             Ext.EventManager.onWindowResize(this.hide, this);\r
50373         }else{\r
50374             if(this.initialConfig.hidden !== false){\r
50375                 this.hidden = false;\r
50376             }\r
50377             this.internalDefaults = {hideOnClick: false};\r
50378         }\r
50379         Ext.menu.Menu.superclass.initComponent.call(this);\r
50380         if(this.autoLayout){\r
50381             this.on({\r
50382                 add: this.doLayout,\r
50383                 remove: this.doLayout,\r
50384                 scope: this\r
50385             });\r
50386         }\r
50387     },\r
50388 \r
50389     //private\r
50390     getLayoutTarget : function() {\r
50391         return this.ul;\r
50392     },\r
50393 \r
50394     // private\r
50395     onRender : function(ct, position){\r
50396         if(!ct){\r
50397             ct = Ext.getBody();\r
50398         }\r
50399 \r
50400         var dh = {\r
50401             id: this.getId(),\r
50402             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),\r
50403             style: this.style,\r
50404             cn: [\r
50405                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},\r
50406                 {tag: 'ul', cls: 'x-menu-list'}\r
50407             ]\r
50408         };\r
50409         if(this.floating){\r
50410             this.el = new Ext.Layer({\r
50411                 shadow: this.shadow,\r
50412                 dh: dh,\r
50413                 constrain: false,\r
50414                 parentEl: ct,\r
50415                 zindex:15000\r
50416             });\r
50417         }else{\r
50418             this.el = ct.createChild(dh);\r
50419         }\r
50420         Ext.menu.Menu.superclass.onRender.call(this, ct, position);\r
50421 \r
50422         if(!this.keyNav){\r
50423             this.keyNav = new Ext.menu.MenuNav(this);\r
50424         }\r
50425         // generic focus element\r
50426         this.focusEl = this.el.child('a.x-menu-focus');\r
50427         this.ul = this.el.child('ul.x-menu-list');\r
50428         this.mon(this.ul, {\r
50429             scope: this,\r
50430             click: this.onClick,\r
50431             mouseover: this.onMouseOver,\r
50432             mouseout: this.onMouseOut\r
50433         });\r
50434         if(this.enableScrolling){\r
50435             this.mon(this.el, {\r
50436                 scope: this,\r
50437                 delegate: '.x-menu-scroller',\r
50438                 click: this.onScroll,\r
50439                 mouseover: this.deactivateActive\r
50440             });\r
50441         }\r
50442     },\r
50443 \r
50444     // private\r
50445     findTargetItem : function(e){\r
50446         var t = e.getTarget(".x-menu-list-item", this.ul, true);\r
50447         if(t && t.menuItemId){\r
50448             return this.items.get(t.menuItemId);\r
50449         }\r
50450     },\r
50451 \r
50452     // private\r
50453     onClick : function(e){\r
50454         var t = this.findTargetItem(e);\r
50455         if(t){\r
50456             if(t.isFormField){\r
50457                 this.setActiveItem(t);\r
50458             }else{\r
50459                 if(t.menu && this.ignoreParentClicks){\r
50460                     t.expandMenu();\r
50461                     e.preventDefault();\r
50462                 }else if(t.onClick){\r
50463                     t.onClick(e);\r
50464                     this.fireEvent("click", this, t, e);\r
50465                 }\r
50466             }\r
50467         }\r
50468     },\r
50469 \r
50470     // private\r
50471     setActiveItem : function(item, autoExpand){\r
50472         if(item != this.activeItem){\r
50473             this.deactivateActive();\r
50474             if((this.activeItem = item).isFormField){\r
50475                 item.focus();\r
50476             }else{\r
50477                 item.activate(autoExpand);\r
50478             }\r
50479         }else if(autoExpand){\r
50480             item.expandMenu();\r
50481         }\r
50482     },\r
50483 \r
50484     deactivateActive: function(){\r
50485         var a = this.activeItem;\r
50486         if(a){\r
50487             if(a.isFormField){\r
50488                 //Fields cannot deactivate, but Combos must collapse\r
50489                 if(a.collapse){\r
50490                     a.collapse();\r
50491                 }\r
50492             }else{\r
50493                 a.deactivate();\r
50494             }\r
50495             delete this.activeItem;\r
50496         }\r
50497     },\r
50498 \r
50499     // private\r
50500     tryActivate : function(start, step){\r
50501         var items = this.items;\r
50502         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){\r
50503             var item = items.get(i);\r
50504             if(!item.disabled && (item.canActivate || item.isFormField)){\r
50505                 this.setActiveItem(item, false);\r
50506                 return item;\r
50507             }\r
50508         }\r
50509         return false;\r
50510     },\r
50511 \r
50512     // private\r
50513     onMouseOver : function(e){\r
50514         var t = this.findTargetItem(e);\r
50515         if(t){\r
50516             if(t.canActivate && !t.disabled){\r
50517                 this.setActiveItem(t, true);\r
50518             }\r
50519         }\r
50520         this.over = true;\r
50521         this.fireEvent("mouseover", this, e, t);\r
50522     },\r
50523 \r
50524     // private\r
50525     onMouseOut : function(e){\r
50526         var t = this.findTargetItem(e);\r
50527         if(t){\r
50528             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){\r
50529                 this.activeItem.deactivate();\r
50530                 delete this.activeItem;\r
50531             }\r
50532         }\r
50533         this.over = false;\r
50534         this.fireEvent("mouseout", this, e, t);\r
50535     },\r
50536 \r
50537     // private\r
50538     onScroll: function(e, t){\r
50539         if(e){\r
50540             e.stopEvent();\r
50541         }\r
50542         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
50543         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);\r
50544         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){\r
50545            this.onScrollerOut(null, t);\r
50546         }\r
50547     },\r
50548 \r
50549     // private\r
50550     onScrollerIn: function(e, t){\r
50551         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
50552         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){\r
50553             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
50554         }\r
50555     },\r
50556 \r
50557     // private\r
50558     onScrollerOut: function(e, t){\r
50559         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
50560     },\r
50561 \r
50562     /**\r
50563      * Displays this menu relative to another element\r
50564      * @param {Mixed} element The element to align to\r
50565      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to\r
50566      * the element (defaults to this.defaultAlign)\r
50567      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
50568      */\r
50569     show : function(el, pos, parentMenu){\r
50570         if(this.floating){\r
50571             this.parentMenu = parentMenu;\r
50572             if(!this.el){\r
50573                 this.render();\r
50574                 this.doLayout(false, true);\r
50575             }\r
50576             if(this.fireEvent('beforeshow', this) !== false){\r
50577                 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu, false);\r
50578             }\r
50579         }else{\r
50580             Ext.menu.Menu.superclass.show.call(this);\r
50581         }\r
50582     },\r
50583 \r
50584     /**\r
50585      * Displays this menu at a specific xy position\r
50586      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)\r
50587      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
50588      */\r
50589     showAt : function(xy, parentMenu, /* private: */_e){\r
50590         this.parentMenu = parentMenu;\r
50591         if(!this.el){\r
50592             this.render();\r
50593         }\r
50594         this.el.setXY(xy);\r
50595         if(this.enableScrolling){\r
50596             this.constrainScroll(xy[1]);\r
50597         }\r
50598         this.el.show();\r
50599         Ext.menu.Menu.superclass.onShow.call(this);\r
50600         if(Ext.isIE){\r
50601             this.layout.doAutoSize();\r
50602             if(!Ext.isIE8){\r
50603                 this.el.repaint();\r
50604             }\r
50605         }\r
50606         this.hidden = false;\r
50607         this.focus();\r
50608         this.fireEvent("show", this);\r
50609     },\r
50610 \r
50611     constrainScroll: function(y){\r
50612         var max, full = this.ul.setHeight('auto').getHeight();\r
50613         if(this.floating){\r
50614             max = this.maxHeight ? this.maxHeight : Ext.fly(this.el.dom.parentNode).getViewSize().height - y;\r
50615         }else{\r
50616             max = this.getHeight();\r
50617         }\r
50618         if(full > max && max > 0){\r
50619             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);\r
50620             this.ul.setHeight(this.activeMax);\r
50621             this.createScrollers();\r
50622             this.el.select('.x-menu-scroller').setDisplayed('');\r
50623         }else{\r
50624             this.ul.setHeight(full);\r
50625             this.el.select('.x-menu-scroller').setDisplayed('none');\r
50626         }\r
50627         this.ul.dom.scrollTop = 0;\r
50628     },\r
50629 \r
50630     createScrollers: function(){\r
50631         if(!this.scroller){\r
50632             this.scroller = {\r
50633                 pos: 0,\r
50634                 top: this.el.insertFirst({\r
50635                     tag: 'div',\r
50636                     cls: 'x-menu-scroller x-menu-scroller-top',\r
50637                     html: '&#160;'\r
50638                 }),\r
50639                 bottom: this.el.createChild({\r
50640                     tag: 'div',\r
50641                     cls: 'x-menu-scroller x-menu-scroller-bottom',\r
50642                     html: '&#160;'\r
50643                 })\r
50644             };\r
50645             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);\r
50646             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {\r
50647                 listeners: {\r
50648                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)\r
50649                 }\r
50650             });\r
50651             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);\r
50652             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {\r
50653                 listeners: {\r
50654                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)\r
50655                 }\r
50656             });\r
50657         }\r
50658     },\r
50659 \r
50660     onLayout: function(){\r
50661         if(this.isVisible()){\r
50662             if(this.enableScrolling){\r
50663                 this.constrainScroll(this.el.getTop());\r
50664             }\r
50665             if(this.floating){\r
50666                 this.el.sync();\r
50667             }\r
50668         }\r
50669     },\r
50670 \r
50671     focus : function(){\r
50672         if(!this.hidden){\r
50673             this.doFocus.defer(50, this);\r
50674         }\r
50675     },\r
50676 \r
50677     doFocus : function(){\r
50678         if(!this.hidden){\r
50679             this.focusEl.focus();\r
50680         }\r
50681     },\r
50682 \r
50683     /**\r
50684      * Hides this menu and optionally all parent menus\r
50685      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)\r
50686      */\r
50687     hide : function(deep){\r
50688         this.deepHide = deep;\r
50689         Ext.menu.Menu.superclass.hide.call(this);\r
50690         delete this.deepHide;\r
50691     },\r
50692 \r
50693     // private\r
50694     onHide: function(){\r
50695         Ext.menu.Menu.superclass.onHide.call(this);\r
50696         this.deactivateActive();\r
50697         if(this.el && this.floating){\r
50698             this.el.hide();\r
50699         }\r
50700         if(this.deepHide === true && this.parentMenu){\r
50701             this.parentMenu.hide(true);\r
50702         }\r
50703     },\r
50704 \r
50705     // private\r
50706     lookupComponent: function(c){\r
50707          if(Ext.isString(c)){\r
50708             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);\r
50709              this.applyDefaults(c);\r
50710          }else{\r
50711             if(Ext.isObject(c)){\r
50712                 c = this.getMenuItem(c);\r
50713             }else if(c.tagName || c.el){ // element. Wrap it.\r
50714                 c = new Ext.BoxComponent({\r
50715                     el: c\r
50716                 });\r
50717             }\r
50718          }\r
50719          return c;\r
50720     },\r
50721 \r
50722     applyDefaults : function(c){\r
50723         if(!Ext.isString(c)){\r
50724             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);\r
50725             var d = this.internalDefaults;\r
50726             if(d){\r
50727                 if(c.events){\r
50728                     Ext.applyIf(c.initialConfig, d);\r
50729                     Ext.apply(c, d);\r
50730                 }else{\r
50731                     Ext.applyIf(c, d);\r
50732                 }\r
50733             }\r
50734         }\r
50735         return c;\r
50736     },\r
50737 \r
50738     // private\r
50739     getMenuItem: function(config){\r
50740        if(!config.isXType){\r
50741             if(!config.xtype && Ext.isBoolean(config.checked)){\r
50742                 return new Ext.menu.CheckItem(config)\r
50743             }\r
50744             return Ext.create(config, this.defaultType);\r
50745         }\r
50746         return config;\r
50747     },\r
50748 \r
50749     /**\r
50750      * Adds a separator bar to the menu\r
50751      * @return {Ext.menu.Item} The menu item that was added\r
50752      */\r
50753     addSeparator : function(){\r
50754         return this.add(new Ext.menu.Separator());\r
50755     },\r
50756 \r
50757     /**\r
50758      * Adds an {@link Ext.Element} object to the menu\r
50759      * @param {Mixed} el The element or DOM node to add, or its id\r
50760      * @return {Ext.menu.Item} The menu item that was added\r
50761      */\r
50762     addElement : function(el){\r
50763         return this.add(new Ext.menu.BaseItem(el));\r
50764     },\r
50765 \r
50766     /**\r
50767      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu\r
50768      * @param {Ext.menu.Item} item The menu item to add\r
50769      * @return {Ext.menu.Item} The menu item that was added\r
50770      */\r
50771     addItem : function(item){\r
50772         return this.add(item);\r
50773     },\r
50774 \r
50775     /**\r
50776      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu\r
50777      * @param {Object} config A MenuItem config object\r
50778      * @return {Ext.menu.Item} The menu item that was added\r
50779      */\r
50780     addMenuItem : function(config){\r
50781         return this.add(this.getMenuItem(config));\r
50782     },\r
50783 \r
50784     /**\r
50785      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu\r
50786      * @param {String} text The text to display in the menu item\r
50787      * @return {Ext.menu.Item} The menu item that was added\r
50788      */\r
50789     addText : function(text){\r
50790         return this.add(new Ext.menu.TextItem(text));\r
50791     },\r
50792 \r
50793     //private\r
50794     onDestroy : function(){\r
50795         Ext.menu.Menu.superclass.onDestroy.call(this);\r
50796         Ext.menu.MenuMgr.unregister(this);\r
50797         Ext.EventManager.removeResizeListener(this.hide, this);\r
50798         if(this.keyNav) {\r
50799             this.keyNav.disable();\r
50800         }\r
50801         var s = this.scroller;\r
50802         if(s){\r
50803             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);\r
50804         }\r
50805     }\r
50806 });\r
50807 \r
50808 Ext.reg('menu', Ext.menu.Menu);\r
50809 \r
50810 // MenuNav is a private utility class used internally by the Menu\r
50811 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){\r
50812     function up(e, m){\r
50813         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){\r
50814             m.tryActivate(m.items.length-1, -1);\r
50815         }\r
50816     }\r
50817     function down(e, m){\r
50818         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){\r
50819             m.tryActivate(0, 1);\r
50820         }\r
50821     }\r
50822     return {\r
50823         constructor: function(menu){\r
50824             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);\r
50825             this.scope = this.menu = menu;\r
50826         },\r
50827 \r
50828         doRelay : function(e, h){\r
50829             var k = e.getKey();\r
50830 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB\r
50831             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {\r
50832                 return false;\r
50833             }\r
50834             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){\r
50835                 this.menu.tryActivate(0, 1);\r
50836                 return false;\r
50837             }\r
50838             return h.call(this.scope || this, e, this.menu);\r
50839         },\r
50840 \r
50841         tab: function(e, m) {\r
50842             e.stopEvent();\r
50843             if (e.shiftKey) {\r
50844                 up(e, m);\r
50845             } else {\r
50846                 down(e, m);\r
50847             }\r
50848         },\r
50849 \r
50850         up : up,\r
50851 \r
50852         down : down,\r
50853 \r
50854         right : function(e, m){\r
50855             if(m.activeItem){\r
50856                 m.activeItem.expandMenu(true);\r
50857             }\r
50858         },\r
50859 \r
50860         left : function(e, m){\r
50861             m.hide();\r
50862             if(m.parentMenu && m.parentMenu.activeItem){\r
50863                 m.parentMenu.activeItem.activate();\r
50864             }\r
50865         },\r
50866 \r
50867         enter : function(e, m){\r
50868             if(m.activeItem){\r
50869                 e.stopPropagation();\r
50870                 m.activeItem.onClick(e);\r
50871                 m.fireEvent("click", this, m.activeItem);\r
50872                 return true;\r
50873             }\r
50874         }\r
50875     };\r
50876 }());/**
50877  * @class Ext.menu.MenuMgr
50878  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
50879  * @singleton
50880  */
50881 Ext.menu.MenuMgr = function(){
50882    var menus, active, groups = {}, attached = false, lastShow = new Date();
50883
50884    // private - called when first menu is created
50885    function init(){
50886        menus = {};
50887        active = new Ext.util.MixedCollection();
50888        Ext.getDoc().addKeyListener(27, function(){
50889            if(active.length > 0){
50890                hideAll();
50891            }
50892        });
50893    }
50894
50895    // private
50896    function hideAll(){
50897        if(active && active.length > 0){
50898            var c = active.clone();
50899            c.each(function(m){
50900                m.hide();
50901            });
50902        }
50903    }
50904
50905    // private
50906    function onHide(m){
50907        active.remove(m);
50908        if(active.length < 1){
50909            Ext.getDoc().un("mousedown", onMouseDown);
50910            attached = false;
50911        }
50912    }
50913
50914    // private
50915    function onShow(m){
50916        var last = active.last();
50917        lastShow = new Date();
50918        active.add(m);
50919        if(!attached){
50920            Ext.getDoc().on("mousedown", onMouseDown);
50921            attached = true;
50922        }
50923        if(m.parentMenu){
50924           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
50925           m.parentMenu.activeChild = m;
50926        }else if(last && last.isVisible()){
50927           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
50928        }
50929    }
50930
50931    // private
50932    function onBeforeHide(m){
50933        if(m.activeChild){
50934            m.activeChild.hide();
50935        }
50936        if(m.autoHideTimer){
50937            clearTimeout(m.autoHideTimer);
50938            delete m.autoHideTimer;
50939        }
50940    }
50941
50942    // private
50943    function onBeforeShow(m){
50944        var pm = m.parentMenu;
50945        if(!pm && !m.allowOtherMenus){
50946            hideAll();
50947        }else if(pm && pm.activeChild){
50948            pm.activeChild.hide();
50949        }
50950    }
50951
50952    // private
50953    function onMouseDown(e){
50954        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
50955            hideAll();
50956        }
50957    }
50958
50959    // private
50960    function onBeforeCheck(mi, state){
50961        if(state){
50962            var g = groups[mi.group];
50963            for(var i = 0, l = g.length; i < l; i++){
50964                if(g[i] != mi){
50965                    g[i].setChecked(false);
50966                }
50967            }
50968        }
50969    }
50970
50971    return {
50972
50973        /**
50974         * Hides all menus that are currently visible
50975         */
50976        hideAll : function(){
50977             hideAll();  
50978        },
50979
50980        // private
50981        register : function(menu){
50982            if(!menus){
50983                init();
50984            }
50985            menus[menu.id] = menu;
50986            menu.on("beforehide", onBeforeHide);
50987            menu.on("hide", onHide);
50988            menu.on("beforeshow", onBeforeShow);
50989            menu.on("show", onShow);
50990            var g = menu.group;
50991            if(g && menu.events["checkchange"]){
50992                if(!groups[g]){
50993                    groups[g] = [];
50994                }
50995                groups[g].push(menu);
50996                menu.on("checkchange", onCheck);
50997            }
50998        },
50999
51000         /**
51001          * Returns a {@link Ext.menu.Menu} object
51002          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
51003          * be used to generate and return a new Menu instance.
51004          * @return {Ext.menu.Menu} The specified menu, or null if none are found
51005          */
51006        get : function(menu){
51007            if(typeof menu == "string"){ // menu id
51008                if(!menus){  // not initialized, no menus to return
51009                    return null;
51010                }
51011                return menus[menu];
51012            }else if(menu.events){  // menu instance
51013                return menu;
51014            }else if(typeof menu.length == 'number'){ // array of menu items?
51015                return new Ext.menu.Menu({items:menu});
51016            }else{ // otherwise, must be a config
51017                return Ext.create(menu, 'menu');
51018            }
51019        },
51020
51021        // private
51022        unregister : function(menu){
51023            delete menus[menu.id];
51024            menu.un("beforehide", onBeforeHide);
51025            menu.un("hide", onHide);
51026            menu.un("beforeshow", onBeforeShow);
51027            menu.un("show", onShow);
51028            var g = menu.group;
51029            if(g && menu.events["checkchange"]){
51030                groups[g].remove(menu);
51031                menu.un("checkchange", onCheck);
51032            }
51033        },
51034
51035        // private
51036        registerCheckable : function(menuItem){
51037            var g = menuItem.group;
51038            if(g){
51039                if(!groups[g]){
51040                    groups[g] = [];
51041                }
51042                groups[g].push(menuItem);
51043                menuItem.on("beforecheckchange", onBeforeCheck);
51044            }
51045        },
51046
51047        // private
51048        unregisterCheckable : function(menuItem){
51049            var g = menuItem.group;
51050            if(g){
51051                groups[g].remove(menuItem);
51052                menuItem.un("beforecheckchange", onBeforeCheck);
51053            }
51054        },
51055
51056        getCheckedItem : function(groupId){
51057            var g = groups[groupId];
51058            if(g){
51059                for(var i = 0, l = g.length; i < l; i++){
51060                    if(g[i].checked){
51061                        return g[i];
51062                    }
51063                }
51064            }
51065            return null;
51066        },
51067
51068        setCheckedItem : function(groupId, itemId){
51069            var g = groups[groupId];
51070            if(g){
51071                for(var i = 0, l = g.length; i < l; i++){
51072                    if(g[i].id == itemId){
51073                        g[i].setChecked(true);
51074                    }
51075                }
51076            }
51077            return null;
51078        }
51079    };
51080 }();
51081 /**
51082  * @class Ext.menu.BaseItem
51083  * @extends Ext.Component
51084  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
51085  * management and base configuration options shared by all menu components.
51086  * @constructor
51087  * Creates a new BaseItem
51088  * @param {Object} config Configuration options
51089  * @xtype menubaseitem
51090  */
51091 Ext.menu.BaseItem = function(config){
51092     Ext.menu.BaseItem.superclass.constructor.call(this, config);
51093
51094     this.addEvents(
51095         /**
51096          * @event click
51097          * Fires when this item is clicked
51098          * @param {Ext.menu.BaseItem} this
51099          * @param {Ext.EventObject} e
51100          */
51101         'click',
51102         /**
51103          * @event activate
51104          * Fires when this item is activated
51105          * @param {Ext.menu.BaseItem} this
51106          */
51107         'activate',
51108         /**
51109          * @event deactivate
51110          * Fires when this item is deactivated
51111          * @param {Ext.menu.BaseItem} this
51112          */
51113         'deactivate'
51114     );
51115
51116     if(this.handler){
51117         this.on("click", this.handler, this.scope);
51118     }
51119 };
51120
51121 Ext.extend(Ext.menu.BaseItem, Ext.Component, {
51122     /**
51123      * @property parentMenu
51124      * @type Ext.menu.Menu
51125      * The parent Menu of this Item.
51126      */
51127     /**
51128      * @cfg {Function} handler
51129      * A function that will handle the click event of this menu item (optional).
51130      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
51131      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
51132      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
51133      * </ul></div>
51134      */
51135     /**
51136      * @cfg {Object} scope
51137      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
51138      */
51139     /**
51140      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
51141      */
51142     canActivate : false,
51143     /**
51144      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
51145      */
51146     activeClass : "x-menu-item-active",
51147     /**
51148      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
51149      */
51150     hideOnClick : true,
51151     /**
51152      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
51153      */
51154     clickHideDelay : 1,
51155
51156     // private
51157     ctype : "Ext.menu.BaseItem",
51158
51159     // private
51160     actionMode : "container",
51161
51162     // private
51163     onRender : function(container, position){
51164         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
51165         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
51166             this.parentMenu = this.ownerCt;
51167         }else{
51168             this.container.addClass('x-menu-list-item');
51169             this.mon(this.el, 'click', this.onClick, this);
51170             this.mon(this.el, 'mouseenter', this.activate, this);
51171             this.mon(this.el, 'mouseleave', this.deactivate, this);
51172         }
51173     },
51174
51175     /**
51176      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
51177      * config property).  If an existing handler is already registered, it will be unregistered for you.
51178      * @param {Function} handler The function that should be called on click
51179      * @param {Object} scope The scope that should be passed to the handler
51180      */
51181     setHandler : function(handler, scope){
51182         if(this.handler){
51183             this.un("click", this.handler, this.scope);
51184         }
51185         this.on("click", this.handler = handler, this.scope = scope);
51186     },
51187
51188     // private
51189     onClick : function(e){
51190         if(!this.disabled && this.fireEvent("click", this, e) !== false
51191                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
51192             this.handleClick(e);
51193         }else{
51194             e.stopEvent();
51195         }
51196     },
51197
51198     // private
51199     activate : function(){
51200         if(this.disabled){
51201             return false;
51202         }
51203         var li = this.container;
51204         li.addClass(this.activeClass);
51205         this.region = li.getRegion().adjust(2, 2, -2, -2);
51206         this.fireEvent("activate", this);
51207         return true;
51208     },
51209
51210     // private
51211     deactivate : function(){
51212         this.container.removeClass(this.activeClass);
51213         this.fireEvent("deactivate", this);
51214     },
51215
51216     // private
51217     shouldDeactivate : function(e){
51218         return !this.region || !this.region.contains(e.getPoint());
51219     },
51220
51221     // private
51222     handleClick : function(e){
51223         if(this.hideOnClick){
51224             this.parentMenu.hide.defer(this.clickHideDelay, this.parentMenu, [true]);
51225         }
51226     },
51227
51228     // private. Do nothing
51229     expandMenu : Ext.emptyFn,
51230
51231     // private. Do nothing
51232     hideMenu : Ext.emptyFn
51233 });
51234 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
51235  * @class Ext.menu.TextItem
51236  * @extends Ext.menu.BaseItem
51237  * Adds a static text string to a menu, usually used as either a heading or group separator.
51238  * @constructor
51239  * Creates a new TextItem
51240  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
51241  * is applied as a config object (and should contain a <tt>text</tt> property).
51242  * @xtype menutextitem
51243  */
51244 Ext.menu.TextItem = function(cfg){
51245     if(typeof cfg == 'string'){
51246         cfg = {text: cfg}
51247     }
51248     Ext.menu.TextItem.superclass.constructor.call(this, cfg);
51249 };
51250
51251 Ext.extend(Ext.menu.TextItem, Ext.menu.BaseItem, {
51252     /**
51253      * @cfg {String} text The text to display for this item (defaults to '')
51254      */
51255     /**
51256      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
51257      */
51258     hideOnClick : false,
51259     /**
51260      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
51261      */
51262     itemCls : "x-menu-text",
51263
51264     // private
51265     onRender : function(){
51266         var s = document.createElement("span");
51267         s.className = this.itemCls;
51268         s.innerHTML = this.text;
51269         this.el = s;
51270         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
51271     }
51272 });
51273 Ext.reg('menutextitem', Ext.menu.TextItem);/**
51274  * @class Ext.menu.Separator
51275  * @extends Ext.menu.BaseItem
51276  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
51277  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
51278  * @constructor
51279  * @param {Object} config Configuration options
51280  * @xtype menuseparator
51281  */
51282 Ext.menu.Separator = function(config){
51283     Ext.menu.Separator.superclass.constructor.call(this, config);
51284 };
51285
51286 Ext.extend(Ext.menu.Separator, Ext.menu.BaseItem, {
51287     /**
51288      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
51289      */
51290     itemCls : "x-menu-sep",
51291     /**
51292      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
51293      */
51294     hideOnClick : false,
51295     
51296     /** 
51297      * @cfg {String} activeClass
51298      * @hide 
51299      */
51300     activeClass: '',
51301
51302     // private
51303     onRender : function(li){
51304         var s = document.createElement("span");
51305         s.className = this.itemCls;
51306         s.innerHTML = "&#160;";
51307         this.el = s;
51308         li.addClass("x-menu-sep-li");
51309         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
51310     }
51311 });
51312 Ext.reg('menuseparator', Ext.menu.Separator);/**\r
51313  * @class Ext.menu.Item\r
51314  * @extends Ext.menu.BaseItem\r
51315  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static\r
51316  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific\r
51317  * activation and click handling.\r
51318  * @constructor\r
51319  * Creates a new Item\r
51320  * @param {Object} config Configuration options\r
51321  * @xtype menuitem\r
51322  */\r
51323 Ext.menu.Item = function(config){\r
51324     Ext.menu.Item.superclass.constructor.call(this, config);\r
51325     if(this.menu){\r
51326         this.menu = Ext.menu.MenuMgr.get(this.menu);\r
51327     }\r
51328 };\r
51329 Ext.extend(Ext.menu.Item, Ext.menu.BaseItem, {\r
51330     /**\r
51331      * @property menu\r
51332      * @type Ext.menu.Menu\r
51333      * The submenu associated with this Item if one was configured.\r
51334      */\r
51335     /**\r
51336      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an\r
51337      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.\r
51338      */\r
51339     /**\r
51340      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If\r
51341      * icon is specified {@link #iconCls} should not be.\r
51342      */\r
51343     /**\r
51344      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for\r
51345      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.\r
51346      */\r
51347     /**\r
51348      * @cfg {String} text The text to display in this item (defaults to '').\r
51349      */\r
51350     /**\r
51351      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').\r
51352      */\r
51353     /**\r
51354      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').\r
51355      */\r
51356     /**\r
51357      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')\r
51358      */\r
51359     itemCls : 'x-menu-item',\r
51360     /**\r
51361      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)\r
51362      */\r
51363     canActivate : true,\r
51364     /**\r
51365      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)\r
51366      */\r
51367     showDelay: 200,\r
51368     // doc'd in BaseItem\r
51369     hideDelay: 200,\r
51370 \r
51371     // private\r
51372     ctype: 'Ext.menu.Item',\r
51373 \r
51374     // private\r
51375     onRender : function(container, position){\r
51376         if (!this.itemTpl) {\r
51377             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(\r
51378                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',\r
51379                     '<tpl if="hrefTarget">',\r
51380                         ' target="{hrefTarget}"',\r
51381                     '</tpl>',\r
51382                  '>',\r
51383                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',\r
51384                      '<span class="x-menu-item-text">{text}</span>',\r
51385                  '</a>'\r
51386              );\r
51387         }\r
51388         var a = this.getTemplateArgs();\r
51389         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);\r
51390         this.iconEl = this.el.child('img.x-menu-item-icon');\r
51391         this.textEl = this.el.child('.x-menu-item-text');\r
51392         Ext.menu.Item.superclass.onRender.call(this, container, position);\r
51393     },\r
51394 \r
51395     getTemplateArgs: function() {\r
51396         return {\r
51397             id: this.id,\r
51398             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),\r
51399             href: this.href || '#',\r
51400             hrefTarget: this.hrefTarget,\r
51401             icon: this.icon || Ext.BLANK_IMAGE_URL,\r
51402             iconCls: this.iconCls || '',\r
51403             text: this.itemText||this.text||'&#160;'\r
51404         };\r
51405     },\r
51406 \r
51407     /**\r
51408      * Sets the text to display in this menu item\r
51409      * @param {String} text The text to display\r
51410      */\r
51411     setText : function(text){\r
51412         this.text = text||'&#160;';\r
51413         if(this.rendered){\r
51414             this.textEl.update(this.text);\r
51415             this.parentMenu.layout.doAutoSize();\r
51416         }\r
51417     },\r
51418 \r
51419     /**\r
51420      * Sets the CSS class to apply to the item's icon element\r
51421      * @param {String} cls The CSS class to apply\r
51422      */\r
51423     setIconClass : function(cls){\r
51424         var oldCls = this.iconCls;\r
51425         this.iconCls = cls;\r
51426         if(this.rendered){\r
51427             this.iconEl.replaceClass(oldCls, this.iconCls);\r
51428         }\r
51429     },\r
51430     \r
51431     //private\r
51432     beforeDestroy: function(){\r
51433         if (this.menu){\r
51434             this.menu.destroy();\r
51435         }\r
51436         Ext.menu.Item.superclass.beforeDestroy.call(this);\r
51437     },\r
51438 \r
51439     // private\r
51440     handleClick : function(e){\r
51441         if(!this.href){ // if no link defined, stop the event automatically\r
51442             e.stopEvent();\r
51443         }\r
51444         Ext.menu.Item.superclass.handleClick.apply(this, arguments);\r
51445     },\r
51446 \r
51447     // private\r
51448     activate : function(autoExpand){\r
51449         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){\r
51450             this.focus();\r
51451             if(autoExpand){\r
51452                 this.expandMenu();\r
51453             }\r
51454         }\r
51455         return true;\r
51456     },\r
51457 \r
51458     // private\r
51459     shouldDeactivate : function(e){\r
51460         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){\r
51461             if(this.menu && this.menu.isVisible()){\r
51462                 return !this.menu.getEl().getRegion().contains(e.getPoint());\r
51463             }\r
51464             return true;\r
51465         }\r
51466         return false;\r
51467     },\r
51468 \r
51469     // private\r
51470     deactivate : function(){\r
51471         Ext.menu.Item.superclass.deactivate.apply(this, arguments);\r
51472         this.hideMenu();\r
51473     },\r
51474 \r
51475     // private\r
51476     expandMenu : function(autoActivate){\r
51477         if(!this.disabled && this.menu){\r
51478             clearTimeout(this.hideTimer);\r
51479             delete this.hideTimer;\r
51480             if(!this.menu.isVisible() && !this.showTimer){\r
51481                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);\r
51482             }else if (this.menu.isVisible() && autoActivate){\r
51483                 this.menu.tryActivate(0, 1);\r
51484             }\r
51485         }\r
51486     },\r
51487 \r
51488     // private\r
51489     deferExpand : function(autoActivate){\r
51490         delete this.showTimer;\r
51491         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);\r
51492         if(autoActivate){\r
51493             this.menu.tryActivate(0, 1);\r
51494         }\r
51495     },\r
51496 \r
51497     // private\r
51498     hideMenu : function(){\r
51499         clearTimeout(this.showTimer);\r
51500         delete this.showTimer;\r
51501         if(!this.hideTimer && this.menu && this.menu.isVisible()){\r
51502             this.hideTimer = this.deferHide.defer(this.hideDelay, this);\r
51503         }\r
51504     },\r
51505 \r
51506     // private\r
51507     deferHide : function(){\r
51508         delete this.hideTimer;\r
51509         if(this.menu.over){\r
51510             this.parentMenu.setActiveItem(this, false);\r
51511         }else{\r
51512             this.menu.hide();\r
51513         }\r
51514     }\r
51515 });\r
51516 Ext.reg('menuitem', Ext.menu.Item);/**
51517  * @class Ext.menu.CheckItem
51518  * @extends Ext.menu.Item
51519  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
51520  * @constructor
51521  * Creates a new CheckItem
51522  * @param {Object} config Configuration options
51523  * @xtype menucheckitem
51524  */
51525 Ext.menu.CheckItem = function(config){
51526     Ext.menu.CheckItem.superclass.constructor.call(this, config);
51527     this.addEvents(
51528         /**
51529          * @event beforecheckchange
51530          * Fires before the checked value is set, providing an opportunity to cancel if needed
51531          * @param {Ext.menu.CheckItem} this
51532          * @param {Boolean} checked The new checked value that will be set
51533          */
51534         "beforecheckchange" ,
51535         /**
51536          * @event checkchange
51537          * Fires after the checked value has been set
51538          * @param {Ext.menu.CheckItem} this
51539          * @param {Boolean} checked The checked value that was set
51540          */
51541         "checkchange"
51542     );
51543     /**
51544      * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
51545      * is provided, it will be called automatically when the checkchange event fires.
51546      * @param {Ext.menu.CheckItem} this
51547      * @param {Boolean} checked The checked value that was set
51548      * @method checkHandler
51549      */
51550     if(this.checkHandler){
51551         this.on('checkchange', this.checkHandler, this.scope);
51552     }
51553     Ext.menu.MenuMgr.registerCheckable(this);
51554 };
51555 Ext.extend(Ext.menu.CheckItem, Ext.menu.Item, {
51556     /**
51557      * @cfg {String} group
51558      * All check items with the same group name will automatically be grouped into a single-select
51559      * radio button group (defaults to '')
51560      */
51561     /**
51562      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
51563      */
51564     itemCls : "x-menu-item x-menu-check-item",
51565     /**
51566      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
51567      */
51568     groupClass : "x-menu-group-item",
51569
51570     /**
51571      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
51572      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
51573      * initialized with checked = true will be rendered as checked.
51574      */
51575     checked: false,
51576
51577     // private
51578     ctype: "Ext.menu.CheckItem",
51579
51580     // private
51581     onRender : function(c){
51582         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
51583         if(this.group){
51584             this.el.addClass(this.groupClass);
51585         }
51586         if(this.checked){
51587             this.checked = false;
51588             this.setChecked(true, true);
51589         }
51590     },
51591
51592     // private
51593     destroy : function(){
51594         Ext.menu.MenuMgr.unregisterCheckable(this);
51595         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
51596     },
51597
51598     /**
51599      * Set the checked state of this item
51600      * @param {Boolean} checked The new checked value
51601      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
51602      */
51603     setChecked : function(state, suppressEvent){
51604         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
51605             if(this.container){
51606                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
51607             }
51608             this.checked = state;
51609             if(suppressEvent !== true){
51610                 this.fireEvent("checkchange", this, state);
51611             }
51612         }
51613     },
51614
51615     // private
51616     handleClick : function(e){
51617        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
51618            this.setChecked(!this.checked);
51619        }
51620        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
51621     }
51622 });
51623 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**\r
51624  * @class Ext.menu.DateMenu\r
51625  * @extends Ext.menu.Menu\r
51626  * A menu containing a {@link Ext.DatePicker} Component.\r
51627  * @xtype datemenu\r
51628  */\r
51629  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {\r
51630     /** \r
51631      * @cfg {Boolean} enableScrolling\r
51632      * @hide \r
51633      */\r
51634     enableScrolling: false,\r
51635     \r
51636     /** \r
51637      * @cfg {Boolean} hideOnClick\r
51638      * False to continue showing the menu after a date is selected, defaults to true.\r
51639      */\r
51640     hideOnClick: true,\r
51641     \r
51642     /** \r
51643      * @cfg {Number} maxHeight\r
51644      * @hide \r
51645      */\r
51646     /** \r
51647      * @cfg {Number} scrollIncrement\r
51648      * @hide \r
51649      */\r
51650     /**\r
51651      * @property picker\r
51652      * @type DatePicker\r
51653      * The {@link Ext.DatePicker} instance for this DateMenu\r
51654      */\r
51655     cls: 'x-date-menu',\r
51656     \r
51657     /**\r
51658      * @event click\r
51659      * @hide\r
51660      */\r
51661     \r
51662     /**\r
51663      * @event itemclick\r
51664      * @hide\r
51665      */\r
51666 \r
51667     initComponent: function(){\r
51668         this.on('beforeshow', this.onBeforeShow, this);\r
51669         if(this.strict = (Ext.isIE7 && Ext.isStrict)){\r
51670             this.on('show', this.onShow, this, {single: true, delay: 20});\r
51671         }\r
51672         Ext.apply(this, {\r
51673             plain: true,\r
51674             showSeparator: false,\r
51675             items: this.picker = new Ext.DatePicker(Ext.apply({\r
51676                 internalRender: this.strict || !Ext.isIE,\r
51677                 ctCls: 'x-menu-date-item'\r
51678             }, this.initialConfig))\r
51679         });\r
51680         this.picker.purgeListeners();\r
51681         Ext.menu.DateMenu.superclass.initComponent.call(this);\r
51682         this.relayEvents(this.picker, ["select"]);\r
51683         this.on('select', this.menuHide, this);\r
51684         if(this.handler){\r
51685             this.on('select', this.handler, this.scope || this);\r
51686         }\r
51687     },\r
51688 \r
51689     menuHide: function() {\r
51690         if(this.hideOnClick){\r
51691             this.hide(true);\r
51692         }\r
51693     },\r
51694 \r
51695     onBeforeShow: function(){\r
51696         if(this.picker){\r
51697             this.picker.hideMonthPicker(true);\r
51698         }\r
51699     },\r
51700 \r
51701     onShow: function(){\r
51702         var el = this.picker.getEl();\r
51703         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode\r
51704     }\r
51705  });\r
51706  Ext.reg('datemenu', Ext.menu.DateMenu);/**\r
51707  * @class Ext.menu.ColorMenu\r
51708  * @extends Ext.menu.Menu\r
51709  * A menu containing a {@link Ext.ColorPalette} Component.\r
51710  * @xtype colormenu\r
51711  */\r
51712  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {\r
51713     /** \r
51714      * @cfg {Boolean} enableScrolling\r
51715      * @hide \r
51716      */\r
51717     enableScrolling: false,\r
51718     \r
51719     /** \r
51720      * @cfg {Boolean} hideOnClick\r
51721      * False to continue showing the menu after a color is selected, defaults to true.\r
51722      */\r
51723     hideOnClick: true,\r
51724     \r
51725     /** \r
51726      * @cfg {Number} maxHeight\r
51727      * @hide \r
51728      */\r
51729     /** \r
51730      * @cfg {Number} scrollIncrement\r
51731      * @hide \r
51732      */\r
51733     /**\r
51734      * @property palette\r
51735      * @type ColorPalette\r
51736      * The {@link Ext.ColorPalette} instance for this ColorMenu\r
51737      */\r
51738     \r
51739     \r
51740     /**\r
51741      * @event click\r
51742      * @hide\r
51743      */\r
51744     \r
51745     /**\r
51746      * @event itemclick\r
51747      * @hide\r
51748      */\r
51749     \r
51750     initComponent: function(){\r
51751         Ext.apply(this, {\r
51752             plain: true,\r
51753             showSeparator: false,\r
51754             items: this.palette = new Ext.ColorPalette(this.initialConfig)\r
51755         });\r
51756         this.palette.purgeListeners();\r
51757         Ext.menu.ColorMenu.superclass.initComponent.call(this);\r
51758         this.relayEvents(this.palette, ['select']);\r
51759         this.on('select', this.menuHide, this);\r
51760         if(this.handler){\r
51761             this.on('select', this.handler, this.scope || this)\r
51762         }\r
51763     },\r
51764 \r
51765     menuHide: function(){\r
51766         if(this.hideOnClick){\r
51767             this.hide(true);\r
51768         }\r
51769     }\r
51770 });\r
51771 Ext.reg('colormenu', Ext.menu.ColorMenu);/**
51772  * @class Ext.form.Field
51773  * @extends Ext.BoxComponent
51774  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
51775  * @constructor
51776  * Creates a new Field
51777  * @param {Object} config Configuration options
51778  * @xtype field
51779  */
51780 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
51781     /**
51782      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
51783      * to "text"). The types "file" and "password" must be used to render those field types currently -- there are
51784      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
51785      * is not supported and should be avoided.
51786      */
51787     /**
51788      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
51789      * not those which are built via applyTo (defaults to undefined).
51790      */
51791     /**
51792      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
51793      */
51794     /**
51795      * @cfg {String} name The field's HTML name attribute (defaults to "").
51796      * <b>Note</b>: this property must be set if this field is to be automatically included with
51797      * {@link Ext.form.BasicForm#submit form submit()}.
51798      */
51799     /**
51800      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to "").
51801      */
51802
51803     /**
51804      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
51805      */
51806     invalidClass : "x-form-invalid",
51807     /**
51808      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
51809      * (defaults to "The value in this field is invalid")
51810      */
51811     invalidText : "The value in this field is invalid",
51812     /**
51813      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
51814      */
51815     focusClass : "x-form-focus",
51816     /**
51817      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
51818       automatic validation (defaults to "keyup").
51819      */
51820     validationEvent : "keyup",
51821     /**
51822      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
51823      */
51824     validateOnBlur : true,
51825     /**
51826      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
51827      * is initiated (defaults to 250)
51828      */
51829     validationDelay : 250,
51830     /**
51831      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
51832      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
51833      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
51834      * <pre><code>{tag: "input", type: "text", size: "20", autocomplete: "off"}</code></pre>
51835      */
51836     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
51837     /**
51838      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
51839      */
51840     fieldClass : "x-form-field",
51841     /**
51842      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values
51843      * (defaults to 'qtip'):
51844      *<pre>
51845 Value         Description
51846 -----------   ----------------------------------------------------------------------
51847 qtip          Display a quick tip when the user hovers over the field
51848 title         Display a default browser title attribute popup
51849 under         Add a block div beneath the field containing the error text
51850 side          Add an error icon to the right of the field with a popup on hover
51851 [element id]  Add the error text directly to the innerHTML of the specified element
51852 </pre>
51853      */
51854     msgTarget : 'qtip',
51855     /**
51856      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
51857      * (defaults to 'normal').
51858      */
51859     msgFx : 'normal',
51860     /**
51861      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
51862      * (defaults to <tt>false</tt>).
51863      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
51864      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
51865      * ComboBox or DateField; it gives you the option of forcing the user to choose
51866      * via the trigger without typing in the text box. To hide the trigger use
51867      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
51868      */
51869     readOnly : false,
51870     /**
51871      * @cfg {Boolean} disabled True to disable the field (defaults to false).
51872      * <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>,
51873      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
51874      */
51875     disabled : false,
51876
51877     // private
51878     isFormField : true,
51879
51880     // private
51881     hasFocus : false,
51882
51883     // private
51884     initComponent : function(){
51885         Ext.form.Field.superclass.initComponent.call(this);
51886         this.addEvents(
51887             /**
51888              * @event focus
51889              * Fires when this field receives input focus.
51890              * @param {Ext.form.Field} this
51891              */
51892             'focus',
51893             /**
51894              * @event blur
51895              * Fires when this field loses input focus.
51896              * @param {Ext.form.Field} this
51897              */
51898             'blur',
51899             /**
51900              * @event specialkey
51901              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
51902              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
51903              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
51904              * For example: <pre><code>
51905 var form = new Ext.form.FormPanel({
51906     ...
51907     items: [{
51908             fieldLabel: 'Field 1',
51909             name: 'field1',
51910             allowBlank: false
51911         },{
51912             fieldLabel: 'Field 2',
51913             name: 'field2',
51914             listeners: {
51915                 specialkey: function(field, e){
51916                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
51917                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
51918                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
51919                         var form = field.ownerCt.getForm();
51920                         form.submit();
51921                     }
51922                 }
51923             }
51924         }
51925     ],
51926     ...
51927 });
51928              * </code></pre>
51929              * @param {Ext.form.Field} this
51930              * @param {Ext.EventObject} e The event object
51931              */
51932             'specialkey',
51933             /**
51934              * @event change
51935              * Fires just before the field blurs if the field value has changed.
51936              * @param {Ext.form.Field} this
51937              * @param {Mixed} newValue The new value
51938              * @param {Mixed} oldValue The original value
51939              */
51940             'change',
51941             /**
51942              * @event invalid
51943              * Fires after the field has been marked as invalid.
51944              * @param {Ext.form.Field} this
51945              * @param {String} msg The validation message
51946              */
51947             'invalid',
51948             /**
51949              * @event valid
51950              * Fires after the field has been validated with no errors.
51951              * @param {Ext.form.Field} this
51952              */
51953             'valid'
51954         );
51955     },
51956
51957     /**
51958      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
51959      * attribute of the field if available.
51960      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}  
51961      */
51962     getName: function(){
51963         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
51964     },
51965
51966     // private
51967     onRender : function(ct, position){
51968         if(!this.el){
51969             var cfg = this.getAutoCreate();
51970
51971             if(!cfg.name){
51972                 cfg.name = this.name || this.id;
51973             }
51974             if(this.inputType){
51975                 cfg.type = this.inputType;
51976             }
51977             this.autoEl = cfg;
51978         }
51979         Ext.form.Field.superclass.onRender.call(this, ct, position);
51980         
51981         var type = this.el.dom.type;
51982         if(type){
51983             if(type == 'password'){
51984                 type = 'text';
51985             }
51986             this.el.addClass('x-form-'+type);
51987         }
51988         if(this.readOnly){
51989             this.el.dom.readOnly = true;
51990         }
51991         if(this.tabIndex !== undefined){
51992             this.el.dom.setAttribute('tabIndex', this.tabIndex);
51993         }
51994
51995         this.el.addClass([this.fieldClass, this.cls]);
51996     },
51997
51998     // private
51999     getItemCt : function(){
52000         return this.el.up('.x-form-item', 4);
52001     },
52002
52003     // private
52004     initValue : function(){
52005         if(this.value !== undefined){
52006             this.setValue(this.value);
52007         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
52008             this.setValue(this.el.dom.value);
52009         }
52010         /**
52011          * The original value of the field as configured in the {@link #value} configuration, or
52012          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
52013          * setting is <code>true</code>.
52014          * @type mixed
52015          * @property originalValue
52016          */
52017         this.originalValue = this.getValue();
52018     },
52019
52020     /**
52021      * <p>Returns true if the value of this Field has been changed from its original value.
52022      * Will return false if the field is disabled or has not been rendered yet.</p>
52023      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
52024      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
52025      * then the <i>original value</i> is updated when the values are loaded by
52026      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
52027      * @return {Boolean} True if this field has been changed from its original value (and
52028      * is not disabled), false otherwise.
52029      */
52030     isDirty : function() {
52031         if(this.disabled || !this.rendered) {
52032             return false;
52033         }
52034         return String(this.getValue()) !== String(this.originalValue);
52035     },
52036
52037     // private
52038     afterRender : function(){
52039         Ext.form.Field.superclass.afterRender.call(this);
52040         this.initEvents();
52041         this.initValue();
52042     },
52043
52044     // private
52045     fireKey : function(e){
52046         if(e.isSpecialKey()){
52047             this.fireEvent("specialkey", this, e);
52048         }
52049     },
52050
52051     /**
52052      * Resets the current field value to the originally loaded value and clears any validation messages.
52053      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
52054      */
52055     reset : function(){
52056         this.setValue(this.originalValue);
52057         this.clearInvalid();
52058     },
52059
52060     // private
52061     initEvents : function(){
52062         this.mon(this.el, Ext.EventManager.useKeydown ? "keydown" : "keypress", this.fireKey,  this);
52063         this.mon(this.el, 'focus', this.onFocus, this);
52064
52065         // fix weird FF/Win editor issue when changing OS window focus
52066         var o = this.inEditor && Ext.isWindows && Ext.isGecko ? {buffer:10} : null;
52067         this.mon(this.el, 'blur', this.onBlur, this, o);
52068     },
52069
52070     // private
52071     onFocus : function(){
52072         if(this.focusClass){
52073             this.el.addClass(this.focusClass);
52074         }
52075         if(!this.hasFocus){
52076             this.hasFocus = true;
52077             this.startValue = this.getValue();
52078             this.fireEvent("focus", this);
52079         }
52080     },
52081
52082     // private
52083     beforeBlur : Ext.emptyFn,
52084
52085     // private
52086     onBlur : function(){
52087         this.beforeBlur();
52088         if(this.focusClass){
52089             this.el.removeClass(this.focusClass);
52090         }
52091         this.hasFocus = false;
52092         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
52093             this.validate();
52094         }
52095         var v = this.getValue();
52096         if(String(v) !== String(this.startValue)){
52097             this.fireEvent('change', this, v, this.startValue);
52098         }
52099         this.fireEvent("blur", this);
52100     },
52101
52102     /**
52103      * Returns whether or not the field value is currently valid
52104      * @param {Boolean} preventMark True to disable marking the field invalid
52105      * @return {Boolean} True if the value is valid, else false
52106      */
52107     isValid : function(preventMark){
52108         if(this.disabled){
52109             return true;
52110         }
52111         var restore = this.preventMark;
52112         this.preventMark = preventMark === true;
52113         var v = this.validateValue(this.processValue(this.getRawValue()));
52114         this.preventMark = restore;
52115         return v;
52116     },
52117
52118     /**
52119      * Validates the field value
52120      * @return {Boolean} True if the value is valid, else false
52121      */
52122     validate : function(){
52123         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
52124             this.clearInvalid();
52125             return true;
52126         }
52127         return false;
52128     },
52129
52130     // protected - should be overridden by subclasses if necessary to prepare raw values for validation
52131     processValue : function(value){
52132         return value;
52133     },
52134
52135     // private
52136     // Subclasses should provide the validation implementation by overriding this
52137     validateValue : function(value){
52138         return true;
52139     },
52140
52141     /**
52142      * Mark this field as invalid, using {@link #msgTarget} to determine how to display the error and
52143      * applying {@link #invalidClass} to the field's element.
52144      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
52145      */
52146     markInvalid : function(msg){
52147         if(!this.rendered || this.preventMark){ // not rendered
52148             return;
52149         }
52150         msg = msg || this.invalidText;
52151
52152         var mt = this.getMessageHandler();
52153         if(mt){
52154             mt.mark(this, msg);
52155         }else if(this.msgTarget){
52156             this.el.addClass(this.invalidClass);
52157             var t = Ext.getDom(this.msgTarget);
52158             if(t){
52159                 t.innerHTML = msg;
52160                 t.style.display = this.msgDisplay;
52161             }
52162         }
52163         this.fireEvent('invalid', this, msg);
52164     },
52165
52166     /**
52167      * Clear any invalid styles/messages for this field
52168      */
52169     clearInvalid : function(){
52170         if(!this.rendered || this.preventMark){ // not rendered
52171             return;
52172         }
52173         this.el.removeClass(this.invalidClass);
52174         var mt = this.getMessageHandler();
52175         if(mt){
52176             mt.clear(this);
52177         }else if(this.msgTarget){
52178             this.el.removeClass(this.invalidClass);
52179             var t = Ext.getDom(this.msgTarget);
52180             if(t){
52181                 t.innerHTML = '';
52182                 t.style.display = 'none';
52183             }
52184         }
52185         this.fireEvent('valid', this);
52186     },
52187
52188     // private
52189     getMessageHandler : function(){
52190         return Ext.form.MessageTargets[this.msgTarget];
52191     },
52192
52193     // private
52194     getErrorCt : function(){
52195         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
52196             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
52197     },
52198
52199     // private
52200     alignErrorIcon : function(){
52201         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
52202     },
52203
52204     /**
52205      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52206      * @return {Mixed} value The field value
52207      */
52208     getRawValue : function(){
52209         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
52210         if(v === this.emptyText){
52211             v = '';
52212         }
52213         return v;
52214     },
52215
52216     /**
52217      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52218      * @return {Mixed} value The field value
52219      */
52220     getValue : function(){
52221         if(!this.rendered) {
52222             return this.value;
52223         }
52224         var v = this.el.getValue();
52225         if(v === this.emptyText || v === undefined){
52226             v = '';
52227         }
52228         return v;
52229     },
52230
52231     /**
52232      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
52233      * @param {Mixed} value The value to set
52234      * @return {Mixed} value The field value that is set
52235      */
52236     setRawValue : function(v){
52237         return (this.el.dom.value = (Ext.isEmpty(v) ? '' : v));
52238     },
52239
52240     /**
52241      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52242      * @param {Mixed} value The value to set
52243      * @return {Ext.form.Field} this
52244      */
52245     setValue : function(v){
52246         this.value = v;
52247         if(this.rendered){
52248             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
52249             this.validate();
52250         }
52251         return this;
52252     },
52253
52254     // private, does not work for all fields
52255     append : function(v){
52256          this.setValue([this.getValue(), v].join(''));
52257     },
52258
52259     // private
52260     adjustSize : function(w, h){
52261         var s = Ext.form.Field.superclass.adjustSize.call(this, w, h);
52262         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
52263         if(this.offsetCt){
52264             var ct = this.getItemCt();
52265             s.width -= ct.getFrameWidth('lr');
52266             s.height -= ct.getFrameWidth('tb');
52267         }
52268         return s;
52269     },
52270
52271     // private
52272     adjustWidth : function(tag, w){
52273         if(typeof w == 'number' && (Ext.isIE && (Ext.isIE6 || !Ext.isStrict)) && /input|textarea/i.test(tag) && !this.inEditor){
52274             return w - 3;
52275         }
52276         return w;
52277     }
52278
52279     /**
52280      * @cfg {Boolean} autoWidth @hide
52281      */
52282     /**
52283      * @cfg {Boolean} autoHeight @hide
52284      */
52285
52286     /**
52287      * @cfg {String} autoEl @hide
52288      */
52289 });
52290
52291
52292 Ext.form.MessageTargets = {
52293     'qtip' : {
52294         mark: function(field, msg){
52295             field.el.addClass(field.invalidClass);
52296             field.el.dom.qtip = msg;
52297             field.el.dom.qclass = 'x-form-invalid-tip';
52298             if(Ext.QuickTips){ // fix for floating editors interacting with DND
52299                 Ext.QuickTips.enable();
52300             }
52301         },
52302         clear: function(field){
52303             field.el.removeClass(field.invalidClass);
52304             field.el.dom.qtip = '';
52305         }
52306     },
52307     'title' : {
52308         mark: function(field, msg){
52309             field.el.addClass(field.invalidClass);
52310             field.el.dom.title = msg;
52311         },
52312         clear: function(field){
52313             field.el.dom.title = '';
52314         }
52315     },
52316     'under' : {
52317         mark: function(field, msg){
52318             field.el.addClass(field.invalidClass);
52319             if(!field.errorEl){
52320                 var elp = field.getErrorCt();
52321                 if(!elp){ // field has no container el
52322                     field.el.dom.title = msg;
52323                     return;
52324                 }
52325                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
52326                 field.errorEl.setWidth(elp.getWidth(true)-20);
52327             }
52328             field.errorEl.update(msg);
52329             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
52330         },
52331         clear: function(field){
52332             field.el.removeClass(field.invalidClass);
52333             if(field.errorEl){
52334                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
52335             }else{
52336                 field.el.dom.title = '';
52337             }
52338         }
52339     },
52340     'side' : {
52341         mark: function(field, msg){
52342             field.el.addClass(field.invalidClass);
52343             if(!field.errorIcon){
52344                 var elp = field.getErrorCt();
52345                 if(!elp){ // field has no container el
52346                     field.el.dom.title = msg;
52347                     return;
52348                 }
52349                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
52350             }
52351             field.alignErrorIcon();
52352             field.errorIcon.dom.qtip = msg;
52353             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
52354             field.errorIcon.show();
52355             field.on('resize', field.alignErrorIcon, field);
52356         },
52357         clear: function(field){
52358             field.el.removeClass(field.invalidClass);
52359             if(field.errorIcon){
52360                 field.errorIcon.dom.qtip = '';
52361                 field.errorIcon.hide();
52362                 field.un('resize', field.alignErrorIcon, field);
52363             }else{
52364                 field.el.dom.title = '';
52365             }
52366         }
52367     }
52368 };
52369
52370 // anything other than normal should be considered experimental
52371 Ext.form.Field.msgFx = {
52372     normal : {
52373         show: function(msgEl, f){
52374             msgEl.setDisplayed('block');
52375         },
52376
52377         hide : function(msgEl, f){
52378             msgEl.setDisplayed(false).update('');
52379         }
52380     },
52381
52382     slide : {
52383         show: function(msgEl, f){
52384             msgEl.slideIn('t', {stopFx:true});
52385         },
52386
52387         hide : function(msgEl, f){
52388             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
52389         }
52390     },
52391
52392     slideRight : {
52393         show: function(msgEl, f){
52394             msgEl.fixDisplay();
52395             msgEl.alignTo(f.el, 'tl-tr');
52396             msgEl.slideIn('l', {stopFx:true});
52397         },
52398
52399         hide : function(msgEl, f){
52400             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
52401         }
52402     }
52403 };
52404 Ext.reg('field', Ext.form.Field);
52405 /**
52406  * @class Ext.form.TextField
52407  * @extends Ext.form.Field
52408  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
52409  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
52410  * and {@link Ext.form.ComboBox}).</p>
52411  * <p><b><u>Validation</u></b></p>
52412  * <p>Field validation is processed in a particular order.  If validation fails at any particular
52413  * step the validation routine halts.</p>
52414  * <div class="mdetail-params"><ul>
52415  * <li><b>1. Field specific validator</b>
52416  * <div class="sub-desc">
52417  * <p>If a field is configured with a <code>{@link Ext.form.TextField#validator validator}</code> function,
52418  * it will be passed the current field value.  The <code>{@link Ext.form.TextField#validator validator}</code>
52419  * function is expected to return boolean <tt>true</tt> if the value is valid or return a string to
52420  * represent the invalid message if invalid.</p>
52421  * </div></li>
52422  * <li><b>2. Built in Validation</b>
52423  * <div class="sub-desc">
52424  * <p>Basic validation is affected with the following configuration properties:</p>
52425  * <pre>
52426  * <u>Validation</u>    <u>Invalid Message</u>
52427  * <code>{@link Ext.form.TextField#allowBlank allowBlank}    {@link Ext.form.TextField#emptyText emptyText}</code>
52428  * <code>{@link Ext.form.TextField#minLength minLength}     {@link Ext.form.TextField#minLengthText minLengthText}</code>
52429  * <code>{@link Ext.form.TextField#maxLength maxLength}     {@link Ext.form.TextField#maxLengthText maxLengthText}</code>
52430  * </pre>
52431  * </div></li>
52432  * <li><b>3. Preconfigured Validation Types (VTypes)</b>
52433  * <div class="sub-desc">
52434  * <p>Using VTypes offers a convenient way to reuse validation. If a field is configured with a
52435  * <code>{@link Ext.form.TextField#vtype vtype}</code>, the corresponding {@link Ext.form.VTypes VTypes}
52436  * validation function will be used for validation.  If invalid, either the field's
52437  * <code>{@link Ext.form.TextField#vtypeText vtypeText}</code> or the VTypes vtype Text property will be
52438  * used for the invalid message.  Keystrokes on the field will be filtered according to the VTypes
52439  * vtype Mask property.</p>
52440  * </div></li>
52441  * <li><b>4. Field specific regex test</b>
52442  * <div class="sub-desc">
52443  * <p>Each field may also specify a <code>{@link Ext.form.TextField#regex regex}</code> test.
52444  * The invalid message for this test is configured with
52445  * <code>{@link Ext.form.TextField#regexText regexText}</code>.</p>
52446  * </div></li>
52447  * <li><b>Alter Validation Behavior</b>
52448  * <div class="sub-desc">
52449  * <p>Validation behavior for each field can be configured:</p><ul>
52450  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
52451  * show if any validation step above does not provide a message when invalid</li>
52452  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
52453  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
52454  * but before being validated</li>
52455  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
52456  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
52457  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
52458  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
52459  * </ul>
52460  * </div></li>
52461  * </ul></div>
52462  * @constructor
52463  * Creates a new TextField
52464  * @param {Object} config Configuration options
52465  * @xtype textfield
52466  */
52467 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
52468     /**
52469      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
52470      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
52471      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
52472      */
52473     /**
52474      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
52475      * before validation (defaults to <tt>null</tt>).
52476      */
52477     /**
52478      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
52479      * (defaults to <tt>false</tt>)
52480      */
52481     grow : false,
52482     /**
52483      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
52484      * to <tt>30</tt>)
52485      */
52486     growMin : 30,
52487     /**
52488      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
52489      * to <tt>800</tt>)
52490      */
52491     growMax : 800,
52492     /**
52493      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
52494      */
52495     vtype : null,
52496     /**
52497      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
52498      * not match (defaults to <tt>null</tt>)
52499      */
52500     maskRe : null,
52501     /**
52502      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
52503      * to <tt>false</tt>)
52504      */
52505     disableKeyFilter : false,
52506     /**
52507      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
52508      * <tt>true</tt>)
52509      */
52510     allowBlank : true,
52511     /**
52512      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
52513      */
52514     minLength : 0,
52515     /**
52516      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
52517      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
52518      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
52519      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
52520      * any attributes you want to a field, for example:<pre><code>
52521 var myField = new Ext.form.NumberField({
52522     id: 'mobile',
52523     anchor:'90%',
52524     fieldLabel: 'Mobile',
52525     maxLength: 16, // for validation
52526     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
52527 });
52528 </code></pre>
52529      */
52530     maxLength : Number.MAX_VALUE,
52531     /**
52532      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
52533      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
52534      */
52535     minLengthText : 'The minimum length for this field is {0}',
52536     /**
52537      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
52538      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
52539      */
52540     maxLengthText : 'The maximum length for this field is {0}',
52541     /**
52542      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
52543      * receives input focus (defaults to <tt>false</tt>)
52544      */
52545     selectOnFocus : false,
52546     /**
52547      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
52548      * fails (defaults to <tt>'This field is required'</tt>)
52549      */
52550     blankText : 'This field is required',
52551     /**
52552      * @cfg {Function} validator A custom validation function to be called during field validation
52553      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
52554      * developer to override the default validation process. This function will be passed the current
52555      * field value and expected to return boolean <tt>true</tt> if the value is valid or a string
52556      * error message if invalid.
52557      */
52558     validator : null,
52559     /**
52560      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
52561      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
52562      * <b><tt>{@link #regexText}</tt></b>.
52563      */
52564     regex : null,
52565     /**
52566      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
52567      * test fails during validation (defaults to <tt>''</tt>)
52568      */
52569     regexText : '',
52570     /**
52571      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
52572      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
52573      * with a {@link #name}.
52574      */
52575     emptyText : null,
52576     /**
52577      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
52578      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
52579      * depending on the current field value.
52580      */
52581     emptyClass : 'x-form-empty-field',
52582
52583     /**
52584      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
52585      * field (defaults to <tt>false</tt>)
52586      */
52587
52588     initComponent : function(){
52589         Ext.form.TextField.superclass.initComponent.call(this);
52590         this.addEvents(
52591             /**
52592              * @event autosize
52593              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
52594              * may not have actually changed size according to the default logic, but this event provides
52595              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
52596              * @param {Ext.form.Field} this This text field
52597              * @param {Number} width The new field width
52598              */
52599             'autosize',
52600
52601             /**
52602              * @event keydown
52603              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
52604              * is set to true.
52605              * @param {Ext.form.TextField} this This text field
52606              * @param {Ext.EventObject} e
52607              */
52608             'keydown',
52609             /**
52610              * @event keyup
52611              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
52612              * is set to true.
52613              * @param {Ext.form.TextField} this This text field
52614              * @param {Ext.EventObject} e
52615              */
52616             'keyup',
52617             /**
52618              * @event keypress
52619              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
52620              * is set to true.
52621              * @param {Ext.form.TextField} this This text field
52622              * @param {Ext.EventObject} e
52623              */
52624             'keypress'
52625         );
52626     },
52627
52628     // private
52629     initEvents : function(){
52630         Ext.form.TextField.superclass.initEvents.call(this);
52631         if(this.validationEvent == 'keyup'){
52632             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
52633             this.mon(this.el, 'keyup', this.filterValidation, this);
52634         }
52635         else if(this.validationEvent !== false){
52636                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
52637         }
52638         if(this.selectOnFocus || this.emptyText){
52639             this.on('focus', this.preFocus, this);
52640             
52641             this.mon(this.el, 'mousedown', function(){
52642                 if(!this.hasFocus){
52643                     this.el.on('mouseup', function(e){
52644                         e.preventDefault();
52645                     }, this, {single:true});
52646                 }
52647             }, this);
52648             
52649             if(this.emptyText){
52650                 this.on('blur', this.postBlur, this);
52651                 this.applyEmptyText();
52652             }
52653         }
52654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
52655                 this.mon(this.el, 'keypress', this.filterKeys, this);
52656         }
52657         if(this.grow){
52658                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
52659                         this.mon(this.el, 'click', this.autoSize, this);
52660         }
52661         if(this.enableKeyEvents){
52662                 this.mon(this.el, 'keyup', this.onKeyUp, this);
52663                 this.mon(this.el, 'keydown', this.onKeyDown, this);
52664                 this.mon(this.el, 'keypress', this.onKeyPress, this);
52665         }
52666     },
52667
52668     processValue : function(value){
52669         if(this.stripCharsRe){
52670             var newValue = value.replace(this.stripCharsRe, '');
52671             if(newValue !== value){
52672                 this.setRawValue(newValue);
52673                 return newValue;
52674             }
52675         }
52676         return value;
52677     },
52678
52679     filterValidation : function(e){
52680         if(!e.isNavKeyPress()){
52681             this.validationTask.delay(this.validationDelay);
52682         }
52683     },
52684     
52685     //private
52686     onDisable: function(){
52687         Ext.form.TextField.superclass.onDisable.call(this);
52688         if(Ext.isIE){
52689             this.el.dom.unselectable = 'on';
52690         }
52691     },
52692     
52693     //private
52694     onEnable: function(){
52695         Ext.form.TextField.superclass.onEnable.call(this);
52696         if(Ext.isIE){
52697             this.el.dom.unselectable = '';
52698         }
52699     },
52700
52701     // private
52702     onKeyUpBuffered : function(e){
52703         if(!e.isNavKeyPress()){
52704             this.autoSize();
52705         }
52706     },
52707
52708     // private
52709     onKeyUp : function(e){
52710         this.fireEvent('keyup', this, e);
52711     },
52712
52713     // private
52714     onKeyDown : function(e){
52715         this.fireEvent('keydown', this, e);
52716     },
52717
52718     // private
52719     onKeyPress : function(e){
52720         this.fireEvent('keypress', this, e);
52721     },
52722
52723     /**
52724      * Resets the current field value to the originally-loaded value and clears any validation messages.
52725      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
52726      * original value was blank.
52727      */
52728     reset : function(){
52729         Ext.form.TextField.superclass.reset.call(this);
52730         this.applyEmptyText();
52731     },
52732
52733     applyEmptyText : function(){
52734         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
52735             this.setRawValue(this.emptyText);
52736             this.el.addClass(this.emptyClass);
52737         }
52738     },
52739
52740     // private
52741     preFocus : function(){
52742         var el = this.el;
52743         if(this.emptyText){
52744             if(el.dom.value == this.emptyText){
52745                 this.setRawValue('');
52746             }
52747             el.removeClass(this.emptyClass);
52748         }
52749         if(this.selectOnFocus){
52750             (function(){
52751                 el.dom.select();
52752             }).defer(this.inEditor && Ext.isIE ? 50 : 0);    
52753         }
52754     },
52755
52756     // private
52757     postBlur : function(){
52758         this.applyEmptyText();
52759     },
52760
52761     // private
52762     filterKeys : function(e){
52763         // special keys don't generate charCodes, so leave them alone
52764         if(e.ctrlKey || e.isSpecialKey()){
52765             return;
52766         }
52767         
52768         if(!this.maskRe.test(String.fromCharCode(e.getCharCode()))){
52769             e.stopEvent();
52770         }
52771     },
52772
52773     setValue : function(v){
52774         if(this.emptyText && this.el && !Ext.isEmpty(v)){
52775             this.el.removeClass(this.emptyClass);
52776         }
52777         Ext.form.TextField.superclass.setValue.apply(this, arguments);
52778         this.applyEmptyText();
52779         this.autoSize();
52780         return this;
52781     },
52782
52783     /**
52784      * Validates a value according to the field's validation rules and marks the field as invalid
52785      * if the validation fails
52786      * @param {Mixed} value The value to validate
52787      * @return {Boolean} True if the value is valid, else false
52788      */
52789     validateValue : function(value){
52790         if(Ext.isFunction(this.validator)){
52791             var msg = this.validator(value);
52792             if(msg !== true){
52793                 this.markInvalid(msg);
52794                 return false;
52795             }
52796         }
52797         if(value.length < 1 || value === this.emptyText){ // if it's blank
52798              if(this.allowBlank){
52799                  this.clearInvalid();
52800                  return true;
52801              }else{
52802                  this.markInvalid(this.blankText);
52803                  return false;
52804              }
52805         }
52806         if(value.length < this.minLength){
52807             this.markInvalid(String.format(this.minLengthText, this.minLength));
52808             return false;
52809         }
52810         if(value.length > this.maxLength){
52811             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
52812             return false;
52813         }       
52814         if(this.vtype){
52815             var vt = Ext.form.VTypes;
52816             if(!vt[this.vtype](value, this)){
52817                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
52818                 return false;
52819             }
52820         }
52821         if(this.regex && !this.regex.test(value)){
52822             this.markInvalid(this.regexText);
52823             return false;
52824         }
52825         return true;
52826     },
52827
52828     /**
52829      * Selects text in this field
52830      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
52831      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
52832      */
52833     selectText : function(start, end){
52834         var v = this.getRawValue();
52835         var doFocus = false;
52836         if(v.length > 0){
52837             start = start === undefined ? 0 : start;
52838             end = end === undefined ? v.length : end;
52839             var d = this.el.dom;
52840             if(d.setSelectionRange){
52841                 d.setSelectionRange(start, end);
52842             }else if(d.createTextRange){
52843                 var range = d.createTextRange();
52844                 range.moveStart('character', start);
52845                 range.moveEnd('character', end-v.length);
52846                 range.select();
52847             }
52848             doFocus = Ext.isGecko || Ext.isOpera;
52849         }else{
52850             doFocus = true;
52851         }
52852         if(doFocus){
52853             this.focus();
52854         }
52855     },
52856
52857     /**
52858      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
52859      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
52860      */
52861     autoSize : function(){
52862         if(!this.grow || !this.rendered){
52863             return;
52864         }
52865         if(!this.metrics){
52866             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
52867         }
52868         var el = this.el;
52869         var v = el.dom.value;
52870         var d = document.createElement('div');
52871         d.appendChild(document.createTextNode(v));
52872         v = d.innerHTML;
52873         d = null;
52874         Ext.removeNode(d);
52875         v += '&#160;';
52876         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
52877         this.el.setWidth(w);
52878         this.fireEvent('autosize', this, w);
52879     },
52880         
52881         onDestroy: function(){
52882                 if(this.validationTask){
52883                         this.validationTask.cancel();
52884                         this.validationTask = null;
52885                 }
52886                 Ext.form.TextField.superclass.onDestroy.call(this);
52887         }
52888 });
52889 Ext.reg('textfield', Ext.form.TextField);
52890 /**
52891  * @class Ext.form.TriggerField
52892  * @extends Ext.form.TextField
52893  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
52894  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
52895  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
52896  * for which you can provide a custom implementation.  For example:
52897  * <pre><code>
52898 var trigger = new Ext.form.TriggerField();
52899 trigger.onTriggerClick = myTriggerFn;
52900 trigger.applyToMarkup('my-field');
52901 </code></pre>
52902  *
52903  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
52904  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
52905  * 
52906  * @constructor
52907  * Create a new TriggerField.
52908  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
52909  * to the base TextField)
52910  * @xtype trigger
52911  */
52912 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
52913     /**
52914      * @cfg {String} triggerClass
52915      * An additional CSS class used to style the trigger button.  The trigger will always get the
52916      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
52917      */
52918     /**
52919      * @cfg {Mixed} triggerConfig
52920      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
52921      * trigger element for this Field. (Optional).</p>
52922      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
52923      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
52924      * and appearance of the trigger.  Defaults to:</p>
52925      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
52926      */
52927     /**
52928      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
52929      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
52930      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
52931      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
52932      */
52933     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
52934     /**
52935      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
52936      * text field (defaults to <tt>false</tt>)
52937      */
52938     hideTrigger:false,
52939     /**
52940      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
52941      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>)
52942      */
52943     editable: true,
52944     /**
52945      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
52946      * <tt>x-trigger-wrap-focus</tt>.
52947      */
52948     wrapFocusClass: 'x-trigger-wrap-focus',
52949     /**
52950      * @hide 
52951      * @method autoSize
52952      */
52953     autoSize: Ext.emptyFn,
52954     // private
52955     monitorTab : true,
52956     // private
52957     deferHeight : true,
52958     // private
52959     mimicing : false,
52960     
52961     actionMode: 'wrap',
52962
52963     // private
52964     onResize : function(w, h){
52965         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
52966         if(typeof w == 'number'){
52967             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
52968         }
52969         this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
52970     },
52971
52972     // private
52973     adjustSize : Ext.BoxComponent.prototype.adjustSize,
52974
52975     // private
52976     getResizeEl : function(){
52977         return this.wrap;
52978     },
52979
52980     // private
52981     getPositionEl : function(){
52982         return this.wrap;
52983     },
52984
52985     // private
52986     alignErrorIcon : function(){
52987         if(this.wrap){
52988             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
52989         }
52990     },
52991
52992     // private
52993     onRender : function(ct, position){
52994         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
52995
52996         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
52997         this.trigger = this.wrap.createChild(this.triggerConfig ||
52998                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
52999         if(this.hideTrigger){
53000             this.trigger.setDisplayed(false);
53001         }
53002         this.initTrigger();
53003         if(!this.width){
53004             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
53005         }
53006         if(!this.editable){
53007             this.editable = true;
53008             this.setEditable(false);
53009         }
53010     },
53011
53012     afterRender : function(){
53013         Ext.form.TriggerField.superclass.afterRender.call(this);
53014     },
53015
53016     // private
53017     initTrigger : function(){
53018         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
53019         this.trigger.addClassOnOver('x-form-trigger-over');
53020         this.trigger.addClassOnClick('x-form-trigger-click');
53021     },
53022
53023     // private
53024     onDestroy : function(){
53025                 Ext.destroy(this.trigger, this.wrap);
53026         if (this.mimicing){
53027             Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
53028         }
53029         Ext.form.TriggerField.superclass.onDestroy.call(this);
53030     },
53031
53032     // private
53033     onFocus : function(){
53034         Ext.form.TriggerField.superclass.onFocus.call(this);
53035         if(!this.mimicing){
53036             this.wrap.addClass(this.wrapFocusClass);
53037             this.mimicing = true;
53038             Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {delay: 10});
53039             if(this.monitorTab){
53040                 this.el.on('keydown', this.checkTab, this);
53041             }
53042         }
53043     },
53044
53045     // private
53046     checkTab : function(e){
53047         if(e.getKey() == e.TAB){
53048             this.triggerBlur();
53049         }
53050     },
53051
53052     // private
53053     onBlur : function(){
53054         // do nothing
53055     },
53056
53057     // private
53058     mimicBlur : function(e){
53059         if(!this.wrap.contains(e.target) && this.validateBlur(e)){
53060             this.triggerBlur();
53061         }
53062     },
53063
53064     // private
53065     triggerBlur : function(){
53066         this.mimicing = false;
53067         Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
53068         if(this.monitorTab && this.el){
53069             this.el.un("keydown", this.checkTab, this);
53070         }
53071         Ext.form.TriggerField.superclass.onBlur.call(this);
53072         if(this.wrap){
53073             this.wrap.removeClass(this.wrapFocusClass);
53074         }
53075     },
53076
53077     beforeBlur : Ext.emptyFn, 
53078     
53079     /**
53080      * Allow or prevent the user from directly editing the field text.  If false is passed,
53081      * the user will only be able to modify the field using the trigger.  This method
53082      * is the runtime equivalent of setting the 'editable' config option at config time.
53083      * @param {Boolean} value True to allow the user to directly edit the field text
53084      */
53085     setEditable : function(value){
53086         if(value == this.editable){
53087             return;
53088         }
53089         this.editable = value;
53090         if(!value){
53091             this.el.addClass('x-trigger-noedit').on('click', this.onTriggerClick, this).dom.setAttribute('readOnly', true);
53092         }else{
53093             this.el.removeClass('x-trigger-noedit').un('click', this.onTriggerClick,  this).dom.removeAttribute('readOnly');
53094         }
53095     },
53096
53097     // private
53098     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
53099     validateBlur : function(e){
53100         return true;
53101     },
53102
53103     /**
53104      * The function that should handle the trigger's click event.  This method does nothing by default
53105      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
53106      * sample implementations.
53107      * @method
53108      * @param {EventObject} e
53109      */
53110     onTriggerClick : Ext.emptyFn
53111
53112     /**
53113      * @cfg {Boolean} grow @hide
53114      */
53115     /**
53116      * @cfg {Number} growMin @hide
53117      */
53118     /**
53119      * @cfg {Number} growMax @hide
53120      */
53121 });
53122
53123 /**
53124  * @class Ext.form.TwinTriggerField
53125  * @extends Ext.form.TriggerField
53126  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
53127  * to be extended by an implementing class.  For an example of implementing this class, see the custom
53128  * SearchField implementation here:
53129  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
53130  */
53131 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
53132     /**
53133      * @cfg {Mixed} triggerConfig
53134      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
53135      * for this Field. (Optional).</p>
53136      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
53137      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
53138      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
53139      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
53140      * positioning and appearance of the triggers.</p>
53141      */
53142     /**
53143      * @cfg {String} trigger1Class
53144      * An additional CSS class used to style the trigger button.  The trigger will always get the
53145      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
53146      */
53147     /**
53148      * @cfg {String} trigger2Class
53149      * An additional CSS class used to style the trigger button.  The trigger will always get the
53150      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
53151      */
53152
53153     initComponent : function(){
53154         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
53155
53156         this.triggerConfig = {
53157             tag:'span', cls:'x-form-twin-triggers', cn:[
53158             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
53159             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
53160         ]};
53161     },
53162
53163     getTrigger : function(index){
53164         return this.triggers[index];
53165     },
53166
53167     initTrigger : function(){
53168         var ts = this.trigger.select('.x-form-trigger', true);
53169         this.wrap.setStyle('overflow', 'hidden');
53170         var triggerField = this;
53171         ts.each(function(t, all, index){
53172             t.hide = function(){
53173                 var w = triggerField.wrap.getWidth();
53174                 this.dom.style.display = 'none';
53175                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
53176             };
53177             t.show = function(){
53178                 var w = triggerField.wrap.getWidth();
53179                 this.dom.style.display = '';
53180                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
53181             };
53182             var triggerIndex = 'Trigger'+(index+1);
53183
53184             if(this['hide'+triggerIndex]){
53185                 t.dom.style.display = 'none';
53186             }
53187             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
53188             t.addClassOnOver('x-form-trigger-over');
53189             t.addClassOnClick('x-form-trigger-click');
53190         }, this);
53191         this.triggers = ts.elements;
53192     },
53193
53194     /**
53195      * The function that should handle the trigger's click event.  This method does nothing by default
53196      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
53197      * for additional information.  
53198      * @method
53199      * @param {EventObject} e
53200      */
53201     onTrigger1Click : Ext.emptyFn,
53202     /**
53203      * The function that should handle the trigger's click event.  This method does nothing by default
53204      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
53205      * for additional information.  
53206      * @method
53207      * @param {EventObject} e
53208      */
53209     onTrigger2Click : Ext.emptyFn
53210 });
53211 Ext.reg('trigger', Ext.form.TriggerField);/**
53212  * @class Ext.form.TextArea
53213  * @extends Ext.form.TextField
53214  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
53215  * support for auto-sizing.
53216  * @constructor
53217  * Creates a new TextArea
53218  * @param {Object} config Configuration options
53219  * @xtype textarea
53220  */
53221 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
53222     /**
53223      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
53224      * (defaults to <tt>60</tt>)
53225      */
53226     growMin : 60,
53227     /**
53228      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
53229      * (defaults to <tt>1000</tt>)
53230      */
53231     growMax: 1000,
53232     growAppend : '&#160;\n&#160;',
53233     growPad : Ext.isWebKit ? -6 : 0,
53234
53235     enterIsSpecial : false,
53236
53237     /**
53238      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
53239      * in the field (equivalent to setting overflow: hidden, defaults to <tt>false</tt>)
53240      */
53241     preventScrollbars: false,
53242     /**
53243      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
53244      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
53245      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
53246      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
53247      */
53248
53249     // private
53250     onRender : function(ct, position){
53251         if(!this.el){
53252             this.defaultAutoCreate = {
53253                 tag: "textarea",
53254                 style:"width:100px;height:60px;",
53255                 autocomplete: "off"
53256             };
53257         }
53258         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
53259         if(this.grow){
53260             this.textSizeEl = Ext.DomHelper.append(document.body, {
53261                 tag: "pre", cls: "x-form-grow-sizer"
53262             });
53263             if(this.preventScrollbars){
53264                 this.el.setStyle("overflow", "hidden");
53265             }
53266             this.el.setHeight(this.growMin);
53267         }
53268     },
53269
53270     onDestroy : function(){
53271         Ext.destroy(this.textSizeEl);
53272         Ext.form.TextArea.superclass.onDestroy.call(this);
53273     },
53274
53275     fireKey : function(e){
53276         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
53277             this.fireEvent("specialkey", this, e);
53278         }
53279     },
53280
53281     // private
53282     onKeyUp : function(e){
53283         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
53284             this.autoSize();
53285         }
53286         Ext.form.TextArea.superclass.onKeyUp.call(this, e);
53287     },
53288
53289     /**
53290      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
53291      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
53292      */
53293     autoSize: function(){
53294         if(!this.grow || !this.textSizeEl){
53295             return;
53296         }
53297         var el = this.el;
53298         var v = el.dom.value;
53299         var ts = this.textSizeEl;
53300         ts.innerHTML = '';
53301         ts.appendChild(document.createTextNode(v));
53302         v = ts.innerHTML;
53303         Ext.fly(ts).setWidth(this.el.getWidth());
53304         if(v.length < 1){
53305             v = "&#160;&#160;";
53306         }else{
53307             v += this.growAppend;
53308             if(Ext.isIE){
53309                 v = v.replace(/\n/g, '<br />');
53310             }
53311         }
53312         ts.innerHTML = v;
53313         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin) + this.growPad);
53314         if(h != this.lastHeight){
53315             this.lastHeight = h;
53316             this.el.setHeight(h);
53317             this.fireEvent("autosize", this, h);
53318         }
53319     }
53320 });
53321 Ext.reg('textarea', Ext.form.TextArea);/**
53322  * @class Ext.form.NumberField
53323  * @extends Ext.form.TextField
53324  * Numeric text field that provides automatic keystroke filtering and numeric validation.
53325  * @constructor
53326  * Creates a new NumberField
53327  * @param {Object} config Configuration options
53328  * @xtype numberfield
53329  */
53330 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
53331     /**
53332      * @cfg {RegExp} stripCharsRe @hide
53333      */
53334     /**
53335      * @cfg {RegExp} maskRe @hide
53336      */
53337     /**
53338      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
53339      */
53340     fieldClass: "x-form-field x-form-num-field",
53341     /**
53342      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
53343      */
53344     allowDecimals : true,
53345     /**
53346      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
53347      */
53348     decimalSeparator : ".",
53349     /**
53350      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
53351      */
53352     decimalPrecision : 2,
53353     /**
53354      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
53355      */
53356     allowNegative : true,
53357     /**
53358      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
53359      */
53360     minValue : Number.NEGATIVE_INFINITY,
53361     /**
53362      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
53363      */
53364     maxValue : Number.MAX_VALUE,
53365     /**
53366      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
53367      */
53368     minText : "The minimum value for this field is {0}",
53369     /**
53370      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
53371      */
53372     maxText : "The maximum value for this field is {0}",
53373     /**
53374      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
53375      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
53376      */
53377     nanText : "{0} is not a valid number",
53378     /**
53379      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
53380      */
53381     baseChars : "0123456789",
53382
53383     // private
53384     initEvents : function(){
53385         var allowed = this.baseChars + '';
53386         if (this.allowDecimals) {
53387             allowed += this.decimalSeparator;
53388         }
53389         if (this.allowNegative) {
53390             allowed += '-';
53391         }
53392         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
53393         Ext.form.NumberField.superclass.initEvents.call(this);
53394     },
53395
53396     // private
53397     validateValue : function(value){
53398         if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
53399             return false;
53400         }
53401         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
53402              return true;
53403         }
53404         value = String(value).replace(this.decimalSeparator, ".");
53405         if(isNaN(value)){
53406             this.markInvalid(String.format(this.nanText, value));
53407             return false;
53408         }
53409         var num = this.parseValue(value);
53410         if(num < this.minValue){
53411             this.markInvalid(String.format(this.minText, this.minValue));
53412             return false;
53413         }
53414         if(num > this.maxValue){
53415             this.markInvalid(String.format(this.maxText, this.maxValue));
53416             return false;
53417         }
53418         return true;
53419     },
53420
53421     getValue : function(){
53422         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
53423     },
53424
53425     setValue : function(v){
53426         v = typeof v == 'number' ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
53427         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
53428         return Ext.form.NumberField.superclass.setValue.call(this, v);
53429     },
53430
53431     // private
53432     parseValue : function(value){
53433         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
53434         return isNaN(value) ? '' : value;
53435     },
53436
53437     // private
53438     fixPrecision : function(value){
53439         var nan = isNaN(value);
53440         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
53441            return nan ? '' : value;
53442         }
53443         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
53444     },
53445
53446     beforeBlur : function(){
53447         var v = this.parseValue(this.getRawValue());
53448         if(!Ext.isEmpty(v)){
53449             this.setValue(this.fixPrecision(v));
53450         }
53451     }
53452 });
53453 Ext.reg('numberfield', Ext.form.NumberField);/**
53454  * @class Ext.form.DateField
53455  * @extends Ext.form.TriggerField
53456  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
53457  * @constructor
53458  * Create a new DateField
53459  * @param {Object} config
53460  * @xtype datefield
53461  */
53462 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
53463     /**
53464      * @cfg {String} format
53465      * The default date format string which can be overriden for localization support.  The format must be
53466      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
53467      */
53468     format : "m/d/Y",
53469     /**
53470      * @cfg {String} altFormats
53471      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
53472      * does not match the defined format (defaults to
53473      * <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>).
53474      */
53475     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",
53476     /**
53477      * @cfg {String} disabledDaysText
53478      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
53479      */
53480     disabledDaysText : "Disabled",
53481     /**
53482      * @cfg {String} disabledDatesText
53483      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
53484      */
53485     disabledDatesText : "Disabled",
53486     /**
53487      * @cfg {String} minText
53488      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
53489      * <tt>'The date in this field must be after {minValue}'</tt>).
53490      */
53491     minText : "The date in this field must be equal to or after {0}",
53492     /**
53493      * @cfg {String} maxText
53494      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
53495      * <tt>'The date in this field must be before {maxValue}'</tt>).
53496      */
53497     maxText : "The date in this field must be equal to or before {0}",
53498     /**
53499      * @cfg {String} invalidText
53500      * The error text to display when the date in the field is invalid (defaults to
53501      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
53502      */
53503     invalidText : "{0} is not a valid date - it must be in the format {1}",
53504     /**
53505      * @cfg {String} triggerClass
53506      * An additional CSS class used to style the trigger button.  The trigger will always get the
53507      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
53508      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
53509      */
53510     triggerClass : 'x-form-date-trigger',
53511     /**
53512      * @cfg {Boolean} showToday
53513      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
53514      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
53515      */
53516     showToday : true,
53517     /**
53518      * @cfg {Date/String} minValue
53519      * The minimum allowed date. Can be either a Javascript date object or a string date in a
53520      * valid format (defaults to null).
53521      */
53522     /**
53523      * @cfg {Date/String} maxValue
53524      * The maximum allowed date. Can be either a Javascript date object or a string date in a
53525      * valid format (defaults to null).
53526      */
53527     /**
53528      * @cfg {Array} disabledDays
53529      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
53530 // disable Sunday and Saturday:
53531 disabledDays:  [0, 6]
53532 // disable weekdays:
53533 disabledDays: [1,2,3,4,5]
53534      * </code></pre>
53535      */
53536     /**
53537      * @cfg {Array} disabledDates
53538      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
53539      * expression so they are very powerful. Some examples:<pre><code>
53540 // disable these exact dates:
53541 disabledDates: ["03/08/2003", "09/16/2003"]
53542 // disable these days for every year:
53543 disabledDates: ["03/08", "09/16"]
53544 // only match the beginning (useful if you are using short years):
53545 disabledDates: ["^03/08"]
53546 // disable every day in March 2006:
53547 disabledDates: ["03/../2006"]
53548 // disable every day in every March:
53549 disabledDates: ["^03"]
53550      * </code></pre>
53551      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
53552      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
53553      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
53554      */
53555     /**
53556      * @cfg {String/Object} autoCreate
53557      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
53558      * specification object:<pre><code>
53559      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
53560      * </code></pre>
53561      */
53562
53563     // private
53564     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
53565
53566     initComponent : function(){
53567         Ext.form.DateField.superclass.initComponent.call(this);
53568
53569         this.addEvents(
53570             /**
53571              * @event select
53572              * Fires when a date is selected via the date picker.
53573              * @param {Ext.form.DateField} this
53574              * @param {Date} date The date that was selected
53575              */
53576             'select'
53577         );
53578
53579         if(Ext.isString(this.minValue)){
53580             this.minValue = this.parseDate(this.minValue);
53581         }
53582         if(Ext.isString(this.maxValue)){
53583             this.maxValue = this.parseDate(this.maxValue);
53584         }
53585         this.disabledDatesRE = null;
53586         this.initDisabledDays();
53587     },
53588
53589     // private
53590     initDisabledDays : function(){
53591         if(this.disabledDates){
53592             var dd = this.disabledDates,
53593                 len = dd.length - 1, 
53594                 re = "(?:";
53595                 
53596             Ext.each(dd, function(d, i){
53597                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
53598                 if(i != len){
53599                     re += '|';
53600                 }
53601             }, this);
53602             this.disabledDatesRE = new RegExp(re + ')');
53603         }
53604     },
53605
53606     /**
53607      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
53608      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
53609      * for details on supported values) used to disable a pattern of dates.
53610      */
53611     setDisabledDates : function(dd){
53612         this.disabledDates = dd;
53613         this.initDisabledDays();
53614         if(this.menu){
53615             this.menu.picker.setDisabledDates(this.disabledDatesRE);
53616         }
53617     },
53618
53619     /**
53620      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
53621      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
53622      * config for details on supported values.
53623      */
53624     setDisabledDays : function(dd){
53625         this.disabledDays = dd;
53626         if(this.menu){
53627             this.menu.picker.setDisabledDays(dd);
53628         }
53629     },
53630
53631     /**
53632      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
53633      * @param {Date} value The minimum date that can be selected
53634      */
53635     setMinValue : function(dt){
53636         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
53637         if(this.menu){
53638             this.menu.picker.setMinDate(this.minValue);
53639         }
53640     },
53641
53642     /**
53643      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
53644      * @param {Date} value The maximum date that can be selected
53645      */
53646     setMaxValue : function(dt){
53647         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
53648         if(this.menu){
53649             this.menu.picker.setMaxDate(this.maxValue);
53650         }
53651     },
53652
53653     // private
53654     validateValue : function(value){
53655         value = this.formatDate(value);
53656         if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
53657             return false;
53658         }
53659         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
53660              return true;
53661         }
53662         var svalue = value;
53663         value = this.parseDate(value);
53664         if(!value){
53665             this.markInvalid(String.format(this.invalidText, svalue, this.format));
53666             return false;
53667         }
53668         var time = value.getTime();
53669         if(this.minValue && time < this.minValue.getTime()){
53670             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
53671             return false;
53672         }
53673         if(this.maxValue && time > this.maxValue.getTime()){
53674             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
53675             return false;
53676         }
53677         if(this.disabledDays){
53678             var day = value.getDay();
53679             for(var i = 0; i < this.disabledDays.length; i++) {
53680                 if(day === this.disabledDays[i]){
53681                     this.markInvalid(this.disabledDaysText);
53682                     return false;
53683                 }
53684             }
53685         }
53686         var fvalue = this.formatDate(value);
53687         if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
53688             this.markInvalid(String.format(this.disabledDatesText, fvalue));
53689             return false;
53690         }
53691         return true;
53692     },
53693
53694     // private
53695     // Provides logic to override the default TriggerField.validateBlur which just returns true
53696     validateBlur : function(){
53697         return !this.menu || !this.menu.isVisible();
53698     },
53699
53700     /**
53701      * Returns the current date value of the date field.
53702      * @return {Date} The date value
53703      */
53704     getValue : function(){
53705         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
53706     },
53707
53708     /**
53709      * Sets the value of the date field.  You can pass a date object or any string that can be
53710      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
53711      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
53712      * <br />Usage:
53713      * <pre><code>
53714 //All of these calls set the same date value (May 4, 2006)
53715
53716 //Pass a date object:
53717 var dt = new Date('5/4/2006');
53718 dateField.setValue(dt);
53719
53720 //Pass a date string (default format):
53721 dateField.setValue('05/04/2006');
53722
53723 //Pass a date string (custom format):
53724 dateField.format = 'Y-m-d';
53725 dateField.setValue('2006-05-04');
53726 </code></pre>
53727      * @param {String/Date} date The date or valid date string
53728      * @return {Ext.form.Field} this
53729      */
53730     setValue : function(date){
53731         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
53732     },
53733
53734     // private
53735     parseDate : function(value){
53736         if(!value || Ext.isDate(value)){
53737             return value;
53738         }
53739         var v = Date.parseDate(value, this.format);
53740         if(!v && this.altFormats){
53741             if(!this.altFormatsArray){
53742                 this.altFormatsArray = this.altFormats.split("|");
53743             }
53744             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
53745                 v = Date.parseDate(value, this.altFormatsArray[i]);
53746             }
53747         }
53748         return v;
53749     },
53750
53751     // private
53752     onDestroy : function(){
53753                 Ext.destroy(this.menu);
53754         Ext.form.DateField.superclass.onDestroy.call(this);
53755     },
53756
53757     // private
53758     formatDate : function(date){
53759         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
53760     },
53761
53762     /**
53763      * @method onTriggerClick
53764      * @hide
53765      */
53766     // private
53767     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
53768     onTriggerClick : function(){
53769         if(this.disabled){
53770             return;
53771         }
53772         if(this.menu == null){
53773             this.menu = new Ext.menu.DateMenu({
53774                 hideOnClick: false
53775             });
53776         }
53777         this.onFocus();
53778         Ext.apply(this.menu.picker,  {
53779             minDate : this.minValue,
53780             maxDate : this.maxValue,
53781             disabledDatesRE : this.disabledDatesRE,
53782             disabledDatesText : this.disabledDatesText,
53783             disabledDays : this.disabledDays,
53784             disabledDaysText : this.disabledDaysText,
53785             format : this.format,
53786             showToday : this.showToday,
53787             minText : String.format(this.minText, this.formatDate(this.minValue)),
53788             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
53789         });
53790         this.menu.picker.setValue(this.getValue() || new Date());
53791         this.menu.show(this.el, "tl-bl?");
53792         this.menuEvents('on');
53793     },
53794     
53795     //private
53796     menuEvents: function(method){
53797         this.menu[method]('select', this.onSelect, this);
53798         this.menu[method]('hide', this.onMenuHide, this);
53799         this.menu[method]('show', this.onFocus, this);
53800     },
53801     
53802     onSelect: function(m, d){
53803         this.setValue(d);
53804         this.fireEvent('select', this, d);
53805         this.menu.hide();
53806     },
53807     
53808     onMenuHide: function(){
53809         this.focus(false, 60);
53810         this.menuEvents('un');
53811     },
53812
53813     // private
53814     beforeBlur : function(){
53815         var v = this.parseDate(this.getRawValue());
53816         if(v){
53817             this.setValue(v);
53818         }
53819     }
53820
53821     /**
53822      * @cfg {Boolean} grow @hide
53823      */
53824     /**
53825      * @cfg {Number} growMin @hide
53826      */
53827     /**
53828      * @cfg {Number} growMax @hide
53829      */
53830     /**
53831      * @hide
53832      * @method autoSize
53833      */
53834 });
53835 Ext.reg('datefield', Ext.form.DateField);/**\r
53836  * @class Ext.form.DisplayField\r
53837  * @extends Ext.form.Field\r
53838  * A display-only text field which is not validated and not submitted.\r
53839  * @constructor\r
53840  * Creates a new DisplayField.\r
53841  * @param {Object} config Configuration options\r
53842  * @xtype displayfield\r
53843  */\r
53844 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {\r
53845     validationEvent : false,\r
53846     validateOnBlur : false,\r
53847     defaultAutoCreate : {tag: "div"},\r
53848     /**\r
53849      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)\r
53850      */\r
53851     fieldClass : "x-form-display-field",\r
53852     /**\r
53853      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to\r
53854      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than\r
53855      * rendering them as string literals per the default logic.\r
53856      */\r
53857     htmlEncode: false,\r
53858 \r
53859     // private\r
53860     initEvents : Ext.emptyFn,\r
53861 \r
53862     isValid : function(){\r
53863         return true;\r
53864     },\r
53865 \r
53866     validate : function(){\r
53867         return true;\r
53868     },\r
53869 \r
53870     getRawValue : function(){\r
53871         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');\r
53872         if(v === this.emptyText){\r
53873             v = '';\r
53874         }\r
53875         if(this.htmlEncode){\r
53876             v = Ext.util.Format.htmlDecode(v);\r
53877         }\r
53878         return v;\r
53879     },\r
53880 \r
53881     getValue : function(){\r
53882         return this.getRawValue();\r
53883     },\r
53884     \r
53885     getName: function() {\r
53886         return this.name;\r
53887     },\r
53888 \r
53889     setRawValue : function(v){\r
53890         if(this.htmlEncode){\r
53891             v = Ext.util.Format.htmlEncode(v);\r
53892         }\r
53893         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);\r
53894     },\r
53895 \r
53896     setValue : function(v){\r
53897         this.setRawValue(v);\r
53898         return this;\r
53899     }\r
53900     /** \r
53901      * @cfg {String} inputType \r
53902      * @hide\r
53903      */\r
53904     /** \r
53905      * @cfg {Boolean} disabled \r
53906      * @hide\r
53907      */\r
53908     /** \r
53909      * @cfg {Boolean} readOnly \r
53910      * @hide\r
53911      */\r
53912     /** \r
53913      * @cfg {Boolean} validateOnBlur \r
53914      * @hide\r
53915      */\r
53916     /** \r
53917      * @cfg {Number} validationDelay \r
53918      * @hide\r
53919      */\r
53920     /** \r
53921      * @cfg {String/Boolean} validationEvent \r
53922      * @hide\r
53923      */\r
53924 });\r
53925 \r
53926 Ext.reg('displayfield', Ext.form.DisplayField);\r
53927 /**
53928  * @class Ext.form.ComboBox
53929  * @extends Ext.form.TriggerField
53930  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
53931  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
53932  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
53933  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
53934  * which is named according to the {@link #name}.</p>
53935  * <p><b><u>Events</u></b></p>
53936  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
53937 var cb = new Ext.form.ComboBox({
53938     // all of your config options
53939     listeners:{
53940          scope: yourScope,
53941          'select': yourFunction
53942     }
53943 });
53944
53945 // Alternatively, you can assign events after the object is created:
53946 var cb = new Ext.form.ComboBox(yourOptions);
53947 cb.on('select', yourFunction, yourScope);
53948  * </code></pre></p>
53949  *
53950  * <p><b><u>ComboBox in Grid</u></b></p>
53951  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
53952  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
53953  * a reusable render, for example:<pre><code>
53954 // create reusable renderer
53955 Ext.util.Format.comboRenderer = function(combo){
53956     return function(value){
53957         var record = combo.findRecord(combo.{@link #valueField}, value);
53958         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
53959     }
53960 }
53961
53962 // create the combo instance
53963 var combo = new Ext.form.ComboBox({
53964     {@link #typeAhead}: true,
53965     {@link #triggerAction}: 'all',
53966     {@link #lazyRender}:true,
53967     {@link #mode}: 'local',
53968     {@link #store}: new Ext.data.ArrayStore({
53969         id: 0,
53970         fields: [
53971             'myId',
53972             'displayText'
53973         ],
53974         data: [[1, 'item1'], [2, 'item2']]
53975     }),
53976     {@link #valueField}: 'myId',
53977     {@link #displayField}: 'displayText'
53978 });
53979
53980 // snippet of column model used within grid
53981 var cm = new Ext.grid.ColumnModel([{
53982        ...
53983     },{
53984        header: "Some Header",
53985        dataIndex: 'whatever',
53986        width: 130,
53987        editor: combo, // specify reference to combo instance
53988        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
53989     },
53990     ...
53991 ]);
53992  * </code></pre></p>
53993  *
53994  * <p><b><u>Filtering</u></b></p>
53995  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
53996  * store manually see <tt>{@link #lastQuery}</tt>.</p>
53997  * @constructor
53998  * Create a new ComboBox.
53999  * @param {Object} config Configuration options
54000  * @xtype combo
54001  */
54002 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
54003     /**
54004      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
54005      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
54006      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
54007      */
54008     /**
54009      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
54010      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
54011      * defaults to <tt>false</tt>).
54012      */
54013     /**
54014      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
54015      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
54016      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
54017      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
54018      */
54019     /**
54020      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
54021      * Acceptable values for this property are:
54022      * <div class="mdetail-params"><ul>
54023      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
54024      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.
54025      * <div class="mdetail-params"><ul>
54026      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
54027      * A 1-dimensional array will automatically be expanded (each array item will be the combo
54028      * {@link #valueField value} and {@link #displayField text})</div></li>
54029      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
54030      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
54031      * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.
54032      * </div></li></ul></div></li></ul></div>
54033      * <p>See also <tt>{@link #mode}</tt>.</p>
54034      */
54035     /**
54036      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
54037      * the dropdown list (defaults to undefined, with no header element)
54038      */
54039
54040     // private
54041     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
54042     /**
54043      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
54044      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
54045      */
54046     /**
54047      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
54048      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'text'</tt> if
54049      * {@link #transform transforming a select} a select).
54050      * <p>See also <tt>{@link #valueField}</tt>.</p>
54051      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
54052      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
54053      * active.</p>
54054      */
54055     /**
54056      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
54057      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'value'</tt> if
54058      * {@link #transform transforming a select}).
54059      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
54060      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
54061      */
54062     /**
54063      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
54064      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
54065      * post during a form submission.  See also {@link #valueField}.
54066      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
54067      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
54068      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
54069      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
54070      */
54071     /**
54072      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
54073      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>
54074      * and combo {@link Ext.Component#id id} should be different, since no two DOM
54075      * nodes should share the same id.
54076      */
54077     /**
54078      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
54079      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
54080      * <tt>{@link Ext.form.Field#value value}</tt>.
54081      */
54082     /**
54083      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
54084      * applied the dropdown list element (defaults to '').
54085      */
54086     listClass : '',
54087     /**
54088      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
54089      * (defaults to <tt>'x-combo-selected'</tt>)
54090      */
54091     selectedClass : 'x-combo-selected',
54092     /**
54093      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
54094      * (defaults to '')
54095      */
54096     listEmptyText: '',
54097     /**
54098      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
54099      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
54100      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
54101      */
54102     triggerClass : 'x-form-arrow-trigger',
54103     /**
54104      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
54105      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
54106      */
54107     shadow : 'sides',
54108     /**
54109      * @cfg {String} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
54110      * on supported anchor positions (defaults to <tt>'tl-bl?'</tt>)
54111      */
54112     listAlign : 'tl-bl?',
54113     /**
54114      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
54115      * (defaults to <tt>300</tt>)
54116      */
54117     maxHeight : 300,
54118     /**
54119      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
54120      * distance to the viewport edges (defaults to <tt>90</tt>)
54121      */
54122     minHeight : 90,
54123     /**
54124      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
54125      * <div class="mdetail-params"><ul>
54126      * <li><b><tt>'query'</tt></b> : <b>Default</b>
54127      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
54128      * <li><b><tt>'all'</tt></b> :
54129      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
54130      * </ul></div>
54131      * <p>See also <code>{@link #queryParam}</code>.</p>
54132      */
54133     triggerAction : 'query',
54134     /**
54135      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
54136      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
54137      * <tt>{@link #mode} = 'local'</tt>, does not apply if
54138      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
54139      */
54140     minChars : 4,
54141     /**
54142      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
54143      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
54144      * to <tt>false</tt>)
54145      */
54146     typeAhead : false,
54147     /**
54148      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
54149      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
54150      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
54151      */
54152     queryDelay : 500,
54153     /**
54154      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
54155      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
54156      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
54157      * (defaults to <tt>0</tt>).
54158      */
54159     pageSize : 0,
54160     /**
54161      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
54162      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
54163      * <tt>false</tt>).
54164      */
54165     selectOnFocus : false,
54166     /**
54167      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
54168      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
54169      */
54170     queryParam : 'query',
54171     /**
54172      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
54173      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
54174      */
54175     loadingText : 'Loading...',
54176     /**
54177      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
54178      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
54179      * Defaults to <tt>false</tt>.
54180      */
54181     resizable : false,
54182     /**
54183      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
54184      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
54185      */
54186     handleHeight : 8,
54187     /**
54188      * @cfg {String} allQuery The text query to send to the server to return all records for the list
54189      * with no filtering (defaults to '')
54190      */
54191     allQuery: '',
54192     /**
54193      * @cfg {String} mode Acceptable values are:
54194      * <div class="mdetail-params"><ul>
54195      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
54196      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
54197      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
54198      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
54199      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
54200      * <li><b><tt>'local'</tt></b> :
54201      * <p class="sub-desc">ComboBox loads local data</p>
54202      * <pre><code>
54203 var combo = new Ext.form.ComboBox({
54204     renderTo: document.body,
54205     mode: 'local',
54206     store: new Ext.data.ArrayStore({
54207         id: 0,
54208         fields: [
54209             'myId',  // numeric value is the key
54210             'displayText'
54211         ],
54212         data: [[1, 'item1'], [2, 'item2']]  // data is local
54213     }),
54214     valueField: 'myId',
54215     displayField: 'displayText',
54216     triggerAction: 'all'
54217 });
54218      * </code></pre></li>
54219      * </ul></div>
54220      */
54221     mode: 'remote',
54222     /**
54223      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
54224      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
54225      */
54226     minListWidth : 70,
54227     /**
54228      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
54229      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
54230      */
54231     forceSelection : false,
54232     /**
54233      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
54234      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
54235      */
54236     typeAheadDelay : 250,
54237     /**
54238      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
54239      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
54240      * default text is used, it means there is no value set and no validation will occur on this field.
54241      */
54242
54243     /**
54244      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
54245      * (defaults to <tt>true</tt>)
54246      */
54247     lazyInit : true,
54248
54249     /**
54250      * The value of the match string used to filter the store. Delete this property to force a requery.
54251      * Example use:
54252      * <pre><code>
54253 var combo = new Ext.form.ComboBox({
54254     ...
54255     mode: 'remote',
54256     ...
54257     listeners: {
54258         // delete the previous query in the beforequery event or set
54259         // combo.lastQuery = null (this will reload the store the next time it expands)
54260         beforequery: function(qe){
54261             delete qe.combo.lastQuery;
54262         }
54263     }
54264 });
54265      * </code></pre>
54266      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
54267      * configure the combo with <tt>lastQuery=''</tt>. Example use:
54268      * <pre><code>
54269 var combo = new Ext.form.ComboBox({
54270     ...
54271     mode: 'local',
54272     triggerAction: 'all',
54273     lastQuery: ''
54274 });
54275      * </code></pre>
54276      * @property lastQuery
54277      * @type String
54278      */
54279
54280     // private
54281     initComponent : function(){
54282         Ext.form.ComboBox.superclass.initComponent.call(this);
54283         this.addEvents(
54284             /**
54285              * @event expand
54286              * Fires when the dropdown list is expanded
54287              * @param {Ext.form.ComboBox} combo This combo box
54288              */
54289             'expand',
54290             /**
54291              * @event collapse
54292              * Fires when the dropdown list is collapsed
54293              * @param {Ext.form.ComboBox} combo This combo box
54294              */
54295             'collapse',
54296             /**
54297              * @event beforeselect
54298              * Fires before a list item is selected. Return false to cancel the selection.
54299              * @param {Ext.form.ComboBox} combo This combo box
54300              * @param {Ext.data.Record} record The data record returned from the underlying store
54301              * @param {Number} index The index of the selected item in the dropdown list
54302              */
54303             'beforeselect',
54304             /**
54305              * @event select
54306              * Fires when a list item is selected
54307              * @param {Ext.form.ComboBox} combo This combo box
54308              * @param {Ext.data.Record} record The data record returned from the underlying store
54309              * @param {Number} index The index of the selected item in the dropdown list
54310              */
54311             'select',
54312             /**
54313              * @event beforequery
54314              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
54315              * cancel property to true.
54316              * @param {Object} queryEvent An object that has these properties:<ul>
54317              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
54318              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
54319              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
54320              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
54321              * </ul>
54322              */
54323             'beforequery'
54324         );
54325         if(this.transform){
54326             var s = Ext.getDom(this.transform);
54327             if(!this.hiddenName){
54328                 this.hiddenName = s.name;
54329             }
54330             if(!this.store){
54331                 this.mode = 'local';
54332                 var d = [], opts = s.options;
54333                 for(var i = 0, len = opts.length;i < len; i++){
54334                     var o = opts[i],
54335                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
54336                     if(o.selected && Ext.isEmpty(this.value, true)) {
54337                         this.value = value;
54338                     }
54339                     d.push([value, o.text]);
54340                 }
54341                 this.store = new Ext.data.ArrayStore({
54342                     'id': 0,
54343                     fields: ['value', 'text'],
54344                     data : d,
54345                     autoDestroy: true
54346                 });
54347                 this.valueField = 'value';
54348                 this.displayField = 'text';
54349             }
54350             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
54351             if(!this.lazyRender){
54352                 this.target = true;
54353                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
54354                 this.render(this.el.parentNode, s);
54355                 Ext.removeNode(s); // remove it
54356             }else{
54357                 Ext.removeNode(s); // remove it
54358             }
54359         }
54360         //auto-configure store from local array data
54361         else if(this.store){
54362             this.store = Ext.StoreMgr.lookup(this.store);
54363             if(this.store.autoCreated){
54364                 this.displayField = this.valueField = 'field1';
54365                 if(!this.store.expandData){
54366                     this.displayField = 'field2';
54367                 }
54368                 this.mode = 'local';
54369             }
54370         }
54371
54372         this.selectedIndex = -1;
54373         if(this.mode == 'local'){
54374             if(!Ext.isDefined(this.initialConfig.queryDelay)){
54375                 this.queryDelay = 10;
54376             }
54377             if(!Ext.isDefined(this.initialConfig.minChars)){
54378                 this.minChars = 0;
54379             }
54380         }
54381     },
54382
54383     // private
54384     onRender : function(ct, position){
54385         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
54386         if(this.hiddenName){
54387             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
54388                     id: (this.hiddenId||this.hiddenName)}, 'before', true);
54389
54390             // prevent input submission
54391             this.el.dom.removeAttribute('name');
54392         }
54393         if(Ext.isGecko){
54394             this.el.dom.setAttribute('autocomplete', 'off');
54395         }
54396
54397         if(!this.lazyInit){
54398             this.initList();
54399         }else{
54400             this.on('focus', this.initList, this, {single: true});
54401         }
54402     },
54403
54404     // private
54405     initValue : function(){
54406         Ext.form.ComboBox.superclass.initValue.call(this);
54407         if(this.hiddenField){
54408             this.hiddenField.value =
54409                 Ext.isDefined(this.hiddenValue) ? this.hiddenValue :
54410                 Ext.isDefined(this.value) ? this.value : '';
54411         }
54412     },
54413
54414     // private
54415     initList : function(){
54416         if(!this.list){
54417             var cls = 'x-combo-list';
54418
54419             this.list = new Ext.Layer({
54420                 parentEl: this.getListParent(),
54421                 shadow: this.shadow,
54422                 cls: [cls, this.listClass].join(' '),
54423                 constrain:false
54424             });
54425
54426             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
54427             this.list.setSize(lw, 0);
54428             this.list.swallowEvent('mousewheel');
54429             this.assetHeight = 0;
54430             if(this.syncFont !== false){
54431                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
54432             }
54433             if(this.title){
54434                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
54435                 this.assetHeight += this.header.getHeight();
54436             }
54437
54438             this.innerList = this.list.createChild({cls:cls+'-inner'});
54439             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
54440             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
54441             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
54442
54443             if(this.pageSize){
54444                 this.footer = this.list.createChild({cls:cls+'-ft'});
54445                 this.pageTb = new Ext.PagingToolbar({
54446                     store: this.store,
54447                     pageSize: this.pageSize,
54448                     renderTo:this.footer
54449                 });
54450                 this.assetHeight += this.footer.getHeight();
54451             }
54452
54453             if(!this.tpl){
54454                 /**
54455                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
54456                 * use to display each item in the dropdown list. The dropdown list is displayed in a
54457                 * DataView. See {@link #view}.</p>
54458                 * <p>The default template string is:</p><pre><code>
54459                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
54460                 * </code></pre>
54461                 * <p>Override the default value to create custom UI layouts for items in the list.
54462                 * For example:</p><pre><code>
54463                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
54464                 * </code></pre>
54465                 * <p>The template <b>must</b> contain one or more substitution parameters using field
54466                 * names from the Combo's</b> {@link #store Store}. In the example above an
54467                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
54468                 * <p>To preserve the default visual look of list items, add the CSS class name
54469                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
54470                 * <p>Also see {@link #itemSelector} for additional details.</p>
54471                 */
54472                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
54473                 /**
54474                  * @cfg {String} itemSelector
54475                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
54476                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
54477                  * display will be working with.</p>
54478                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
54479                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
54480                  * to dropdown list items</b>
54481                  */
54482             }
54483
54484             /**
54485             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
54486             * @type Ext.DataView
54487             */
54488             this.view = new Ext.DataView({
54489                 applyTo: this.innerList,
54490                 tpl: this.tpl,
54491                 singleSelect: true,
54492                 selectedClass: this.selectedClass,
54493                 itemSelector: this.itemSelector || '.' + cls + '-item',
54494                 emptyText: this.listEmptyText
54495             });
54496
54497             this.mon(this.view, 'click', this.onViewClick, this);
54498
54499             this.bindStore(this.store, true);
54500
54501             if(this.resizable){
54502                 this.resizer = new Ext.Resizable(this.list,  {
54503                    pinned:true, handles:'se'
54504                 });
54505                 this.mon(this.resizer, 'resize', function(r, w, h){
54506                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
54507                     this.listWidth = w;
54508                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
54509                     this.restrictHeight();
54510                 }, this);
54511
54512                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
54513             }
54514         }
54515     },
54516
54517     /**
54518      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
54519      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
54520      * to a different Element. An example might be rendering the list inside a Menu so that clicking
54521      * the list does not hide the Menu:<pre><code>
54522 var store = new Ext.data.ArrayStore({
54523     autoDestroy: true,
54524     fields: ['initials', 'fullname'],
54525     data : [
54526         ['FF', 'Fred Flintstone'],
54527         ['BR', 'Barney Rubble']
54528     ]
54529 });
54530
54531 var combo = new Ext.form.ComboBox({
54532     store: store,
54533     displayField: 'fullname',
54534     emptyText: 'Select a name...',
54535     forceSelection: true,
54536     getListParent: function() {
54537         return this.el.up('.x-menu');
54538     },
54539     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
54540     mode: 'local',
54541     selectOnFocus: true,
54542     triggerAction: 'all',
54543     typeAhead: true,
54544     width: 135
54545 });
54546
54547 var menu = new Ext.menu.Menu({
54548     id: 'mainMenu',
54549     items: [
54550         combo // A Field in a Menu
54551     ]
54552 });
54553 </code></pre>
54554      */
54555     getListParent : function() {
54556         return document.body;
54557     },
54558
54559     /**
54560      * Returns the store associated with this combo.
54561      * @return {Ext.data.Store} The store
54562      */
54563     getStore : function(){
54564         return this.store;
54565     },
54566
54567     // private
54568     bindStore : function(store, initial){
54569         if(this.store && !initial){
54570             this.store.un('beforeload', this.onBeforeLoad, this);
54571             this.store.un('load', this.onLoad, this);
54572             this.store.un('exception', this.collapse, this);
54573             if(this.store !== store && this.store.autoDestroy){
54574                 this.store.destroy();
54575             }
54576             if(!store){
54577                 this.store = null;
54578                 if(this.view){
54579                     this.view.bindStore(null);
54580                 }
54581             }
54582         }
54583         if(store){
54584             if(!initial) {
54585                 this.lastQuery = null;
54586                 if(this.pageTb) {
54587                     this.pageTb.bindStore(store);
54588                 }
54589             }
54590
54591             this.store = Ext.StoreMgr.lookup(store);
54592             this.store.on({
54593                 scope: this,
54594                 beforeload: this.onBeforeLoad,
54595                 load: this.onLoad,
54596                 exception: this.collapse
54597             });
54598
54599             if(this.view){
54600                 this.view.bindStore(store);
54601             }
54602         }
54603     },
54604
54605     // private
54606     initEvents : function(){
54607         Ext.form.ComboBox.superclass.initEvents.call(this);
54608
54609         this.keyNav = new Ext.KeyNav(this.el, {
54610             "up" : function(e){
54611                 this.inKeyMode = true;
54612                 this.selectPrev();
54613             },
54614
54615             "down" : function(e){
54616                 if(!this.isExpanded()){
54617                     this.onTriggerClick();
54618                 }else{
54619                     this.inKeyMode = true;
54620                     this.selectNext();
54621                 }
54622             },
54623
54624             "enter" : function(e){
54625                 this.onViewClick();
54626                 this.delayedCheck = true;
54627                 this.unsetDelayCheck.defer(10, this);
54628             },
54629
54630             "esc" : function(e){
54631                 this.collapse();
54632             },
54633
54634             "tab" : function(e){
54635                 this.onViewClick(false);
54636                 return true;
54637             },
54638
54639             scope : this,
54640
54641             doRelay : function(foo, bar, hname){
54642                 if(hname == 'down' || this.scope.isExpanded()){
54643                    return Ext.KeyNav.prototype.doRelay.apply(this, arguments);
54644                 }
54645                 return true;
54646             },
54647
54648             forceKeyDown : true
54649         });
54650         this.queryDelay = Math.max(this.queryDelay || 10,
54651                 this.mode == 'local' ? 10 : 250);
54652         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
54653         if(this.typeAhead){
54654             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
54655         }
54656         if(this.editable !== false && !this.enableKeyEvents){
54657             this.mon(this.el, 'keyup', this.onKeyUp, this);
54658         }
54659     },
54660
54661     // private
54662     onDestroy : function(){
54663         if (this.dqTask){
54664             this.dqTask.cancel();
54665             this.dqTask = null;
54666         }
54667         this.bindStore(null);
54668         Ext.destroy(
54669             this.resizer,
54670             this.view,
54671             this.pageTb,
54672             this.list
54673         );
54674         Ext.form.ComboBox.superclass.onDestroy.call(this);
54675     },
54676
54677     // private
54678     unsetDelayCheck : function(){
54679         delete this.delayedCheck;
54680     },
54681
54682     // private
54683     fireKey : function(e){
54684         var fn = function(ev){
54685             if (ev.isNavKeyPress() && !this.isExpanded() && !this.delayedCheck) {
54686                 this.fireEvent("specialkey", this, ev);
54687             }
54688         };
54689         //For some reason I can't track down, the events fire in a different order in webkit.
54690         //Need a slight delay here
54691         if(this.inEditor && Ext.isWebKit && e.getKey() == e.TAB){
54692             fn.defer(10, this, [new Ext.EventObjectImpl(e)]);
54693         }else{
54694             fn.call(this, e);
54695         }
54696     },
54697
54698     // private
54699     onResize : function(w, h){
54700         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
54701         if(this.list && !Ext.isDefined(this.listWidth)){
54702             var lw = Math.max(w, this.minListWidth);
54703             this.list.setWidth(lw);
54704             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
54705         }
54706     },
54707
54708     // private
54709     onEnable : function(){
54710         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
54711         if(this.hiddenField){
54712             this.hiddenField.disabled = false;
54713         }
54714     },
54715
54716     // private
54717     onDisable : function(){
54718         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
54719         if(this.hiddenField){
54720             this.hiddenField.disabled = true;
54721         }
54722     },
54723
54724     // private
54725     onBeforeLoad : function(){
54726         if(!this.hasFocus){
54727             return;
54728         }
54729         this.innerList.update(this.loadingText ?
54730                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
54731         this.restrictHeight();
54732         this.selectedIndex = -1;
54733     },
54734
54735     // private
54736     onLoad : function(){
54737         if(!this.hasFocus){
54738             return;
54739         }
54740         if(this.store.getCount() > 0){
54741             this.expand();
54742             this.restrictHeight();
54743             if(this.lastQuery == this.allQuery){
54744                 if(this.editable){
54745                     this.el.dom.select();
54746                 }
54747                 if(!this.selectByValue(this.value, true)){
54748                     this.select(0, true);
54749                 }
54750             }else{
54751                 this.selectNext();
54752                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
54753                     this.taTask.delay(this.typeAheadDelay);
54754                 }
54755             }
54756         }else{
54757             this.onEmptyResults();
54758         }
54759         //this.el.focus();
54760     },
54761
54762     // private
54763     onTypeAhead : function(){
54764         if(this.store.getCount() > 0){
54765             var r = this.store.getAt(0);
54766             var newValue = r.data[this.displayField];
54767             var len = newValue.length;
54768             var selStart = this.getRawValue().length;
54769             if(selStart != len){
54770                 this.setRawValue(newValue);
54771                 this.selectText(selStart, newValue.length);
54772             }
54773         }
54774     },
54775
54776     // private
54777     onSelect : function(record, index){
54778         if(this.fireEvent('beforeselect', this, record, index) !== false){
54779             this.setValue(record.data[this.valueField || this.displayField]);
54780             this.collapse();
54781             this.fireEvent('select', this, record, index);
54782         }
54783     },
54784
54785     // inherit docs
54786     getName: function(){
54787         var hf = this.hiddenField;
54788         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
54789     },
54790
54791     /**
54792      * Returns the currently selected field value or empty string if no value is set.
54793      * @return {String} value The selected value
54794      */
54795     getValue : function(){
54796         if(this.valueField){
54797             return Ext.isDefined(this.value) ? this.value : '';
54798         }else{
54799             return Ext.form.ComboBox.superclass.getValue.call(this);
54800         }
54801     },
54802
54803     /**
54804      * Clears any text/value currently set in the field
54805      */
54806     clearValue : function(){
54807         if(this.hiddenField){
54808             this.hiddenField.value = '';
54809         }
54810         this.setRawValue('');
54811         this.lastSelectionText = '';
54812         this.applyEmptyText();
54813         this.value = '';
54814     },
54815
54816     /**
54817      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54818      * will be displayed in the field.  If the value does not match the data value of an existing item,
54819      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54820      * Otherwise the field will be blank (although the value will still be set).
54821      * @param {String} value The value to match
54822      * @return {Ext.form.Field} this
54823      */
54824     setValue : function(v){
54825         var text = v;
54826         if(this.valueField){
54827             var r = this.findRecord(this.valueField, v);
54828             if(r){
54829                 text = r.data[this.displayField];
54830             }else if(Ext.isDefined(this.valueNotFoundText)){
54831                 text = this.valueNotFoundText;
54832             }
54833         }
54834         this.lastSelectionText = text;
54835         if(this.hiddenField){
54836             this.hiddenField.value = v;
54837         }
54838         Ext.form.ComboBox.superclass.setValue.call(this, text);
54839         this.value = v;
54840         return this;
54841     },
54842
54843     // private
54844     findRecord : function(prop, value){
54845         var record;
54846         if(this.store.getCount() > 0){
54847             this.store.each(function(r){
54848                 if(r.data[prop] == value){
54849                     record = r;
54850                     return false;
54851                 }
54852             });
54853         }
54854         return record;
54855     },
54856
54857     // private
54858     onViewMove : function(e, t){
54859         this.inKeyMode = false;
54860     },
54861
54862     // private
54863     onViewOver : function(e, t){
54864         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
54865             return;
54866         }
54867         var item = this.view.findItemFromChild(t);
54868         if(item){
54869             var index = this.view.indexOf(item);
54870             this.select(index, false);
54871         }
54872     },
54873
54874     // private
54875     onViewClick : function(doFocus){
54876         var index = this.view.getSelectedIndexes()[0];
54877         var r = this.store.getAt(index);
54878         if(r){
54879             this.onSelect(r, index);
54880         }
54881         if(doFocus !== false){
54882             this.el.focus();
54883         }
54884     },
54885
54886     // private
54887     restrictHeight : function(){
54888         this.innerList.dom.style.height = '';
54889         var inner = this.innerList.dom;
54890         var pad = this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight;
54891         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
54892         var ha = this.getPosition()[1]-Ext.getBody().getScroll().top;
54893         var hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height;
54894         var space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
54895         h = Math.min(h, space, this.maxHeight);
54896
54897         this.innerList.setHeight(h);
54898         this.list.beginUpdate();
54899         this.list.setHeight(h+pad);
54900         this.list.alignTo(this.wrap, this.listAlign);
54901         this.list.endUpdate();
54902     },
54903
54904     // private
54905     onEmptyResults : function(){
54906         this.collapse();
54907     },
54908
54909     /**
54910      * Returns true if the dropdown list is expanded, else false.
54911      */
54912     isExpanded : function(){
54913         return this.list && this.list.isVisible();
54914     },
54915
54916     /**
54917      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54918      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54919      * @param {String} value The data value of the item to select
54920      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54921      * selected item if it is not currently in view (defaults to true)
54922      * @return {Boolean} True if the value matched an item in the list, else false
54923      */
54924     selectByValue : function(v, scrollIntoView){
54925         if(!Ext.isEmpty(v, true)){
54926             var r = this.findRecord(this.valueField || this.displayField, v);
54927             if(r){
54928                 this.select(this.store.indexOf(r), scrollIntoView);
54929                 return true;
54930             }
54931         }
54932         return false;
54933     },
54934
54935     /**
54936      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54937      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54938      * @param {Number} index The zero-based index of the list item to select
54939      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54940      * selected item if it is not currently in view (defaults to true)
54941      */
54942     select : function(index, scrollIntoView){
54943         this.selectedIndex = index;
54944         this.view.select(index);
54945         if(scrollIntoView !== false){
54946             var el = this.view.getNode(index);
54947             if(el){
54948                 this.innerList.scrollChildIntoView(el, false);
54949             }
54950         }
54951     },
54952
54953     // private
54954     selectNext : function(){
54955         var ct = this.store.getCount();
54956         if(ct > 0){
54957             if(this.selectedIndex == -1){
54958                 this.select(0);
54959             }else if(this.selectedIndex < ct-1){
54960                 this.select(this.selectedIndex+1);
54961             }
54962         }
54963     },
54964
54965     // private
54966     selectPrev : function(){
54967         var ct = this.store.getCount();
54968         if(ct > 0){
54969             if(this.selectedIndex == -1){
54970                 this.select(0);
54971             }else if(this.selectedIndex !== 0){
54972                 this.select(this.selectedIndex-1);
54973             }
54974         }
54975     },
54976
54977     // private
54978     onKeyUp : function(e){
54979         var k = e.getKey();
54980         if(this.editable !== false && (k == e.BACKSPACE || !e.isSpecialKey())){
54981             this.lastKey = k;
54982             this.dqTask.delay(this.queryDelay);
54983         }
54984         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
54985     },
54986
54987     // private
54988     validateBlur : function(){
54989         return !this.list || !this.list.isVisible();
54990     },
54991
54992     // private
54993     initQuery : function(){
54994         this.doQuery(this.getRawValue());
54995     },
54996
54997     // private
54998     beforeBlur : function(){
54999         var val = this.getRawValue();
55000         if(this.forceSelection){
55001             if(val.length > 0 && val != this.emptyText){
55002                this.el.dom.value = Ext.isDefined(this.lastSelectionText) ? this.lastSelectionText : '';
55003                 this.applyEmptyText();
55004             }else{
55005                 this.clearValue();
55006             }
55007         }else{
55008             var rec = this.findRecord(this.displayField, val);
55009             if(rec){
55010                 val = rec.get(this.valueField || this.displayField);
55011             }
55012             this.setValue(val);
55013         }
55014     },
55015
55016     /**
55017      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
55018      * query allowing the query action to be canceled if needed.
55019      * @param {String} query The SQL query to execute
55020      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
55021      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
55022      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
55023      */
55024     doQuery : function(q, forceAll){
55025         q = Ext.isEmpty(q) ? '' : q;
55026         var qe = {
55027             query: q,
55028             forceAll: forceAll,
55029             combo: this,
55030             cancel:false
55031         };
55032         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
55033             return false;
55034         }
55035         q = qe.query;
55036         forceAll = qe.forceAll;
55037         if(forceAll === true || (q.length >= this.minChars)){
55038             if(this.lastQuery !== q){
55039                 this.lastQuery = q;
55040                 if(this.mode == 'local'){
55041                     this.selectedIndex = -1;
55042                     if(forceAll){
55043                         this.store.clearFilter();
55044                     }else{
55045                         this.store.filter(this.displayField, q);
55046                     }
55047                     this.onLoad();
55048                 }else{
55049                     this.store.baseParams[this.queryParam] = q;
55050                     this.store.load({
55051                         params: this.getParams(q)
55052                     });
55053                     this.expand();
55054                 }
55055             }else{
55056                 this.selectedIndex = -1;
55057                 this.onLoad();
55058             }
55059         }
55060     },
55061
55062     // private
55063     getParams : function(q){
55064         var p = {};
55065         //p[this.queryParam] = q;
55066         if(this.pageSize){
55067             p.start = 0;
55068             p.limit = this.pageSize;
55069         }
55070         return p;
55071     },
55072
55073     /**
55074      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
55075      */
55076     collapse : function(){
55077         if(!this.isExpanded()){
55078             return;
55079         }
55080         this.list.hide();
55081         Ext.getDoc().un('mousewheel', this.collapseIf, this);
55082         Ext.getDoc().un('mousedown', this.collapseIf, this);
55083         this.fireEvent('collapse', this);
55084     },
55085
55086     // private
55087     collapseIf : function(e){
55088         if(!e.within(this.wrap) && !e.within(this.list)){
55089             this.collapse();
55090         }
55091     },
55092
55093     /**
55094      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
55095      */
55096     expand : function(){
55097         if(this.isExpanded() || !this.hasFocus){
55098             return;
55099         }
55100         this.list.alignTo(this.wrap, this.listAlign);
55101         this.list.show();
55102         if(Ext.isGecko2){
55103             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
55104         }
55105         Ext.getDoc().on({
55106             scope: this,
55107             mousewheel: this.collapseIf,
55108             mousedown: this.collapseIf
55109         });
55110         this.fireEvent('expand', this);
55111     },
55112
55113     /**
55114      * @method onTriggerClick
55115      * @hide
55116      */
55117     // private
55118     // Implements the default empty TriggerField.onTriggerClick function
55119     onTriggerClick : function(){
55120         if(this.disabled){
55121             return;
55122         }
55123         if(this.isExpanded()){
55124             this.collapse();
55125             this.el.focus();
55126         }else {
55127             this.onFocus({});
55128             if(this.triggerAction == 'all') {
55129                 this.doQuery(this.allQuery, true);
55130             } else {
55131                 this.doQuery(this.getRawValue());
55132             }
55133             this.el.focus();
55134         }
55135     }
55136
55137     /**
55138      * @hide
55139      * @method autoSize
55140      */
55141     /**
55142      * @cfg {Boolean} grow @hide
55143      */
55144     /**
55145      * @cfg {Number} growMin @hide
55146      */
55147     /**
55148      * @cfg {Number} growMax @hide
55149      */
55150
55151 });
55152 Ext.reg('combo', Ext.form.ComboBox);/**
55153  * @class Ext.form.Checkbox
55154  * @extends Ext.form.Field
55155  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
55156  * @constructor
55157  * Creates a new Checkbox
55158  * @param {Object} config Configuration options
55159  * @xtype checkbox
55160  */
55161 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
55162     /**
55163      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
55164      */
55165     focusClass : undefined,
55166     /**
55167      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
55168      */
55169     fieldClass : 'x-form-field',
55170     /**
55171      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
55172      */
55173     checked : false,
55174     /**
55175      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
55176      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
55177      */
55178     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
55179     /**
55180      * @cfg {String} boxLabel The text that appears beside the checkbox
55181      */
55182     /**
55183      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
55184      */
55185     /**
55186      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of 
55187      * handling the check event). The handler is passed the following parameters:
55188      * <div class="mdetail-params"><ul>
55189      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
55190      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
55191      * </ul></div>
55192      */
55193     /**
55194      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
55195      * (defaults to this Checkbox).
55196      */
55197
55198     // private
55199     actionMode : 'wrap',
55200     
55201         // private
55202     initComponent : function(){
55203         Ext.form.Checkbox.superclass.initComponent.call(this);
55204         this.addEvents(
55205             /**
55206              * @event check
55207              * Fires when the checkbox is checked or unchecked.
55208              * @param {Ext.form.Checkbox} this This checkbox
55209              * @param {Boolean} checked The new checked value
55210              */
55211             'check'
55212         );
55213     },
55214
55215     // private
55216     onResize : function(){
55217         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
55218         if(!this.boxLabel && !this.fieldLabel){
55219             this.el.alignTo(this.wrap, 'c-c');
55220         }
55221     },
55222
55223     // private
55224     initEvents : function(){
55225         Ext.form.Checkbox.superclass.initEvents.call(this);
55226         this.mon(this.el, 'click', this.onClick, this);
55227         this.mon(this.el, 'change', this.onClick, this);
55228     },
55229
55230         // private
55231     getResizeEl : function(){
55232         return this.wrap;
55233     },
55234
55235     // private
55236     getPositionEl : function(){
55237         return this.wrap;
55238     },
55239
55240     /**
55241      * @hide
55242      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
55243      * @method
55244      */
55245     markInvalid : Ext.emptyFn,
55246     /**
55247      * @hide
55248      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
55249      * @method
55250      */
55251     clearInvalid : Ext.emptyFn,
55252
55253     // private
55254     onRender : function(ct, position){
55255         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
55256         if(this.inputValue !== undefined){
55257             this.el.dom.value = this.inputValue;
55258         }
55259         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
55260         if(this.boxLabel){
55261             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
55262         }
55263         if(this.checked){
55264             this.setValue(true);
55265         }else{
55266             this.checked = this.el.dom.checked;
55267         }
55268     },
55269
55270     // private
55271     onDestroy : function(){
55272         Ext.destroy(this.wrap);
55273         Ext.form.Checkbox.superclass.onDestroy.call(this);
55274     },
55275
55276     // private
55277     initValue : function() {
55278         this.originalValue = this.getValue();
55279     },
55280
55281     /**
55282      * Returns the checked state of the checkbox.
55283      * @return {Boolean} True if checked, else false
55284      */
55285     getValue : function(){
55286         if(this.rendered){
55287             return this.el.dom.checked;
55288         }
55289         return false;
55290     },
55291
55292         // private
55293     onClick : function(){
55294         if(this.el.dom.checked != this.checked){
55295             this.setValue(this.el.dom.checked);
55296         }
55297     },
55298
55299     /**
55300      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
55301      * <code>{@link #handler}</code> (if configured).
55302      * @param {Boolean/String} checked The following values will check the checkbox:
55303      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
55304      * @return {Ext.form.Field} this
55305      */
55306     setValue : function(v){
55307         var checked = this.checked ;
55308         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
55309         if(this.rendered){
55310             this.el.dom.checked = this.checked;
55311             this.el.dom.defaultChecked = this.checked;
55312         }
55313         if(checked != this.checked){
55314             this.fireEvent('check', this, this.checked);
55315             if(this.handler){
55316                 this.handler.call(this.scope || this, this, this.checked);
55317             }
55318         }
55319         return this;
55320     }
55321 });
55322 Ext.reg('checkbox', Ext.form.Checkbox);
55323 /**
55324  * @class Ext.form.CheckboxGroup
55325  * @extends Ext.form.Field
55326  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
55327  * <p>Sample usage:</p>
55328  * <pre><code>
55329 var myCheckboxGroup = new Ext.form.CheckboxGroup({
55330     id:'myGroup',
55331     xtype: 'checkboxgroup',
55332     fieldLabel: 'Single Column',
55333     itemCls: 'x-check-group-alt',
55334     // Put all controls in a single column with width 100%
55335     columns: 1,
55336     items: [
55337         {boxLabel: 'Item 1', name: 'cb-col-1'},
55338         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
55339         {boxLabel: 'Item 3', name: 'cb-col-3'}
55340     ]
55341 });
55342  * </code></pre>
55343  * @constructor
55344  * Creates a new CheckboxGroup
55345  * @param {Object} config Configuration options
55346  * @xtype checkboxgroup
55347  */
55348 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
55349     /**
55350      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
55351      * to arrange in the group.
55352      */
55353     /**
55354      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
55355      * checkbox/radio controls using automatic layout.  This config can take several types of values:
55356      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
55357      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
55358      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be 
55359      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
55360      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
55361      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
55362      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
55363      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
55364      * container you should do so.</p></li></ul>
55365      */
55366     columns : 'auto',
55367     /**
55368      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column 
55369      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
55370      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
55371      * to columns one at a time, completely filling each row left to right before starting on the next row.
55372      */
55373     vertical : false,
55374     /**
55375      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
55376      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
55377      */
55378     allowBlank : true,
55379     /**
55380      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must 
55381      * select at least one item in this group")
55382      */
55383     blankText : "You must select at least one item in this group",
55384     
55385     // private
55386     defaultType : 'checkbox',
55387     
55388     // private
55389     groupCls : 'x-form-check-group',
55390     
55391     // private
55392     initComponent: function(){
55393         this.addEvents(
55394             /**
55395              * @event change
55396              * Fires when the state of a child checkbox changes.
55397              * @param {Ext.form.CheckboxGroup} this
55398              * @param {Array} checked An array containing the checked boxes.
55399              */
55400             'change'
55401         );   
55402         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
55403     },
55404     
55405     // private
55406     onRender : function(ct, position){
55407         if(!this.el){
55408             var panelCfg = {
55409                 cls: this.groupCls,
55410                 layout: 'column',
55411                 border: false,
55412                 renderTo: ct
55413             };
55414             var colCfg = {
55415                 defaultType: this.defaultType,
55416                 layout: 'form',
55417                 border: false,
55418                 defaults: {
55419                     hideLabel: true,
55420                     anchor: '100%'
55421                 }
55422             };
55423             
55424             if(this.items[0].items){
55425                 
55426                 // The container has standard ColumnLayout configs, so pass them in directly
55427                 
55428                 Ext.apply(panelCfg, {
55429                     layoutConfig: {columns: this.items.length},
55430                     defaults: this.defaults,
55431                     items: this.items
55432                 });
55433                 for(var i=0, len=this.items.length; i<len; i++){
55434                     Ext.applyIf(this.items[i], colCfg);
55435                 }
55436                 
55437             }else{
55438                 
55439                 // The container has field item configs, so we have to generate the column
55440                 // panels first then move the items into the columns as needed.
55441                 
55442                 var numCols, cols = [];
55443                 
55444                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
55445                     this.columns = this.items.length;
55446                 }
55447                 if(!Ext.isArray(this.columns)){
55448                     var cs = [];
55449                     for(var i=0; i<this.columns; i++){
55450                         cs.push((100/this.columns)*.01); // distribute by even %
55451                     }
55452                     this.columns = cs;
55453                 }
55454                 
55455                 numCols = this.columns.length;
55456                 
55457                 // Generate the column configs with the correct width setting
55458                 for(var i=0; i<numCols; i++){
55459                     var cc = Ext.apply({items:[]}, colCfg);
55460                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
55461                     if(this.defaults){
55462                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)
55463                     }
55464                     cols.push(cc);
55465                 };
55466                 
55467                 // Distribute the original items into the columns
55468                 if(this.vertical){
55469                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
55470                     for(var i=0, len=this.items.length; i<len; i++){
55471                         if(i>0 && i%rows==0){
55472                             ri++;
55473                         }
55474                         if(this.items[i].fieldLabel){
55475                             this.items[i].hideLabel = false;
55476                         }
55477                         cols[ri].items.push(this.items[i]);
55478                     };
55479                 }else{
55480                     for(var i=0, len=this.items.length; i<len; i++){
55481                         var ci = i % numCols;
55482                         if(this.items[i].fieldLabel){
55483                             this.items[i].hideLabel = false;
55484                         }
55485                         cols[ci].items.push(this.items[i]);
55486                     };
55487                 }
55488                 
55489                 Ext.apply(panelCfg, {
55490                     layoutConfig: {columns: numCols},
55491                     items: cols
55492                 });
55493             }
55494             
55495             this.panel = new Ext.Panel(panelCfg);
55496             this.panel.ownerCt = this;
55497             this.el = this.panel.getEl();
55498             
55499             if(this.forId && this.itemCls){
55500                 var l = this.el.up(this.itemCls).child('label', true);
55501                 if(l){
55502                     l.setAttribute('htmlFor', this.forId);
55503                 }
55504             }
55505             
55506             var fields = this.panel.findBy(function(c){
55507                 return c.isFormField;
55508             }, this);
55509             
55510             this.items = new Ext.util.MixedCollection();
55511             this.items.addAll(fields);
55512         }
55513         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
55514     },
55515     
55516     afterRender : function(){
55517         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
55518         if(this.values){
55519             this.setValue.apply(this, this.values);
55520             delete this.values;
55521         }
55522         this.eachItem(function(item){
55523             item.on('check', this.fireChecked, this);
55524             item.inGroup = true;
55525         });
55526     },
55527     
55528     // private
55529     doLayout: function(){
55530         //ugly method required to layout hidden items
55531         if(this.rendered){
55532             this.panel.forceLayout = this.ownerCt.forceLayout;
55533             this.panel.doLayout();
55534         }
55535     },
55536     
55537     // private
55538     fireChecked: function(){
55539         var arr = [];
55540         this.eachItem(function(item){
55541             if(item.checked){
55542                 arr.push(item);
55543             }
55544         });
55545         this.fireEvent('change', this, arr);
55546     },
55547     
55548     // private
55549     validateValue : function(value){
55550         if(!this.allowBlank){
55551             var blank = true;
55552             this.eachItem(function(f){
55553                 if(f.checked){
55554                     return (blank = false);
55555                 }
55556             });
55557             if(blank){
55558                 this.markInvalid(this.blankText);
55559                 return false;
55560             }
55561         }
55562         return true;
55563     },
55564     
55565     // private
55566     onDisable : function(){
55567         this.eachItem(function(item){
55568             item.disable();
55569         });
55570     },
55571
55572     // private
55573     onEnable : function(){
55574         this.eachItem(function(item){
55575             item.enable();
55576         });
55577     },
55578     
55579     // private
55580     doLayout: function(){
55581         if(this.rendered){
55582             this.panel.forceLayout = this.ownerCt.forceLayout;
55583             this.panel.doLayout();
55584         }
55585     },
55586     
55587     // private
55588     onResize : function(w, h){
55589         this.panel.setSize(w, h);
55590         this.panel.doLayout();
55591     },
55592     
55593     // inherit docs from Field
55594     reset : function(){
55595         Ext.form.CheckboxGroup.superclass.reset.call(this);
55596         this.eachItem(function(c){
55597             if(c.reset){
55598                 c.reset();
55599             }
55600         });
55601     },
55602     
55603     /**
55604      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
55605      * in the group. Examples illustrating how this method may be called:
55606      * <pre><code>
55607 // call with name and value
55608 myCheckboxGroup.setValue('cb-col-1', true);
55609 // call with an array of boolean values 
55610 myCheckboxGroup.setValue([true, false, false]);
55611 // call with an object literal specifying item:value pairs
55612 myCheckboxGroup.setValue({
55613     'cb-col-2': false,
55614     'cb-col-3': true
55615 });
55616 // use comma separated string to set items with name to true (checked)
55617 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
55618      * </code></pre>
55619      * See {@link Ext.form.Checkbox#setValue} for additional information.
55620      * @param {Mixed} id The checkbox to check, or as described by example shown.
55621      * @param {Boolean} value (optional) The value to set the item.
55622      * @return {Ext.form.CheckboxGroup} this
55623      */
55624     setValue : function(id, value){
55625         if(this.rendered){
55626             if(arguments.length == 1){
55627                 if(Ext.isArray(id)){
55628                     //an array of boolean values
55629                     Ext.each(id, function(val, idx){
55630                         var item = this.items.itemAt(idx);
55631                         if(item){
55632                             item.setValue(val);
55633                         }
55634                     }, this);
55635                 }else if(Ext.isObject(id)){
55636                     //set of name/value pairs
55637                     for(var i in id){
55638                         var f = this.getBox(i);
55639                         if(f){
55640                             f.setValue(id[i]);
55641                         }
55642                     }
55643                 }else{
55644                     this.setValueForItem(id);
55645                 }
55646             }else{
55647                 var f = this.getBox(id);
55648                 if(f){
55649                     f.setValue(value);
55650                 }
55651             }
55652         }else{
55653             this.values = arguments;
55654         }
55655         return this;
55656     },
55657     
55658     // private
55659     onDestroy: function(){
55660         Ext.destroy(this.panel);
55661         Ext.form.CheckboxGroup.superclass.onDestroy.call(this);
55662
55663     },
55664     
55665     setValueForItem : function(val){
55666         val = String(val).split(',');
55667         this.eachItem(function(item){
55668             if(val.indexOf(item.inputValue)> -1){
55669                 item.setValue(true);
55670             }
55671         });
55672     },
55673     
55674     // private
55675     getBox : function(id){
55676         var box = null;
55677         this.eachItem(function(f){
55678             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
55679                 box = f;
55680                 return false;
55681             }
55682         });
55683         return box;
55684     },
55685     
55686     /**
55687      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
55688      * @return {Array} An array of the selected checkboxes.
55689      */
55690     getValue : function(){
55691         var out = [];
55692         this.eachItem(function(item){
55693             if(item.checked){
55694                 out.push(item);
55695             }
55696         });
55697         return out;
55698     },
55699     
55700     // private
55701     eachItem: function(fn){
55702         if(this.items && this.items.each){
55703             this.items.each(fn, this);
55704         }
55705     },
55706     
55707     /**
55708      * @cfg {String} name
55709      * @hide
55710      */
55711     /**
55712      * @method initValue
55713      * @hide
55714      */
55715     initValue : Ext.emptyFn,
55716     /**
55717      * @method getValue
55718      * @hide
55719      */
55720     getValue : Ext.emptyFn,
55721     /**
55722      * @method getRawValue
55723      * @hide
55724      */
55725     getRawValue : Ext.emptyFn,
55726     
55727     /**
55728      * @method setRawValue
55729      * @hide
55730      */
55731     setRawValue : Ext.emptyFn
55732     
55733 });
55734
55735 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
55736 /**
55737  * @class Ext.form.Radio
55738  * @extends Ext.form.Checkbox
55739  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
55740  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
55741  * @constructor
55742  * Creates a new Radio
55743  * @param {Object} config Configuration options
55744  * @xtype radio
55745  */
55746 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
55747     inputType: 'radio',
55748
55749     /**
55750      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
55751      * @method
55752      */
55753     markInvalid : Ext.emptyFn,
55754     /**
55755      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
55756      * @method
55757      */
55758     clearInvalid : Ext.emptyFn,
55759
55760     /**
55761      * If this radio is part of a group, it will return the selected value
55762      * @return {String}
55763      */
55764     getGroupValue : function(){
55765         var p = this.el.up('form') || Ext.getBody();
55766         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
55767         return c ? c.value : null;
55768     },
55769
55770     // private
55771     onClick : function(){
55772         if(this.el.dom.checked != this.checked){
55773                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
55774                         els.each(function(el){
55775                                 if(el.dom.id == this.id){
55776                                         this.setValue(true);
55777                                 }else{
55778                                         Ext.getCmp(el.dom.id).setValue(false);
55779                                 }
55780                         }, this);
55781                 }
55782     },
55783
55784     /**
55785      * Sets either the checked/unchecked status of this Radio, or, if a string value
55786      * is passed, checks a sibling Radio of the same name whose value is the value specified.
55787      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
55788      * @return {Ext.form.Field} this
55789      */
55790     setValue : function(v){
55791         if (typeof v == 'boolean') {
55792             Ext.form.Radio.superclass.setValue.call(this, v);
55793         } else {
55794             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
55795             if(r){
55796                 Ext.getCmp(r.id).setValue(true);
55797             }
55798         }
55799         return this;
55800     },
55801     
55802     // private
55803     getCheckEl: function(){
55804         if(this.inGroup){
55805             return this.el.up('.x-form-radio-group')
55806         }
55807         return this.el.up('form') || Ext.getBody();
55808     }
55809 });
55810 Ext.reg('radio', Ext.form.Radio);
55811 /**
55812  * @class Ext.form.RadioGroup
55813  * @extends Ext.form.CheckboxGroup
55814  * A grouping container for {@link Ext.form.Radio} controls.
55815  * @constructor
55816  * Creates a new RadioGroup
55817  * @param {Object} config Configuration options
55818  * @xtype radiogroup
55819  */
55820 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
55821     /**
55822      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
55823      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
55824      * be used as the error text.
55825      */
55826     allowBlank : true,
55827     /**
55828      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
55829      * (defaults to 'You must select one item in this group')
55830      */
55831     blankText : 'You must select one item in this group',
55832     
55833     // private
55834     defaultType : 'radio',
55835     
55836     // private
55837     groupCls : 'x-form-radio-group',
55838     
55839     /**
55840      * @event change
55841      * Fires when the state of a child radio changes.
55842      * @param {Ext.form.RadioGroup} this
55843      * @param {Ext.form.Radio} checked The checked radio
55844      */
55845     
55846     /**
55847      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
55848      * @return {Ext.form.Radio} The selected radio.
55849      */
55850     getValue : function(){
55851         var out = null;
55852         this.eachItem(function(item){
55853             if(item.checked){
55854                 out = item;
55855                 return false;
55856             }
55857         });
55858         return out;
55859     },
55860     
55861     /**
55862      * Sets the checked radio in the group.
55863      * @param {String/Ext.form.Radio} id The radio to check.
55864      * @param {Boolean} value The value to set the radio.
55865      * @return {Ext.form.RadioGroup} this
55866      */
55867     setValue : function(id, value){
55868         if(this.rendered){
55869             if(arguments.length > 1){
55870                 var f = this.getBox(id);
55871                 if(f){
55872                     f.setValue(value);
55873                     if(f.checked){
55874                         this.eachItem(function(item){
55875                             if (item !== f){
55876                                 item.setValue(false);
55877                             }
55878                         });
55879                     }
55880                 }
55881             }else{
55882                 this.setValueForItem(id);
55883             }
55884         }else{
55885             this.values = arguments;
55886         }
55887         return this;
55888     },
55889     
55890     // private
55891     fireChecked : function(){
55892         if(!this.checkTask){
55893             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
55894         }
55895         this.checkTask.delay(10);
55896     },
55897     
55898     // private
55899     bufferChecked : function(){
55900         var out = null;
55901         this.eachItem(function(item){
55902             if(item.checked){
55903                 out = item;
55904                 return false;
55905             }
55906         });
55907         this.fireEvent('change', this, out);
55908     },
55909     
55910     onDestroy : function(){
55911         if(this.checkTask){
55912             this.checkTask.cancel();
55913             this.checkTask = null;
55914         }
55915         Ext.form.RadioGroup.superclass.onDestroy.call(this);
55916     }
55917
55918 });
55919
55920 Ext.reg('radiogroup', Ext.form.RadioGroup);
55921 /**\r
55922  * @class Ext.form.Hidden\r
55923  * @extends Ext.form.Field\r
55924  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.\r
55925  * @constructor\r
55926  * Create a new Hidden field.\r
55927  * @param {Object} config Configuration options\r
55928  * @xtype hidden\r
55929  */\r
55930 Ext.form.Hidden = Ext.extend(Ext.form.Field, {\r
55931     // private\r
55932     inputType : 'hidden',\r
55933 \r
55934     // private\r
55935     onRender : function(){\r
55936         Ext.form.Hidden.superclass.onRender.apply(this, arguments);\r
55937     },\r
55938 \r
55939     // private\r
55940     initEvents : function(){\r
55941         this.originalValue = this.getValue();\r
55942     },\r
55943 \r
55944     // These are all private overrides\r
55945     setSize : Ext.emptyFn,\r
55946     setWidth : Ext.emptyFn,\r
55947     setHeight : Ext.emptyFn,\r
55948     setPosition : Ext.emptyFn,\r
55949     setPagePosition : Ext.emptyFn,\r
55950     markInvalid : Ext.emptyFn,\r
55951     clearInvalid : Ext.emptyFn\r
55952 });\r
55953 Ext.reg('hidden', Ext.form.Hidden);/**
55954  * @class Ext.form.BasicForm
55955  * @extends Ext.util.Observable
55956  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
55957  * input field management, validation, submission, and form loading services.</p>
55958  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
55959  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
55960  * <p><b><u>File Uploads</u></b></p>
55961  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
55962  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
55963  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
55964  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
55965  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
55966  * but removed after the return data has been gathered.</p>
55967  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
55968  * server is using JSON to send the return object, then the
55969  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
55970  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
55971  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
55972  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
55973  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
55974  * is created containing a <tt>responseText</tt> property in order to conform to the
55975  * requirements of event handlers and callbacks.</p>
55976  * <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>
55977  * and some server technologies (notably JEE) may require some custom processing in order to
55978  * retrieve parameter names and parameter values from the packet content.</p>
55979  * @constructor
55980  * @param {Mixed} el The form element or its id
55981  * @param {Object} config Configuration options
55982  */
55983 Ext.form.BasicForm = function(el, config){
55984     Ext.apply(this, config);
55985     if(Ext.isString(this.paramOrder)){
55986         this.paramOrder = this.paramOrder.split(/[\s,|]/);
55987     }
55988     /*
55989      * @property items
55990      * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form.
55991      * @type MixedCollection
55992      */
55993     this.items = new Ext.util.MixedCollection(false, function(o){
55994         return o.itemId || o.id || (o.id = Ext.id());
55995     });
55996     this.addEvents(
55997         /**
55998          * @event beforeaction
55999          * Fires before any action is performed. Return false to cancel the action.
56000          * @param {Form} this
56001          * @param {Action} action The {@link Ext.form.Action} to be performed
56002          */
56003         'beforeaction',
56004         /**
56005          * @event actionfailed
56006          * Fires when an action fails.
56007          * @param {Form} this
56008          * @param {Action} action The {@link Ext.form.Action} that failed
56009          */
56010         'actionfailed',
56011         /**
56012          * @event actioncomplete
56013          * Fires when an action is completed.
56014          * @param {Form} this
56015          * @param {Action} action The {@link Ext.form.Action} that completed
56016          */
56017         'actioncomplete'
56018     );
56019
56020     if(el){
56021         this.initEl(el);
56022     }
56023     Ext.form.BasicForm.superclass.constructor.call(this);
56024 };
56025
56026 Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
56027     /**
56028      * @cfg {String} method
56029      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
56030      */
56031     /**
56032      * @cfg {DataReader} reader
56033      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
56034      * data when executing 'load' actions. This is optional as there is built-in
56035      * support for processing JSON.  For additional information on using an XMLReader
56036      * see the example provided in examples/form/xml-form.html.
56037      */
56038     /**
56039      * @cfg {DataReader} errorReader
56040      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
56041      * read field error messages returned from 'submit' actions. This is optional
56042      * as there is built-in support for processing JSON.</p>
56043      * <p>The Records which provide messages for the invalid Fields must use the
56044      * Field name (or id) as the Record ID, and must contain a field called 'msg'
56045      * which contains the error message.</p>
56046      * <p>The errorReader does not have to be a full-blown implementation of a
56047      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
56048      * which returns an Array of Records in an object with the following
56049      * structure:</p><pre><code>
56050 {
56051     records: recordArray
56052 }
56053 </code></pre>
56054      */
56055     /**
56056      * @cfg {String} url
56057      * The URL to use for form actions if one isn't supplied in the
56058      * <code>{@link #doAction doAction} options</code>.
56059      */
56060     /**
56061      * @cfg {Boolean} fileUpload
56062      * Set to true if this form is a file upload.
56063      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
56064      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
56065      * DOM <tt>&lt;form></tt> element temporarily modified to have its
56066      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
56067      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
56068      * but removed after the return data has been gathered.</p>
56069      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
56070      * server is using JSON to send the return object, then the
56071      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
56072      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
56073      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
56074      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
56075      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
56076      * is created containing a <tt>responseText</tt> property in order to conform to the
56077      * requirements of event handlers and callbacks.</p>
56078      * <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>
56079      * and some server technologies (notably JEE) may require some custom processing in order to
56080      * retrieve parameter names and parameter values from the packet content.</p>
56081      */
56082     /**
56083      * @cfg {Object} baseParams
56084      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
56085      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
56086      */
56087     /**
56088      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
56089      */
56090     timeout: 30,
56091
56092     /**
56093      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
56094      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
56095      * Methods which have been imported by Ext.Direct can be specified here to load and submit
56096      * forms.
56097      * Such as the following:<pre><code>
56098 api: {
56099     load: App.ss.MyProfile.load,
56100     submit: App.ss.MyProfile.submit
56101 }
56102 </code></pre>
56103      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
56104      * to customize how the load method is invoked.
56105      * Submit actions will always use a standard form submit. The formHandler configuration must
56106      * be set on the associated server-side method which has been imported by Ext.Direct</p>
56107      */
56108
56109     /**
56110      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
56111      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
56112      * <code>load</code> configuration.</p>
56113      * <br><p>Specify the params in the order in which they must be executed on the
56114      * server-side as either (1) an Array of String values, or (2) a String of params
56115      * delimited by either whitespace, comma, or pipe. For example,
56116      * any of the following would be acceptable:</p><pre><code>
56117 paramOrder: ['param1','param2','param3']
56118 paramOrder: 'param1 param2 param3'
56119 paramOrder: 'param1,param2,param3'
56120 paramOrder: 'param1|param2|param'
56121      </code></pre>
56122      */
56123     paramOrder: undefined,
56124
56125     /**
56126      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
56127      * <code>load</code> configuration. Send parameters as a collection of named
56128      * arguments (defaults to <tt>false</tt>). Providing a
56129      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
56130      */
56131     paramsAsHash: false,
56132
56133
56134     // private
56135     activeAction : null,
56136
56137     /**
56138      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
56139      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
56140      */
56141     trackResetOnLoad : false,
56142
56143     /**
56144      * @cfg {Boolean} standardSubmit If set to true, standard HTML form submits are used instead of XHR (Ajax) style
56145      * form submissions. (defaults to false)<br>
56146      * <p><b>Note:</b> When using standardSubmit, the options to {@link #submit} are ignored because Ext's
56147      * Ajax infrastracture is bypassed. To pass extra parameters (baseParams and params), you will need to
56148      * create hidden fields within the form.</p>
56149      * <p>The url config option is also bypassed, so set the action as well:</p>
56150      * <pre><code>
56151 PANEL.getForm().getEl().dom.action = 'URL'
56152      * </code></pre>
56153      * An example encapsulating the above:
56154      * <pre><code>
56155 new Ext.FormPanel({
56156     standardSubmit: true,
56157     baseParams: {
56158         foo: 'bar'
56159     },
56160     url: 'myProcess.php',
56161     items: [{
56162         xtype: 'textfield',
56163         name: 'userName'
56164     }],
56165     buttons: [{
56166         text: 'Save',
56167         handler: function(){
56168             var O = this.ownerCt;
56169             if (O.getForm().isValid()) {
56170                 if (O.url)
56171                     O.getForm().getEl().dom.action = O.url;
56172                 if (O.baseParams) {
56173                     for (i in O.baseParams) {
56174                         O.add({
56175                             xtype: 'hidden',
56176                             name: i,
56177                             value: O.baseParams[i]
56178                         })
56179                     }
56180                     O.doLayout();
56181                 }
56182                 O.getForm().submit();
56183             }
56184         }
56185     }]
56186 });
56187      * </code></pre>
56188      */
56189     /**
56190      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
56191      * element by passing it or its id or mask the form itself by passing in true.
56192      * @type Mixed
56193      * @property waitMsgTarget
56194      */
56195
56196     // private
56197     initEl : function(el){
56198         this.el = Ext.get(el);
56199         this.id = this.el.id || Ext.id();
56200         if(!this.standardSubmit){
56201             this.el.on('submit', this.onSubmit, this);
56202         }
56203         this.el.addClass('x-form');
56204     },
56205
56206     /**
56207      * Get the HTML form Element
56208      * @return Ext.Element
56209      */
56210     getEl: function(){
56211         return this.el;
56212     },
56213
56214     // private
56215     onSubmit : function(e){
56216         e.stopEvent();
56217     },
56218
56219     // private
56220     destroy: function() {
56221         this.items.each(function(f){
56222             Ext.destroy(f);
56223         });
56224         if(this.el){
56225             this.el.removeAllListeners();
56226             this.el.remove();
56227         }
56228         this.purgeListeners();
56229     },
56230
56231     /**
56232      * Returns true if client-side validation on the form is successful.
56233      * @return Boolean
56234      */
56235     isValid : function(){
56236         var valid = true;
56237         this.items.each(function(f){
56238            if(!f.validate()){
56239                valid = false;
56240            }
56241         });
56242         return valid;
56243     },
56244
56245     /**
56246      * <p>Returns true if any fields in this form have changed from their original values.</p>
56247      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
56248      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
56249      * or {@link #loadRecord}.</p>
56250      * @return Boolean
56251      */
56252     isDirty : function(){
56253         var dirty = false;
56254         this.items.each(function(f){
56255            if(f.isDirty()){
56256                dirty = true;
56257                return false;
56258            }
56259         });
56260         return dirty;
56261     },
56262
56263     /**
56264      * Performs a predefined action ({@link Ext.form.Action.Submit} or
56265      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
56266      * to perform application-specific processing.
56267      * @param {String/Object} actionName The name of the predefined action type,
56268      * or instance of {@link Ext.form.Action} to perform.
56269      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
56270      * All of the config options listed below are supported by both the
56271      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
56272      * actions unless otherwise noted (custom actions could also accept
56273      * other config options):<ul>
56274      *
56275      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
56276      * to the form's {@link #url}.)</div></li>
56277      *
56278      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
56279      * to the form's method, or POST if not defined)</div></li>
56280      *
56281      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
56282      * (defaults to the form's baseParams, or none if not defined)</p>
56283      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
56284      *
56285      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
56286      * (defaults to the form's default headers)</div></li>
56287      *
56288      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
56289      * be invoked after a successful response (see top of
56290      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
56291      * for a description of what constitutes a successful response).
56292      * The function is passed the following parameters:<ul>
56293      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
56294      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
56295      * <div class="sub-desc">The action object contains these properties of interest:<ul>
56296      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
56297      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
56298      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
56299      * </ul></div></li></ul></div></li>
56300      *
56301      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
56302      * failed transaction attempt. The function is passed the following parameters:<ul>
56303      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
56304      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
56305      * <div class="sub-desc">The action object contains these properties of interest:<ul>
56306      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
56307      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
56308      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
56309      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
56310      * </ul></div></li></ul></div></li>
56311      *
56312      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
56313      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
56314      *
56315      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
56316      * Determines whether a Form's fields are validated in a final call to
56317      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
56318      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
56319      *
56320      * @return {BasicForm} this
56321      */
56322     doAction : function(action, options){
56323         if(Ext.isString(action)){
56324             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
56325         }
56326         if(this.fireEvent('beforeaction', this, action) !== false){
56327             this.beforeAction(action);
56328             action.run.defer(100, action);
56329         }
56330         return this;
56331     },
56332
56333     /**
56334      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
56335      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
56336      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
56337      * <p>The following code:</p><pre><code>
56338 myFormPanel.getForm().submit({
56339     clientValidation: true,
56340     url: 'updateConsignment.php',
56341     params: {
56342         newStatus: 'delivered'
56343     },
56344     success: function(form, action) {
56345        Ext.Msg.alert('Success', action.result.msg);
56346     },
56347     failure: function(form, action) {
56348         switch (action.failureType) {
56349             case Ext.form.Action.CLIENT_INVALID:
56350                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
56351                 break;
56352             case Ext.form.Action.CONNECT_FAILURE:
56353                 Ext.Msg.alert('Failure', 'Ajax communication failed');
56354                 break;
56355             case Ext.form.Action.SERVER_INVALID:
56356                Ext.Msg.alert('Failure', action.result.msg);
56357        }
56358     }
56359 });
56360 </code></pre>
56361      * would process the following server response for a successful submission:<pre><code>
56362 {
56363     "success":true, // note this is Boolean, not string
56364     "msg":"Consignment updated"
56365 }
56366 </code></pre>
56367      * and the following server response for a failed submission:<pre><code>
56368 {
56369     "success":false, // note this is Boolean, not string
56370     "msg":"You do not have permission to perform this operation"
56371 }
56372 </code></pre>
56373      * @return {BasicForm} this
56374      */
56375     submit : function(options){
56376         if(this.standardSubmit){
56377             var v = this.isValid();
56378             if(v){
56379                 this.el.dom.submit();
56380             }
56381             return v;
56382         }
56383         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
56384         this.doAction(submitAction, options);
56385         return this;
56386     },
56387
56388     /**
56389      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
56390      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
56391      * @return {BasicForm} this
56392      */
56393     load : function(options){
56394         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
56395         this.doAction(loadAction, options);
56396         return this;
56397     },
56398
56399     /**
56400      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
56401      * @param {Record} record The record to edit
56402      * @return {BasicForm} this
56403      */
56404     updateRecord : function(record){
56405         record.beginEdit();
56406         var fs = record.fields;
56407         fs.each(function(f){
56408             var field = this.findField(f.name);
56409             if(field){
56410                 record.set(f.name, field.getValue());
56411             }
56412         }, this);
56413         record.endEdit();
56414         return this;
56415     },
56416
56417     /**
56418      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
56419      * {@link Ext.data.Record#data record data}.
56420      * See also {@link #trackResetOnLoad}.
56421      * @param {Record} record The record to load
56422      * @return {BasicForm} this
56423      */
56424     loadRecord : function(record){
56425         this.setValues(record.data);
56426         return this;
56427     },
56428
56429     // private
56430     beforeAction : function(action){
56431         var o = action.options;
56432         if(o.waitMsg){
56433             if(this.waitMsgTarget === true){
56434                 this.el.mask(o.waitMsg, 'x-mask-loading');
56435             }else if(this.waitMsgTarget){
56436                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
56437                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
56438             }else{
56439                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
56440             }
56441         }
56442     },
56443
56444     // private
56445     afterAction : function(action, success){
56446         this.activeAction = null;
56447         var o = action.options;
56448         if(o.waitMsg){
56449             if(this.waitMsgTarget === true){
56450                 this.el.unmask();
56451             }else if(this.waitMsgTarget){
56452                 this.waitMsgTarget.unmask();
56453             }else{
56454                 Ext.MessageBox.updateProgress(1);
56455                 Ext.MessageBox.hide();
56456             }
56457         }
56458         if(success){
56459             if(o.reset){
56460                 this.reset();
56461             }
56462             Ext.callback(o.success, o.scope, [this, action]);
56463             this.fireEvent('actioncomplete', this, action);
56464         }else{
56465             Ext.callback(o.failure, o.scope, [this, action]);
56466             this.fireEvent('actionfailed', this, action);
56467         }
56468     },
56469
56470     /**
56471      * Find a {@link Ext.form.Field} in this form.
56472      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
56473      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
56474      * @return Field
56475      */
56476     findField : function(id){
56477         var field = this.items.get(id);
56478         if(!Ext.isObject(field)){
56479             this.items.each(function(f){
56480                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
56481                     field = f;
56482                     return false;
56483                 }
56484             });
56485         }
56486         return field || null;
56487     },
56488
56489
56490     /**
56491      * Mark fields in this form invalid in bulk.
56492      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
56493      * @return {BasicForm} this
56494      */
56495     markInvalid : function(errors){
56496         if(Ext.isArray(errors)){
56497             for(var i = 0, len = errors.length; i < len; i++){
56498                 var fieldError = errors[i];
56499                 var f = this.findField(fieldError.id);
56500                 if(f){
56501                     f.markInvalid(fieldError.msg);
56502                 }
56503             }
56504         }else{
56505             var field, id;
56506             for(id in errors){
56507                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
56508                     field.markInvalid(errors[id]);
56509                 }
56510             }
56511         }
56512         return this;
56513     },
56514
56515     /**
56516      * Set values for fields in this form in bulk.
56517      * @param {Array/Object} values Either an array in the form:<pre><code>
56518 [{id:'clientName', value:'Fred. Olsen Lines'},
56519  {id:'portOfLoading', value:'FXT'},
56520  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
56521      * or an object hash of the form:<pre><code>
56522 {
56523     clientName: 'Fred. Olsen Lines',
56524     portOfLoading: 'FXT',
56525     portOfDischarge: 'OSL'
56526 }</code></pre>
56527      * @return {BasicForm} this
56528      */
56529     setValues : function(values){
56530         if(Ext.isArray(values)){ // array of objects
56531             for(var i = 0, len = values.length; i < len; i++){
56532                 var v = values[i];
56533                 var f = this.findField(v.id);
56534                 if(f){
56535                     f.setValue(v.value);
56536                     if(this.trackResetOnLoad){
56537                         f.originalValue = f.getValue();
56538                     }
56539                 }
56540             }
56541         }else{ // object hash
56542             var field, id;
56543             for(id in values){
56544                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
56545                     field.setValue(values[id]);
56546                     if(this.trackResetOnLoad){
56547                         field.originalValue = field.getValue();
56548                     }
56549                 }
56550             }
56551         }
56552         return this;
56553     },
56554
56555     /**
56556      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
56557      * If multiple fields exist with the same name they are returned as an array.</p>
56558      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
56559      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
56560      * value can potentially be the emptyText of a field.</p>
56561      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
56562      * @return {String/Object}
56563      */
56564     getValues : function(asString){
56565         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
56566         if(asString === true){
56567             return fs;
56568         }
56569         return Ext.urlDecode(fs);
56570     },
56571
56572     getFieldValues : function(){
56573         var o = {};
56574         this.items.each(function(f){
56575            o[f.getName()] = f.getValue();
56576         });
56577         return o;
56578     },
56579
56580     /**
56581      * Clears all invalid messages in this form.
56582      * @return {BasicForm} this
56583      */
56584     clearInvalid : function(){
56585         this.items.each(function(f){
56586            f.clearInvalid();
56587         });
56588         return this;
56589     },
56590
56591     /**
56592      * Resets this form.
56593      * @return {BasicForm} this
56594      */
56595     reset : function(){
56596         this.items.each(function(f){
56597             f.reset();
56598         });
56599         return this;
56600     },
56601
56602     /**
56603      * Add Ext.form Components to this form's Collection. This does not result in rendering of
56604      * the passed Component, it just enables the form to validate Fields, and distribute values to
56605      * Fields.
56606      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
56607      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
56608      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
56609      * collection.</b></p>
56610      * @param {Field} field1
56611      * @param {Field} field2 (optional)
56612      * @param {Field} etc (optional)
56613      * @return {BasicForm} this
56614      */
56615     add : function(){
56616         this.items.addAll(Array.prototype.slice.call(arguments, 0));
56617         return this;
56618     },
56619
56620
56621     /**
56622      * Removes a field from the items collection (does NOT remove its markup).
56623      * @param {Field} field
56624      * @return {BasicForm} this
56625      */
56626     remove : function(field){
56627         this.items.remove(field);
56628         return this;
56629     },
56630
56631     /**
56632      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
56633      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
56634      * @return {BasicForm} this
56635      */
56636     render : function(){
56637         this.items.each(function(f){
56638             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
56639                 f.applyToMarkup(f.id);
56640             }
56641         });
56642         return this;
56643     },
56644
56645     /**
56646      * Calls {@link Ext#apply} for all fields in this form with the passed object.
56647      * @param {Object} values
56648      * @return {BasicForm} this
56649      */
56650     applyToFields : function(o){
56651         this.items.each(function(f){
56652            Ext.apply(f, o);
56653         });
56654         return this;
56655     },
56656
56657     /**
56658      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
56659      * @param {Object} values
56660      * @return {BasicForm} this
56661      */
56662     applyIfToFields : function(o){
56663         this.items.each(function(f){
56664            Ext.applyIf(f, o);
56665         });
56666         return this;
56667     },
56668
56669     callFieldMethod : function(fnName, args){
56670         args = args || [];
56671         this.items.each(function(f){
56672             if(Ext.isFunction(f[fnName])){
56673                 f[fnName].apply(f, args);
56674             }
56675         });
56676         return this;
56677     }
56678 });
56679
56680 // back compat
56681 Ext.BasicForm = Ext.form.BasicForm;/**
56682  * @class Ext.form.FormPanel
56683  * @extends Ext.Panel
56684  * <p>Standard form container.</p>
56685  * 
56686  * <p><b><u>Layout</u></b></p>
56687  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
56688  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
56689  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
56690  * {@link Ext.layout.FormLayout} layout manager.</p>
56691  * 
56692  * <p><b><u>BasicForm</u></b></p>
56693  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
56694  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
56695  * <div class="mdetail-params"><ul>
56696  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
56697  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
56698  * </ul></div>
56699  *  
56700  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
56701  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
56702  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
56703  * 
56704  * <p><b><u>Form Validation</u></b></p>
56705  * <p>For information on form validation see the following:</p>
56706  * <div class="mdetail-params"><ul>
56707  * <li>{@link Ext.form.TextField}</li>
56708  * <li>{@link Ext.form.VTypes}</li>
56709  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
56710  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
56711  * </ul></div>
56712  * 
56713  * <p><b><u>Form Submission</u></b></p>
56714  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
56715  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
56716  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
56717  * 
56718  * @constructor
56719  * @param {Object} config Configuration options
56720  * @xtype form
56721  */
56722 Ext.FormPanel = Ext.extend(Ext.Panel, {
56723         /**
56724          * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
56725          */
56726     /**
56727      * @cfg {Boolean} hideLabels
56728      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
56729      * <tt>false</tt>.</p>
56730      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
56731      */
56732     /**
56733      * @cfg {Number} labelPad
56734      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
56735      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
56736      */
56737     /**
56738      * @cfg {String} labelSeparator
56739      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
56740      */
56741     /**
56742      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
56743      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
56744      * for its fields) (defaults to <tt>100</tt>).
56745      */
56746     /**
56747      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
56748      */
56749     /**
56750      * @cfg {Array} buttons
56751      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
56752      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
56753      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
56754      * the form's valid/invalid state.</p>
56755      */
56756
56757
56758     /**
56759      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
56760      */
56761     minButtonWidth : 75,
56762
56763     /**
56764      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
56765      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
56766      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
56767      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
56768      * for its fields).
56769      */
56770     labelAlign : 'left',
56771
56772     /**
56773      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
56774      * regularly fires the {@link #clientvalidation} event passing that state.<br>
56775      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
56776      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
56777      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
56778      */
56779     monitorValid : false,
56780
56781     /**
56782      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
56783      */
56784     monitorPoll : 200,
56785
56786     /**
56787      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered. 
56788      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
56789      */
56790     layout : 'form',
56791
56792     // private
56793     initComponent : function(){
56794         this.form = this.createForm();
56795         Ext.FormPanel.superclass.initComponent.call(this);
56796
56797         this.bodyCfg = {
56798             tag: 'form',
56799             cls: this.baseCls + '-body',
56800             method : this.method || 'POST',
56801             id : this.formId || Ext.id()
56802         };
56803         if(this.fileUpload) {
56804             this.bodyCfg.enctype = 'multipart/form-data';
56805         }
56806         this.initItems();
56807         
56808         this.addEvents(
56809             /**
56810              * @event clientvalidation
56811              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
56812              * @param {Ext.form.FormPanel} this
56813              * @param {Boolean} valid true if the form has passed client-side validation
56814              */
56815             'clientvalidation'
56816         );
56817
56818         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
56819     },
56820
56821     // private
56822     createForm : function(){
56823         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
56824         return new Ext.form.BasicForm(null, config);
56825     },
56826
56827     // private
56828     initFields : function(){
56829         var f = this.form;
56830         var formPanel = this;
56831         var fn = function(c){
56832             if(formPanel.isField(c)){
56833                 f.add(c);
56834             }if(c.isFieldWrap){
56835                 Ext.applyIf(c, {
56836                     labelAlign: c.ownerCt.labelAlign,
56837                     labelWidth: c.ownerCt.labelWidth,
56838                     itemCls: c.ownerCt.itemCls
56839                 });
56840                 f.add(c.field);
56841             }else if(c.doLayout && c != formPanel){
56842                 Ext.applyIf(c, {
56843                     labelAlign: c.ownerCt.labelAlign,
56844                     labelWidth: c.ownerCt.labelWidth,
56845                     itemCls: c.ownerCt.itemCls
56846                 });
56847                 //each check required for check/radio groups.
56848                 if(c.items && c.items.each){
56849                     c.items.each(fn, this);
56850                 }
56851             }
56852         };
56853         this.items.each(fn, this);
56854     },
56855
56856     // private
56857     getLayoutTarget : function(){
56858         return this.form.el;
56859     },
56860
56861     /**
56862      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
56863      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
56864      */
56865     getForm : function(){
56866         return this.form;
56867     },
56868
56869     // private
56870     onRender : function(ct, position){
56871         this.initFields();
56872         Ext.FormPanel.superclass.onRender.call(this, ct, position);
56873         this.form.initEl(this.body);
56874     },
56875     
56876     // private
56877     beforeDestroy : function(){
56878         this.stopMonitoring();
56879         Ext.FormPanel.superclass.beforeDestroy.call(this);
56880         /*
56881          * Clear the items here to prevent them being destroyed again.
56882          * Don't move this behaviour to BasicForm because it can be used
56883          * on it's own.
56884          */
56885         this.form.items.clear();
56886         Ext.destroy(this.form);
56887     },
56888
56889         // Determine if a Component is usable as a form Field.
56890     isField : function(c) {
56891         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
56892     },
56893
56894     // private
56895     initEvents : function(){
56896         Ext.FormPanel.superclass.initEvents.call(this);
56897         this.on('remove', this.onRemove, this);
56898         this.on('add', this.onAdd, this);
56899         if(this.monitorValid){ // initialize after render
56900             this.startMonitoring();
56901         }
56902     },
56903     
56904     // private
56905     onAdd : function(ct, c) {
56906                 // If a single form Field, add it
56907         if (this.isField(c)) {
56908             this.form.add(c);
56909                 // If a Container, add any Fields it might contain
56910         } else if (c.findBy) {
56911             Ext.applyIf(c, {
56912                 labelAlign: c.ownerCt.labelAlign,
56913                 labelWidth: c.ownerCt.labelWidth,
56914                 itemCls: c.ownerCt.itemCls
56915             });
56916             this.form.add.apply(this.form, c.findBy(this.isField));
56917         }
56918     },
56919         
56920     // private
56921     onRemove : function(ct, c) {
56922                 // If a single form Field, remove it
56923         if (this.isField(c)) {
56924             Ext.destroy(c.container.up('.x-form-item'));
56925                 this.form.remove(c);
56926                 // If a Container, remove any Fields it might contain
56927         } else if (c.findByType) {
56928             Ext.each(c.findBy(this.isField), this.form.remove, this.form);
56929         }
56930     },
56931
56932     /**
56933      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
56934      * option "monitorValid"
56935      */
56936     startMonitoring : function(){
56937         if(!this.validTask){
56938             this.validTask = new Ext.util.TaskRunner();
56939             this.validTask.start({
56940                 run : this.bindHandler,
56941                 interval : this.monitorPoll || 200,
56942                 scope: this
56943             });
56944         }
56945     },
56946
56947     /**
56948      * Stops monitoring of the valid state of this form
56949      */
56950     stopMonitoring : function(){
56951         if(this.validTask){
56952             this.validTask.stopAll();
56953             this.validTask = null;
56954         }
56955     },
56956
56957     /**
56958      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
56959      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
56960      */
56961     load : function(){
56962         this.form.load.apply(this.form, arguments);  
56963     },
56964
56965     // private
56966     onDisable : function(){
56967         Ext.FormPanel.superclass.onDisable.call(this);
56968         if(this.form){
56969             this.form.items.each(function(){
56970                  this.disable();
56971             });
56972         }
56973     },
56974
56975     // private
56976     onEnable : function(){
56977         Ext.FormPanel.superclass.onEnable.call(this);
56978         if(this.form){
56979             this.form.items.each(function(){
56980                  this.enable();
56981             });
56982         }
56983     },
56984
56985     // private
56986     bindHandler : function(){
56987         var valid = true;
56988         this.form.items.each(function(f){
56989             if(!f.isValid(true)){
56990                 valid = false;
56991                 return false;
56992             }
56993         });
56994         if(this.fbar){
56995             var fitems = this.fbar.items.items;
56996             for(var i = 0, len = fitems.length; i < len; i++){
56997                 var btn = fitems[i];
56998                 if(btn.formBind === true && btn.disabled === valid){
56999                     btn.setDisabled(!valid);
57000                 }
57001             }
57002         }
57003         this.fireEvent('clientvalidation', this, valid);
57004     }
57005 });
57006 Ext.reg('form', Ext.FormPanel);
57007
57008 Ext.form.FormPanel = Ext.FormPanel;
57009
57010 /**\r
57011  * @class Ext.form.FieldSet\r
57012  * @extends Ext.Panel\r
57013  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.\r
57014  * <pre><code>\r
57015 var form = new Ext.FormPanel({\r
57016     title: 'Simple Form with FieldSets',\r
57017     labelWidth: 75, // label settings here cascade unless overridden\r
57018     url: 'save-form.php',\r
57019     frame:true,\r
57020     bodyStyle:'padding:5px 5px 0',\r
57021     width: 700,\r
57022     renderTo: document.body,\r
57023     layout:'column', // arrange items in columns\r
57024     defaults: {      // defaults applied to items\r
57025         layout: 'form',\r
57026         border: false,\r
57027         bodyStyle: 'padding:4px'\r
57028     },\r
57029     items: [{\r
57030         // Fieldset in Column 1\r
57031         xtype:'fieldset',\r
57032         columnWidth: 0.5,\r
57033         title: 'Fieldset 1',\r
57034         collapsible: true,\r
57035         autoHeight:true,\r
57036         defaults: {\r
57037             anchor: '-20' // leave room for error icon\r
57038         },\r
57039         defaultType: 'textfield',\r
57040         items :[{\r
57041                 fieldLabel: 'Field 1'\r
57042             }, {\r
57043                 fieldLabel: 'Field 2'\r
57044             }, {\r
57045                 fieldLabel: 'Field 3'\r
57046             }\r
57047         ]\r
57048     },{\r
57049         // Fieldset in Column 2 - Panel inside\r
57050         xtype:'fieldset',\r
57051         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header\r
57052         autoHeight:true,\r
57053         columnWidth: 0.5,\r
57054         checkboxToggle: true,\r
57055         collapsed: true, // fieldset initially collapsed\r
57056         layout:'anchor',\r
57057         items :[{\r
57058             xtype: 'panel',\r
57059             anchor: '100%',\r
57060             title: 'Panel inside a fieldset',\r
57061             frame: true,\r
57062             height: 100\r
57063         }]\r
57064     }]\r
57065 });\r
57066  * </code></pre>\r
57067  * @constructor\r
57068  * @param {Object} config Configuration options\r
57069  * @xtype fieldset\r
57070  */\r
57071 Ext.form.FieldSet = Ext.extend(Ext.Panel, {\r
57072     /**\r
57073      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just\r
57074      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults\r
57075      * to <tt>false</tt>).\r
57076      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.\r
57077      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element\r
57078      * is:</p><pre><code>\r
57079      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}\r
57080      * </code></pre>   \r
57081      */\r
57082     /**\r
57083      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>\r
57084      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).\r
57085      */\r
57086     /**\r
57087      * @cfg {Boolean} collapsible\r
57088      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically\r
57089      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse\r
57090      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.\r
57091      */\r
57092     /**\r
57093      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.\r
57094      */\r
57095     /**\r
57096      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see \r
57097      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).\r
57098      * This property cascades to child containers.\r
57099      */\r
57100     /**\r
57101      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).\r
57102      */\r
57103     baseCls : 'x-fieldset',\r
57104     /**\r
57105      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).\r
57106      */\r
57107     layout : 'form',\r
57108     /**\r
57109      * @cfg {Boolean} animCollapse\r
57110      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the\r
57111      * animation (defaults to <tt>false</tt>).\r
57112      */\r
57113     animCollapse : false,\r
57114 \r
57115     // private\r
57116     onRender : function(ct, position){\r
57117         if(!this.el){\r
57118             this.el = document.createElement('fieldset');\r
57119             this.el.id = this.id;\r
57120             if (this.title || this.header || this.checkboxToggle) {\r
57121                 this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';\r
57122             }\r
57123         }\r
57124 \r
57125         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);\r
57126 \r
57127         if(this.checkboxToggle){\r
57128             var o = typeof this.checkboxToggle == 'object' ?\r
57129                     this.checkboxToggle :\r
57130                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};\r
57131             this.checkbox = this.header.insertFirst(o);\r
57132             this.checkbox.dom.checked = !this.collapsed;\r
57133             this.mon(this.checkbox, 'click', this.onCheckClick, this);\r
57134         }\r
57135     },\r
57136 \r
57137     // private\r
57138     onCollapse : function(doAnim, animArg){\r
57139         if(this.checkbox){\r
57140             this.checkbox.dom.checked = false;\r
57141         }\r
57142         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);\r
57143 \r
57144     },\r
57145 \r
57146     // private\r
57147     onExpand : function(doAnim, animArg){\r
57148         if(this.checkbox){\r
57149             this.checkbox.dom.checked = true;\r
57150         }\r
57151         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);\r
57152     },\r
57153 \r
57154     /**\r
57155      * This function is called by the fieldset's checkbox when it is toggled (only applies when\r
57156      * checkboxToggle = true).  This method should never be called externally, but can be\r
57157      * overridden to provide custom behavior when the checkbox is toggled if needed.\r
57158      */\r
57159     onCheckClick : function(){\r
57160         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();\r
57161     }\r
57162 \r
57163     /**\r
57164      * @cfg {String/Number} activeItem\r
57165      * @hide\r
57166      */\r
57167     /**\r
57168      * @cfg {Mixed} applyTo\r
57169      * @hide\r
57170      */\r
57171     /**\r
57172      * @cfg {Boolean} bodyBorder\r
57173      * @hide\r
57174      */\r
57175     /**\r
57176      * @cfg {Boolean} border\r
57177      * @hide\r
57178      */\r
57179     /**\r
57180      * @cfg {Boolean/Number} bufferResize\r
57181      * @hide\r
57182      */\r
57183     /**\r
57184      * @cfg {Boolean} collapseFirst\r
57185      * @hide\r
57186      */\r
57187     /**\r
57188      * @cfg {String} defaultType\r
57189      * @hide\r
57190      */\r
57191     /**\r
57192      * @cfg {String} disabledClass\r
57193      * @hide\r
57194      */\r
57195     /**\r
57196      * @cfg {String} elements\r
57197      * @hide\r
57198      */\r
57199     /**\r
57200      * @cfg {Boolean} floating\r
57201      * @hide\r
57202      */\r
57203     /**\r
57204      * @cfg {Boolean} footer\r
57205      * @hide\r
57206      */\r
57207     /**\r
57208      * @cfg {Boolean} frame\r
57209      * @hide\r
57210      */\r
57211     /**\r
57212      * @cfg {Boolean} header\r
57213      * @hide\r
57214      */\r
57215     /**\r
57216      * @cfg {Boolean} headerAsText\r
57217      * @hide\r
57218      */\r
57219     /**\r
57220      * @cfg {Boolean} hideCollapseTool\r
57221      * @hide\r
57222      */\r
57223     /**\r
57224      * @cfg {String} iconCls\r
57225      * @hide\r
57226      */\r
57227     /**\r
57228      * @cfg {Boolean/String} shadow\r
57229      * @hide\r
57230      */\r
57231     /**\r
57232      * @cfg {Number} shadowOffset\r
57233      * @hide\r
57234      */\r
57235     /**\r
57236      * @cfg {Boolean} shim\r
57237      * @hide\r
57238      */\r
57239     /**\r
57240      * @cfg {Object/Array} tbar\r
57241      * @hide\r
57242      */\r
57243     /**\r
57244      * @cfg {String} tabTip\r
57245      * @hide\r
57246      */\r
57247     /**\r
57248      * @cfg {Boolean} titleCollapse\r
57249      * @hide\r
57250      */\r
57251     /**\r
57252      * @cfg {Array} tools\r
57253      * @hide\r
57254      */\r
57255     /**\r
57256      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate\r
57257      * @hide\r
57258      */\r
57259     /**\r
57260      * @cfg {String} xtype\r
57261      * @hide\r
57262      */\r
57263     /**\r
57264      * @property header\r
57265      * @hide\r
57266      */\r
57267     /**\r
57268      * @property footer\r
57269      * @hide\r
57270      */\r
57271     /**\r
57272      * @method focus\r
57273      * @hide\r
57274      */\r
57275     /**\r
57276      * @method getBottomToolbar\r
57277      * @hide\r
57278      */\r
57279     /**\r
57280      * @method getTopToolbar\r
57281      * @hide\r
57282      */\r
57283     /**\r
57284      * @method setIconClass\r
57285      * @hide\r
57286      */\r
57287     /**\r
57288      * @event activate\r
57289      * @hide\r
57290      */\r
57291     /**\r
57292      * @event beforeclose\r
57293      * @hide\r
57294      */\r
57295     /**\r
57296      * @event bodyresize\r
57297      * @hide\r
57298      */\r
57299     /**\r
57300      * @event close\r
57301      * @hide\r
57302      */\r
57303     /**\r
57304      * @event deactivate\r
57305      * @hide\r
57306      */\r
57307 });\r
57308 Ext.reg('fieldset', Ext.form.FieldSet);\r
57309 /**\r
57310  * @class Ext.form.HtmlEditor\r
57311  * @extends Ext.form.Field\r
57312  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be \r
57313  * automatically hidden when needed.  These are noted in the config options where appropriate.\r
57314  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not \r
57315  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.\r
57316  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT\r
57317  * supported by this editor.</b>\r
57318  * <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\r
57319  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.\r
57320  * <br><br>Example usage:\r
57321  * <pre><code>\r
57322 // Simple example rendered with default options:\r
57323 Ext.QuickTips.init();  // enable tooltips\r
57324 new Ext.form.HtmlEditor({\r
57325     renderTo: Ext.getBody(),\r
57326     width: 800,\r
57327     height: 300\r
57328 });\r
57329 \r
57330 // Passed via xtype into a container and with custom options:\r
57331 Ext.QuickTips.init();  // enable tooltips\r
57332 new Ext.Panel({\r
57333     title: 'HTML Editor',\r
57334     renderTo: Ext.getBody(),\r
57335     width: 600,\r
57336     height: 300,\r
57337     frame: true,\r
57338     layout: 'fit',\r
57339     items: {\r
57340         xtype: 'htmleditor',\r
57341         enableColors: false,\r
57342         enableAlignments: false\r
57343     }\r
57344 });\r
57345 </code></pre>\r
57346  * @constructor\r
57347  * Create a new HtmlEditor\r
57348  * @param {Object} config\r
57349  * @xtype htmleditor\r
57350  */\r
57351 \r
57352 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {\r
57353     /**\r
57354      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)\r
57355      */\r
57356     enableFormat : true,\r
57357     /**\r
57358      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)\r
57359      */\r
57360     enableFontSize : true,\r
57361     /**\r
57362      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)\r
57363      */\r
57364     enableColors : true,\r
57365     /**\r
57366      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)\r
57367      */\r
57368     enableAlignments : true,\r
57369     /**\r
57370      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)\r
57371      */\r
57372     enableLists : true,\r
57373     /**\r
57374      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)\r
57375      */\r
57376     enableSourceEdit : true,\r
57377     /**\r
57378      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)\r
57379      */\r
57380     enableLinks : true,\r
57381     /**\r
57382      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)\r
57383      */\r
57384     enableFont : true,\r
57385     /**\r
57386      * @cfg {String} createLinkText The default text for the create link prompt\r
57387      */\r
57388     createLinkText : 'Please enter the URL for the link:',\r
57389     /**\r
57390      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)\r
57391      */\r
57392     defaultLinkValue : 'http:/'+'/',\r
57393     /**\r
57394      * @cfg {Array} fontFamilies An array of available font families\r
57395      */\r
57396     fontFamilies : [\r
57397         'Arial',\r
57398         'Courier New',\r
57399         'Tahoma',\r
57400         'Times New Roman',\r
57401         'Verdana'\r
57402     ],\r
57403     defaultFont: 'tahoma',\r
57404     /**\r
57405      * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#8203; (Zero-width space), &nbsp; (Non-breaking space) in Opera and IE6).\r
57406      */\r
57407     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&nbsp;' : '&#8203;',\r
57408 \r
57409     // private properties\r
57410     actionMode: 'wrap',\r
57411     validationEvent : false,\r
57412     deferHeight: true,\r
57413     initialized : false,\r
57414     activated : false,\r
57415     sourceEditMode : false,\r
57416     onFocus : Ext.emptyFn,\r
57417     iframePad:3,\r
57418     hideMode:'offsets',\r
57419     defaultAutoCreate : {\r
57420         tag: "textarea",\r
57421         style:"width:500px;height:300px;",\r
57422         autocomplete: "off"\r
57423     },\r
57424 \r
57425     // private\r
57426     initComponent : function(){\r
57427         this.addEvents(\r
57428             /**\r
57429              * @event initialize\r
57430              * Fires when the editor is fully initialized (including the iframe)\r
57431              * @param {HtmlEditor} this\r
57432              */\r
57433             'initialize',\r
57434             /**\r
57435              * @event activate\r
57436              * Fires when the editor is first receives the focus. Any insertion must wait\r
57437              * until after this event.\r
57438              * @param {HtmlEditor} this\r
57439              */\r
57440             'activate',\r
57441              /**\r
57442              * @event beforesync\r
57443              * Fires before the textarea is updated with content from the editor iframe. Return false\r
57444              * to cancel the sync.\r
57445              * @param {HtmlEditor} this\r
57446              * @param {String} html\r
57447              */\r
57448             'beforesync',\r
57449              /**\r
57450              * @event beforepush\r
57451              * Fires before the iframe editor is updated with content from the textarea. Return false\r
57452              * to cancel the push.\r
57453              * @param {HtmlEditor} this\r
57454              * @param {String} html\r
57455              */\r
57456             'beforepush',\r
57457              /**\r
57458              * @event sync\r
57459              * Fires when the textarea is updated with content from the editor iframe.\r
57460              * @param {HtmlEditor} this\r
57461              * @param {String} html\r
57462              */\r
57463             'sync',\r
57464              /**\r
57465              * @event push\r
57466              * Fires when the iframe editor is updated with content from the textarea.\r
57467              * @param {HtmlEditor} this\r
57468              * @param {String} html\r
57469              */\r
57470             'push',\r
57471              /**\r
57472              * @event editmodechange\r
57473              * Fires when the editor switches edit modes\r
57474              * @param {HtmlEditor} this\r
57475              * @param {Boolean} sourceEdit True if source edit, false if standard editing.\r
57476              */\r
57477             'editmodechange'\r
57478         )\r
57479     },\r
57480 \r
57481     // private\r
57482     createFontOptions : function(){\r
57483         var buf = [], fs = this.fontFamilies, ff, lc;\r
57484         for(var i = 0, len = fs.length; i< len; i++){\r
57485             ff = fs[i];\r
57486             lc = ff.toLowerCase();\r
57487             buf.push(\r
57488                 '<option value="',lc,'" style="font-family:',ff,';"',\r
57489                     (this.defaultFont == lc ? ' selected="true">' : '>'),\r
57490                     ff,\r
57491                 '</option>'\r
57492             );\r
57493         }\r
57494         return buf.join('');\r
57495     },\r
57496     \r
57497     /*\r
57498      * Protected method that will not generally be called directly. It\r
57499      * is called when the editor creates its toolbar. Override this method if you need to\r
57500      * add custom toolbar buttons.\r
57501      * @param {HtmlEditor} editor\r
57502      */\r
57503     createToolbar : function(editor){\r
57504         \r
57505         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();\r
57506         \r
57507         function btn(id, toggle, handler){\r
57508             return {\r
57509                 itemId : id,\r
57510                 cls : 'x-btn-icon',\r
57511                 iconCls: 'x-edit-'+id,\r
57512                 enableToggle:toggle !== false,\r
57513                 scope: editor,\r
57514                 handler:handler||editor.relayBtnCmd,\r
57515                 clickEvent:'mousedown',\r
57516                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,\r
57517                 overflowText: editor.buttonTips[id].title || undefined,\r
57518                 tabIndex:-1\r
57519             };\r
57520         }\r
57521 \r
57522         // build the toolbar\r
57523         var tb = new Ext.Toolbar({\r
57524             renderTo:this.wrap.dom.firstChild\r
57525         });\r
57526 \r
57527         // stop form submits\r
57528         this.mon(tb.el, 'click', function(e){\r
57529             e.preventDefault();\r
57530         });\r
57531 \r
57532         if(this.enableFont && !Ext.isSafari2){\r
57533             this.fontSelect = tb.el.createChild({\r
57534                 tag:'select',\r
57535                 cls:'x-font-select',\r
57536                 html: this.createFontOptions()\r
57537             });\r
57538             this.mon(this.fontSelect, 'change', function(){\r
57539                 var font = this.fontSelect.dom.value;\r
57540                 this.relayCmd('fontname', font);\r
57541                 this.deferFocus();\r
57542             }, this);\r
57543 \r
57544             tb.add(\r
57545                 this.fontSelect.dom,\r
57546                 '-'\r
57547             );\r
57548         }\r
57549 \r
57550         if(this.enableFormat){\r
57551             tb.add(\r
57552                 btn('bold'),\r
57553                 btn('italic'),\r
57554                 btn('underline')\r
57555             );\r
57556         }\r
57557 \r
57558         if(this.enableFontSize){\r
57559             tb.add(\r
57560                 '-',\r
57561                 btn('increasefontsize', false, this.adjustFont),\r
57562                 btn('decreasefontsize', false, this.adjustFont)\r
57563             );\r
57564         }\r
57565 \r
57566         if(this.enableColors){\r
57567             tb.add(\r
57568                 '-', {\r
57569                     itemId:'forecolor',\r
57570                     cls:'x-btn-icon',\r
57571                     iconCls: 'x-edit-forecolor',\r
57572                     clickEvent:'mousedown',\r
57573                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,\r
57574                     tabIndex:-1,\r
57575                     menu : new Ext.menu.ColorMenu({\r
57576                         allowReselect: true,\r
57577                         focus: Ext.emptyFn,\r
57578                         value:'000000',\r
57579                         plain:true,\r
57580                         listeners: {\r
57581                             scope: this,\r
57582                             select: function(cp, color){\r
57583                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);\r
57584                                 this.deferFocus();\r
57585                             }\r
57586                         },\r
57587                         clickEvent:'mousedown'\r
57588                     })\r
57589                 }, {\r
57590                     itemId:'backcolor',\r
57591                     cls:'x-btn-icon',\r
57592                     iconCls: 'x-edit-backcolor',\r
57593                     clickEvent:'mousedown',\r
57594                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,\r
57595                     tabIndex:-1,\r
57596                     menu : new Ext.menu.ColorMenu({\r
57597                         focus: Ext.emptyFn,\r
57598                         value:'FFFFFF',\r
57599                         plain:true,\r
57600                         allowReselect: true,\r
57601                         listeners: {\r
57602                             scope: this,\r
57603                             select: function(cp, color){\r
57604                                 if(Ext.isGecko){\r
57605                                     this.execCmd('useCSS', false);\r
57606                                     this.execCmd('hilitecolor', color);\r
57607                                     this.execCmd('useCSS', true);\r
57608                                     this.deferFocus();\r
57609                                 }else{\r
57610                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);\r
57611                                     this.deferFocus();\r
57612                                 }\r
57613                             }\r
57614                         },\r
57615                         clickEvent:'mousedown'\r
57616                     })\r
57617                 }\r
57618             );\r
57619         }\r
57620 \r
57621         if(this.enableAlignments){\r
57622             tb.add(\r
57623                 '-',\r
57624                 btn('justifyleft'),\r
57625                 btn('justifycenter'),\r
57626                 btn('justifyright')\r
57627             );\r
57628         }\r
57629 \r
57630         if(!Ext.isSafari2){\r
57631             if(this.enableLinks){\r
57632                 tb.add(\r
57633                     '-',\r
57634                     btn('createlink', false, this.createLink)\r
57635                 );\r
57636             }\r
57637 \r
57638             if(this.enableLists){\r
57639                 tb.add(\r
57640                     '-',\r
57641                     btn('insertorderedlist'),\r
57642                     btn('insertunorderedlist')\r
57643                 );\r
57644             }\r
57645             if(this.enableSourceEdit){\r
57646                 tb.add(\r
57647                     '-',\r
57648                     btn('sourceedit', true, function(btn){\r
57649                         this.toggleSourceEdit(!this.sourceEditMode);\r
57650                     })\r
57651                 );\r
57652             }\r
57653         }\r
57654 \r
57655         this.tb = tb;\r
57656     },\r
57657 \r
57658     /**\r
57659      * Protected method that will not generally be called directly. It\r
57660      * is called when the editor initializes the iframe with HTML contents. Override this method if you\r
57661      * want to change the initialization markup of the iframe (e.g. to add stylesheets).\r
57662      */\r
57663     getDocMarkup : function(){\r
57664         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';\r
57665     },\r
57666 \r
57667     // private\r
57668     getEditorBody : function(){\r
57669         return this.doc.body || this.doc.documentElement;\r
57670     },\r
57671 \r
57672     // private\r
57673     getDoc : function(){\r
57674         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);\r
57675     },\r
57676 \r
57677     // private\r
57678     getWin : function(){\r
57679         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];\r
57680     },\r
57681 \r
57682     // private\r
57683     onRender : function(ct, position){\r
57684         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);\r
57685         this.el.dom.style.border = '0 none';\r
57686         this.el.dom.setAttribute('tabIndex', -1);\r
57687         this.el.addClass('x-hidden');\r
57688         if(Ext.isIE){ // fix IE 1px bogus margin\r
57689             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')\r
57690         }\r
57691         this.wrap = this.el.wrap({\r
57692             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}\r
57693         });\r
57694 \r
57695         this.createToolbar(this);\r
57696 \r
57697         this.disableItems(true);\r
57698         // is this needed?\r
57699         // this.tb.doLayout();\r
57700 \r
57701         this.createIFrame();\r
57702 \r
57703         if(!this.width){\r
57704             var sz = this.el.getSize();\r
57705             this.setSize(sz.width, this.height || sz.height);\r
57706         }\r
57707     },\r
57708 \r
57709     createIFrame: function(){\r
57710         var iframe = document.createElement('iframe');\r
57711         iframe.name = Ext.id();\r
57712         iframe.frameBorder = '0';\r
57713         iframe.src = Ext.isIE ? Ext.SSL_SECURE_URL : "javascript:;";\r
57714         this.wrap.dom.appendChild(iframe);\r
57715 \r
57716         this.iframe = iframe;\r
57717 \r
57718         this.monitorTask = Ext.TaskMgr.start({\r
57719             run: this.checkDesignMode,\r
57720             scope: this,\r
57721             interval:100\r
57722         });\r
57723     },\r
57724 \r
57725     initFrame : function(){\r
57726         Ext.TaskMgr.stop(this.monitorTask);\r
57727         this.doc = this.getDoc();\r
57728         this.win = this.getWin();\r
57729 \r
57730         this.doc.open();\r
57731         this.doc.write(this.getDocMarkup());\r
57732         this.doc.close();\r
57733 \r
57734         var task = { // must defer to wait for browser to be ready\r
57735             run : function(){\r
57736                 if(this.doc.body || this.doc.readyState == 'complete'){\r
57737                     Ext.TaskMgr.stop(task);\r
57738                     this.doc.designMode="on";\r
57739                     this.initEditor.defer(10, this);\r
57740                 }\r
57741             },\r
57742             interval : 10,\r
57743             duration:10000,\r
57744             scope: this\r
57745         };\r
57746         Ext.TaskMgr.start(task);\r
57747     },\r
57748 \r
57749 \r
57750     checkDesignMode : function(){\r
57751         if(this.wrap && this.wrap.dom.offsetWidth){\r
57752             var doc = this.getDoc();\r
57753             if(!doc){\r
57754                 return;\r
57755             }\r
57756             if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){\r
57757                 this.initFrame();\r
57758             }\r
57759         }\r
57760     },\r
57761     \r
57762     disableItems: function(disabled){\r
57763         if(this.fontSelect){\r
57764             this.fontSelect.dom.disabled = disabled;\r
57765         }\r
57766         this.tb.items.each(function(item){\r
57767             if(item.itemId != 'sourceedit'){\r
57768                 item.setDisabled(disabled);\r
57769             }\r
57770         });\r
57771     },\r
57772 \r
57773     // private\r
57774     onResize : function(w, h){\r
57775         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);\r
57776         if(this.el && this.iframe){\r
57777             if(typeof w == 'number'){\r
57778                 var aw = w - this.wrap.getFrameWidth('lr');\r
57779                 this.el.setWidth(this.adjustWidth('textarea', aw));\r
57780                 this.tb.setWidth(aw);\r
57781                 this.iframe.style.width = Math.max(aw, 0) + 'px';\r
57782             }\r
57783             if(typeof h == 'number'){\r
57784                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();\r
57785                 this.el.setHeight(this.adjustWidth('textarea', ah));\r
57786                 this.iframe.style.height = Math.max(ah, 0) + 'px';\r
57787                 if(this.doc){\r
57788                     this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';\r
57789                 }\r
57790             }\r
57791         }\r
57792     },\r
57793 \r
57794     /**\r
57795      * Toggles the editor between standard and source edit mode.\r
57796      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard\r
57797      */\r
57798     toggleSourceEdit : function(sourceEditMode){\r
57799         if(sourceEditMode === undefined){\r
57800             sourceEditMode = !this.sourceEditMode;\r
57801         }\r
57802         this.sourceEditMode = sourceEditMode === true;\r
57803         var btn = this.tb.items.get('sourceedit');\r
57804         if(btn.pressed !== this.sourceEditMode){\r
57805             btn.toggle(this.sourceEditMode);\r
57806             if(!btn.xtbHidden){\r
57807                 return;\r
57808             }\r
57809         }\r
57810         if(this.sourceEditMode){\r
57811             this.disableItems(true);\r
57812             this.syncValue();\r
57813             this.iframe.className = 'x-hidden';\r
57814             this.el.removeClass('x-hidden');\r
57815             this.el.dom.removeAttribute('tabIndex');\r
57816             this.el.focus();\r
57817         }else{\r
57818             if(this.initialized){\r
57819                 this.disableItems(false);\r
57820             }\r
57821             this.pushValue();\r
57822             this.iframe.className = '';\r
57823             this.el.addClass('x-hidden');\r
57824             this.el.dom.setAttribute('tabIndex', -1);\r
57825             this.deferFocus();\r
57826         }\r
57827         var lastSize = this.lastSize;\r
57828         if(lastSize){\r
57829             delete this.lastSize;\r
57830             this.setSize(lastSize);\r
57831         }\r
57832         this.fireEvent('editmodechange', this, this.sourceEditMode);\r
57833     },\r
57834 \r
57835     // private used internally\r
57836     createLink : function(){\r
57837         var url = prompt(this.createLinkText, this.defaultLinkValue);\r
57838         if(url && url != 'http:/'+'/'){\r
57839             this.relayCmd('createlink', url);\r
57840         }\r
57841     },\r
57842 \r
57843     // private (for BoxComponent)\r
57844     adjustSize : Ext.BoxComponent.prototype.adjustSize,\r
57845 \r
57846     // private (for BoxComponent)\r
57847     getResizeEl : function(){\r
57848         return this.wrap;\r
57849     },\r
57850 \r
57851     // private (for BoxComponent)\r
57852     getPositionEl : function(){\r
57853         return this.wrap;\r
57854     },\r
57855 \r
57856     // private\r
57857     initEvents : function(){\r
57858         this.originalValue = this.getValue();\r
57859     },\r
57860 \r
57861     /**\r
57862      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide\r
57863      * @method\r
57864      */\r
57865     markInvalid : Ext.emptyFn,\r
57866     \r
57867     /**\r
57868      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide\r
57869      * @method\r
57870      */\r
57871     clearInvalid : Ext.emptyFn,\r
57872 \r
57873     // docs inherit from Field\r
57874     setValue : function(v){\r
57875         Ext.form.HtmlEditor.superclass.setValue.call(this, v);\r
57876         this.pushValue();\r
57877         return this;\r
57878     },\r
57879 \r
57880     /**\r
57881      * Protected method that will not generally be called directly. If you need/want\r
57882      * custom HTML cleanup, this is the method you should override.\r
57883      * @param {String} html The HTML to be cleaned\r
57884      * @return {String} The cleaned HTML\r
57885      */\r
57886     cleanHtml : function(html){\r
57887         html = String(html);\r
57888         if(html.length > 5){\r
57889             if(Ext.isWebKit){ // strip safari nonsense\r
57890                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');\r
57891             }\r
57892         }\r
57893         if(html == this.defaultValue){\r
57894             html = '';\r
57895         }\r
57896         return html;\r
57897     },\r
57898 \r
57899     /**\r
57900      * Protected method that will not generally be called directly. Syncs the contents\r
57901      * of the editor iframe with the textarea.\r
57902      */\r
57903     syncValue : function(){\r
57904         if(this.initialized){\r
57905             var bd = this.getEditorBody();\r
57906             var html = bd.innerHTML;\r
57907             if(Ext.isWebKit){\r
57908                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!\r
57909                 var m = bs.match(/text-align:(.*?);/i);\r
57910                 if(m && m[1]){\r
57911                     html = '<div style="'+m[0]+'">' + html + '</div>';\r
57912                 }\r
57913             }\r
57914             html = this.cleanHtml(html);\r
57915             if(this.fireEvent('beforesync', this, html) !== false){\r
57916                 this.el.dom.value = html;\r
57917                 this.fireEvent('sync', this, html);\r
57918             }\r
57919         }\r
57920     },\r
57921     \r
57922     //docs inherit from Field\r
57923     getValue : function() {\r
57924         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();\r
57925         return Ext.form.HtmlEditor.superclass.getValue.call(this);\r
57926     },\r
57927 \r
57928     /**\r
57929      * Protected method that will not generally be called directly. Pushes the value of the textarea\r
57930      * into the iframe editor.\r
57931      */\r
57932     pushValue : function(){\r
57933         if(this.initialized){\r
57934             var v = this.el.dom.value;\r
57935             if(!this.activated && v.length < 1){\r
57936                 v = this.defaultValue;\r
57937             }\r
57938             if(this.fireEvent('beforepush', this, v) !== false){\r
57939                 this.getEditorBody().innerHTML = v;\r
57940                 if(Ext.isGecko){\r
57941                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8\r
57942                     var d = this.doc,\r
57943                         mode = d.designMode.toLowerCase();\r
57944                     \r
57945                     d.designMode = mode.toggle('on', 'off');\r
57946                     d.designMode = mode;\r
57947                 }\r
57948                 this.fireEvent('push', this, v);\r
57949             }\r
57950         }\r
57951     },\r
57952 \r
57953     // private\r
57954     deferFocus : function(){\r
57955         this.focus.defer(10, this);\r
57956     },\r
57957 \r
57958     // docs inherit from Field\r
57959     focus : function(){\r
57960         if(this.win && !this.sourceEditMode){\r
57961             this.win.focus();\r
57962         }else{\r
57963             this.el.focus();\r
57964         }\r
57965     },\r
57966 \r
57967     // private\r
57968     initEditor : function(){\r
57969         //Destroying the component during/before initEditor can cause issues.\r
57970         try{\r
57971             var dbody = this.getEditorBody();\r
57972             var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');\r
57973             ss['background-attachment'] = 'fixed'; // w3c\r
57974             dbody.bgProperties = 'fixed'; // ie\r
57975 \r
57976             Ext.DomHelper.applyStyles(dbody, ss);\r
57977 \r
57978             if(this.doc){\r
57979                 try{\r
57980                     Ext.EventManager.removeAll(this.doc);\r
57981                 }catch(e){}\r
57982             }\r
57983 \r
57984             this.doc = this.getDoc();\r
57985 \r
57986             Ext.EventManager.on(this.doc, {\r
57987                 'mousedown': this.onEditorEvent,\r
57988                 'dblclick': this.onEditorEvent,\r
57989                 'click': this.onEditorEvent,\r
57990                 'keyup': this.onEditorEvent,\r
57991                 buffer:100,\r
57992                 scope: this\r
57993             });\r
57994 \r
57995             if(Ext.isGecko){\r
57996                 Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this);\r
57997             }\r
57998             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){\r
57999                 Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this);\r
58000             }\r
58001             this.initialized = true;\r
58002             this.fireEvent('initialize', this);\r
58003             this.doc.editorInitialized = true;\r
58004             this.pushValue();\r
58005         }catch(e){}\r
58006     },\r
58007 \r
58008     // private\r
58009     onDestroy : function(){\r
58010         if(this.monitorTask){\r
58011             Ext.TaskMgr.stop(this.monitorTask);\r
58012         }\r
58013         if(this.rendered){\r
58014             Ext.destroy(this.tb);\r
58015             if(this.wrap){\r
58016                 this.wrap.dom.innerHTML = '';\r
58017                 this.wrap.remove();\r
58018             }\r
58019         }\r
58020         if(this.el){\r
58021             this.el.removeAllListeners();\r
58022             this.el.remove();\r
58023         }\r
58024  \r
58025         if(this.doc){\r
58026             try{\r
58027                 Ext.EventManager.removeAll(this.doc);\r
58028                 for (var prop in this.doc){\r
58029                    delete this.doc[prop];\r
58030                 }\r
58031             }catch(e){}\r
58032         }\r
58033         this.purgeListeners();\r
58034     },\r
58035 \r
58036     // private\r
58037     onFirstFocus : function(){\r
58038         this.activated = true;\r
58039         this.disableItems(false);\r
58040         if(Ext.isGecko){ // prevent silly gecko errors\r
58041             this.win.focus();\r
58042             var s = this.win.getSelection();\r
58043             if(!s.focusNode || s.focusNode.nodeType != 3){\r
58044                 var r = s.getRangeAt(0);\r
58045                 r.selectNodeContents(this.getEditorBody());\r
58046                 r.collapse(true);\r
58047                 this.deferFocus();\r
58048             }\r
58049             try{\r
58050                 this.execCmd('useCSS', true);\r
58051                 this.execCmd('styleWithCSS', false);\r
58052             }catch(e){}\r
58053         }\r
58054         this.fireEvent('activate', this);\r
58055     },\r
58056 \r
58057     // private\r
58058     adjustFont: function(btn){\r
58059         var adjust = btn.itemId == 'increasefontsize' ? 1 : -1;\r
58060 \r
58061         var v = parseInt(this.doc.queryCommandValue('FontSize') || 2, 10);\r
58062         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){\r
58063             // Safari 3 values\r
58064             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px\r
58065             if(v <= 10){\r
58066                 v = 1 + adjust;\r
58067             }else if(v <= 13){\r
58068                 v = 2 + adjust;\r
58069             }else if(v <= 16){\r
58070                 v = 3 + adjust;\r
58071             }else if(v <= 18){\r
58072                 v = 4 + adjust;\r
58073             }else if(v <= 24){\r
58074                 v = 5 + adjust;\r
58075             }else {\r
58076                 v = 6 + adjust;\r
58077             }\r
58078             v = v.constrain(1, 6);\r
58079         }else{\r
58080             if(Ext.isSafari){ // safari\r
58081                 adjust *= 2;\r
58082             }\r
58083             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);\r
58084         }\r
58085         this.execCmd('FontSize', v);\r
58086     },\r
58087 \r
58088     // private\r
58089     onEditorEvent : function(e){\r
58090         this.updateToolbar();\r
58091     },\r
58092 \r
58093 \r
58094     /**\r
58095      * Protected method that will not generally be called directly. It triggers\r
58096      * a toolbar update by reading the markup state of the current selection in the editor.\r
58097      */\r
58098     updateToolbar: function(){\r
58099 \r
58100         if(!this.activated){\r
58101             this.onFirstFocus();\r
58102             return;\r
58103         }\r
58104 \r
58105         var btns = this.tb.items.map, doc = this.doc;\r
58106 \r
58107         if(this.enableFont && !Ext.isSafari2){\r
58108             var name = (this.doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();\r
58109             if(name != this.fontSelect.dom.value){\r
58110                 this.fontSelect.dom.value = name;\r
58111             }\r
58112         }\r
58113         if(this.enableFormat){\r
58114             btns.bold.toggle(doc.queryCommandState('bold'));\r
58115             btns.italic.toggle(doc.queryCommandState('italic'));\r
58116             btns.underline.toggle(doc.queryCommandState('underline'));\r
58117         }\r
58118         if(this.enableAlignments){\r
58119             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));\r
58120             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));\r
58121             btns.justifyright.toggle(doc.queryCommandState('justifyright'));\r
58122         }\r
58123         if(!Ext.isSafari2 && this.enableLists){\r
58124             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));\r
58125             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));\r
58126         }\r
58127         \r
58128         Ext.menu.MenuMgr.hideAll();\r
58129 \r
58130         this.syncValue();\r
58131     },\r
58132 \r
58133     // private\r
58134     relayBtnCmd : function(btn){\r
58135         this.relayCmd(btn.itemId);\r
58136     },\r
58137 \r
58138     /**\r
58139      * Executes a Midas editor command on the editor document and performs necessary focus and\r
58140      * toolbar updates. <b>This should only be called after the editor is initialized.</b>\r
58141      * @param {String} cmd The Midas command\r
58142      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)\r
58143      */\r
58144     relayCmd : function(cmd, value){\r
58145         (function(){\r
58146             this.focus();\r
58147             this.execCmd(cmd, value);\r
58148             this.updateToolbar();\r
58149         }).defer(10, this);\r
58150     },\r
58151 \r
58152     /**\r
58153      * Executes a Midas editor command directly on the editor document.\r
58154      * For visual commands, you should use {@link #relayCmd} instead.\r
58155      * <b>This should only be called after the editor is initialized.</b>\r
58156      * @param {String} cmd The Midas command\r
58157      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)\r
58158      */\r
58159     execCmd : function(cmd, value){\r
58160         this.doc.execCommand(cmd, false, value === undefined ? null : value);\r
58161         this.syncValue();\r
58162     },\r
58163 \r
58164     // private\r
58165     applyCommand : function(e){\r
58166         if(e.ctrlKey){\r
58167             var c = e.getCharCode(), cmd;\r
58168             if(c > 0){\r
58169                 c = String.fromCharCode(c);\r
58170                 switch(c){\r
58171                     case 'b':\r
58172                         cmd = 'bold';\r
58173                     break;\r
58174                     case 'i':\r
58175                         cmd = 'italic';\r
58176                     break;\r
58177                     case 'u':\r
58178                         cmd = 'underline';\r
58179                     break;\r
58180                 }\r
58181                 if(cmd){\r
58182                     this.win.focus();\r
58183                     this.execCmd(cmd);\r
58184                     this.deferFocus();\r
58185                     e.preventDefault();\r
58186                 }\r
58187             }\r
58188         }\r
58189     },\r
58190 \r
58191     /**\r
58192      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated\r
58193      * to insert text.\r
58194      * @param {String} text\r
58195      */\r
58196     insertAtCursor : function(text){\r
58197         if(!this.activated){\r
58198             return;\r
58199         }\r
58200         if(Ext.isIE){\r
58201             this.win.focus();\r
58202             var r = this.doc.selection.createRange();\r
58203             if(r){\r
58204                 r.collapse(true);\r
58205                 r.pasteHTML(text);\r
58206                 this.syncValue();\r
58207                 this.deferFocus();\r
58208             }\r
58209         }else if(Ext.isGecko || Ext.isOpera){\r
58210             this.win.focus();\r
58211             this.execCmd('InsertHTML', text);\r
58212             this.deferFocus();\r
58213         }else if(Ext.isWebKit){\r
58214             this.execCmd('InsertText', text);\r
58215             this.deferFocus();\r
58216         }\r
58217     },\r
58218 \r
58219     // private\r
58220     fixKeys : function(){ // load time branching for fastest keydown performance\r
58221         if(Ext.isIE){\r
58222             return function(e){\r
58223                 var k = e.getKey(), r;\r
58224                 if(k == e.TAB){\r
58225                     e.stopEvent();\r
58226                     r = this.doc.selection.createRange();\r
58227                     if(r){\r
58228                         r.collapse(true);\r
58229                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');\r
58230                         this.deferFocus();\r
58231                     }\r
58232                 }else if(k == e.ENTER){\r
58233                     r = this.doc.selection.createRange();\r
58234                     if(r){\r
58235                         var target = r.parentElement();\r
58236                         if(!target || target.tagName.toLowerCase() != 'li'){\r
58237                             e.stopEvent();\r
58238                             r.pasteHTML('<br />');\r
58239                             r.collapse(false);\r
58240                             r.select();\r
58241                         }\r
58242                     }\r
58243                 }\r
58244             };\r
58245         }else if(Ext.isOpera){\r
58246             return function(e){\r
58247                 var k = e.getKey();\r
58248                 if(k == e.TAB){\r
58249                     e.stopEvent();\r
58250                     this.win.focus();\r
58251                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');\r
58252                     this.deferFocus();\r
58253                 }\r
58254             };\r
58255         }else if(Ext.isWebKit){\r
58256             return function(e){\r
58257                 var k = e.getKey();\r
58258                 if(k == e.TAB){\r
58259                     e.stopEvent();\r
58260                     this.execCmd('InsertText','\t');\r
58261                     this.deferFocus();\r
58262                 }\r
58263              };\r
58264         }\r
58265     }(),\r
58266 \r
58267     /**\r
58268      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>\r
58269      * @return {Ext.Toolbar}\r
58270      */\r
58271     getToolbar : function(){\r
58272         return this.tb;\r
58273     },\r
58274 \r
58275     /**\r
58276      * Object collection of toolbar tooltips for the buttons in the editor. The key\r
58277      * is the command id associated with that button and the value is a valid QuickTips object.\r
58278      * For example:\r
58279 <pre><code>\r
58280 {\r
58281     bold : {\r
58282         title: 'Bold (Ctrl+B)',\r
58283         text: 'Make the selected text bold.',\r
58284         cls: 'x-html-editor-tip'\r
58285     },\r
58286     italic : {\r
58287         title: 'Italic (Ctrl+I)',\r
58288         text: 'Make the selected text italic.',\r
58289         cls: 'x-html-editor-tip'\r
58290     },\r
58291     ...\r
58292 </code></pre>\r
58293     * @type Object\r
58294      */\r
58295     buttonTips : {\r
58296         bold : {\r
58297             title: 'Bold (Ctrl+B)',\r
58298             text: 'Make the selected text bold.',\r
58299             cls: 'x-html-editor-tip'\r
58300         },\r
58301         italic : {\r
58302             title: 'Italic (Ctrl+I)',\r
58303             text: 'Make the selected text italic.',\r
58304             cls: 'x-html-editor-tip'\r
58305         },\r
58306         underline : {\r
58307             title: 'Underline (Ctrl+U)',\r
58308             text: 'Underline the selected text.',\r
58309             cls: 'x-html-editor-tip'\r
58310         },\r
58311         increasefontsize : {\r
58312             title: 'Grow Text',\r
58313             text: 'Increase the font size.',\r
58314             cls: 'x-html-editor-tip'\r
58315         },\r
58316         decreasefontsize : {\r
58317             title: 'Shrink Text',\r
58318             text: 'Decrease the font size.',\r
58319             cls: 'x-html-editor-tip'\r
58320         },\r
58321         backcolor : {\r
58322             title: 'Text Highlight Color',\r
58323             text: 'Change the background color of the selected text.',\r
58324             cls: 'x-html-editor-tip'\r
58325         },\r
58326         forecolor : {\r
58327             title: 'Font Color',\r
58328             text: 'Change the color of the selected text.',\r
58329             cls: 'x-html-editor-tip'\r
58330         },\r
58331         justifyleft : {\r
58332             title: 'Align Text Left',\r
58333             text: 'Align text to the left.',\r
58334             cls: 'x-html-editor-tip'\r
58335         },\r
58336         justifycenter : {\r
58337             title: 'Center Text',\r
58338             text: 'Center text in the editor.',\r
58339             cls: 'x-html-editor-tip'\r
58340         },\r
58341         justifyright : {\r
58342             title: 'Align Text Right',\r
58343             text: 'Align text to the right.',\r
58344             cls: 'x-html-editor-tip'\r
58345         },\r
58346         insertunorderedlist : {\r
58347             title: 'Bullet List',\r
58348             text: 'Start a bulleted list.',\r
58349             cls: 'x-html-editor-tip'\r
58350         },\r
58351         insertorderedlist : {\r
58352             title: 'Numbered List',\r
58353             text: 'Start a numbered list.',\r
58354             cls: 'x-html-editor-tip'\r
58355         },\r
58356         createlink : {\r
58357             title: 'Hyperlink',\r
58358             text: 'Make the selected text a hyperlink.',\r
58359             cls: 'x-html-editor-tip'\r
58360         },\r
58361         sourceedit : {\r
58362             title: 'Source Edit',\r
58363             text: 'Switch to source editing mode.',\r
58364             cls: 'x-html-editor-tip'\r
58365         }\r
58366     }\r
58367 \r
58368     // hide stuff that is not compatible\r
58369     /**\r
58370      * @event blur\r
58371      * @hide\r
58372      */\r
58373     /**\r
58374      * @event change\r
58375      * @hide\r
58376      */\r
58377     /**\r
58378      * @event focus\r
58379      * @hide\r
58380      */\r
58381     /**\r
58382      * @event specialkey\r
58383      * @hide\r
58384      */\r
58385     /**\r
58386      * @cfg {String} fieldClass @hide\r
58387      */\r
58388     /**\r
58389      * @cfg {String} focusClass @hide\r
58390      */\r
58391     /**\r
58392      * @cfg {String} autoCreate @hide\r
58393      */\r
58394     /**\r
58395      * @cfg {String} inputType @hide\r
58396      */\r
58397     /**\r
58398      * @cfg {String} invalidClass @hide\r
58399      */\r
58400     /**\r
58401      * @cfg {String} invalidText @hide\r
58402      */\r
58403     /**\r
58404      * @cfg {String} msgFx @hide\r
58405      */\r
58406     /**\r
58407      * @cfg {String} validateOnBlur @hide\r
58408      */\r
58409     /**\r
58410      * @cfg {Boolean} allowDomMove  @hide\r
58411      */\r
58412     /**\r
58413      * @cfg {String} applyTo @hide\r
58414      */\r
58415     /**\r
58416      * @cfg {String} autoHeight  @hide\r
58417      */\r
58418     /**\r
58419      * @cfg {String} autoWidth  @hide\r
58420      */\r
58421     /**\r
58422      * @cfg {String} cls  @hide\r
58423      */\r
58424     /**\r
58425      * @cfg {String} disabled  @hide\r
58426      */\r
58427     /**\r
58428      * @cfg {String} disabledClass  @hide\r
58429      */\r
58430     /**\r
58431      * @cfg {String} msgTarget  @hide\r
58432      */\r
58433     /**\r
58434      * @cfg {String} readOnly  @hide\r
58435      */\r
58436     /**\r
58437      * @cfg {String} style  @hide\r
58438      */\r
58439     /**\r
58440      * @cfg {String} validationDelay  @hide\r
58441      */\r
58442     /**\r
58443      * @cfg {String} validationEvent  @hide\r
58444      */\r
58445     /**\r
58446      * @cfg {String} tabIndex  @hide\r
58447      */\r
58448     /**\r
58449      * @property disabled\r
58450      * @hide\r
58451      */\r
58452     /**\r
58453      * @method applyToMarkup\r
58454      * @hide\r
58455      */\r
58456     /**\r
58457      * @method disable\r
58458      * @hide\r
58459      */\r
58460     /**\r
58461      * @method enable\r
58462      * @hide\r
58463      */\r
58464     /**\r
58465      * @method validate\r
58466      * @hide\r
58467      */\r
58468     /**\r
58469      * @event valid\r
58470      * @hide\r
58471      */\r
58472     /**\r
58473      * @method setDisabled\r
58474      * @hide\r
58475      */\r
58476     /**\r
58477      * @cfg keys\r
58478      * @hide\r
58479      */\r
58480 });\r
58481 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**\r
58482  * @class Ext.form.TimeField\r
58483  * @extends Ext.form.ComboBox\r
58484  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:\r
58485  * <pre><code>\r
58486 new Ext.form.TimeField({\r
58487     minValue: '9:00 AM',\r
58488     maxValue: '6:00 PM',\r
58489     increment: 30\r
58490 });\r
58491 </code></pre>\r
58492  * @constructor\r
58493  * Create a new TimeField\r
58494  * @param {Object} config\r
58495  * @xtype timefield\r
58496  */\r
58497 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {\r
58498     /**\r
58499      * @cfg {Date/String} minValue\r
58500      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string \r
58501      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).\r
58502      */\r
58503     minValue : null,\r
58504     /**\r
58505      * @cfg {Date/String} maxValue\r
58506      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string \r
58507      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).\r
58508      */\r
58509     maxValue : null,\r
58510     /**\r
58511      * @cfg {String} minText\r
58512      * The error text to display when the date in the cell is before minValue (defaults to\r
58513      * 'The time in this field must be equal to or after {0}').\r
58514      */\r
58515     minText : "The time in this field must be equal to or after {0}",\r
58516     /**\r
58517      * @cfg {String} maxText\r
58518      * The error text to display when the time is after maxValue (defaults to\r
58519      * 'The time in this field must be equal to or before {0}').\r
58520      */\r
58521     maxText : "The time in this field must be equal to or before {0}",\r
58522     /**\r
58523      * @cfg {String} invalidText\r
58524      * The error text to display when the time in the field is invalid (defaults to\r
58525      * '{value} is not a valid time').\r
58526      */\r
58527     invalidText : "{0} is not a valid time",\r
58528     /**\r
58529      * @cfg {String} format\r
58530      * The default time format string which can be overriden for localization support.  The format must be\r
58531      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time\r
58532      * format try 'H:i' instead.\r
58533      */\r
58534     format : "g:i A",\r
58535     /**\r
58536      * @cfg {String} altFormats\r
58537      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined\r
58538      * 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').\r
58539      */\r
58540     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",\r
58541     /**\r
58542      * @cfg {Number} increment\r
58543      * The number of minutes between each time value in the list (defaults to 15).\r
58544      */\r
58545     increment: 15,\r
58546 \r
58547     // private override\r
58548     mode: 'local',\r
58549     // private override\r
58550     triggerAction: 'all',\r
58551     // private override\r
58552     typeAhead: false,\r
58553     \r
58554     // private - This is the date to use when generating time values in the absence of either minValue\r
58555     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an \r
58556     // arbitrary "safe" date that can be any date aside from DST boundary dates.\r
58557     initDate: '1/1/2008',\r
58558 \r
58559     // private\r
58560     initComponent : function(){\r
58561         if(typeof this.minValue == "string"){\r
58562             this.minValue = this.parseDate(this.minValue);\r
58563         }\r
58564         if(typeof this.maxValue == "string"){\r
58565             this.maxValue = this.parseDate(this.maxValue);\r
58566         }\r
58567 \r
58568         if(!this.store){\r
58569             var min = this.parseDate(this.minValue) || new Date(this.initDate).clearTime();\r
58570             var max = this.parseDate(this.maxValue) || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1);\r
58571             var times = [];\r
58572             while(min <= max){\r
58573                 times.push(min.dateFormat(this.format));\r
58574                 min = min.add('mi', this.increment);\r
58575             }\r
58576             this.store = times;\r
58577         }\r
58578         Ext.form.TimeField.superclass.initComponent.call(this);\r
58579     },\r
58580 \r
58581     // inherited docs\r
58582     getValue : function(){\r
58583         var v = Ext.form.TimeField.superclass.getValue.call(this);\r
58584         return this.formatDate(this.parseDate(v)) || '';\r
58585     },\r
58586 \r
58587     // inherited docs\r
58588     setValue : function(value){\r
58589         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));\r
58590     },\r
58591 \r
58592     // private overrides\r
58593     validateValue : Ext.form.DateField.prototype.validateValue,\r
58594     parseDate : Ext.form.DateField.prototype.parseDate,\r
58595     formatDate : Ext.form.DateField.prototype.formatDate,\r
58596 \r
58597     // private\r
58598     beforeBlur : function(){\r
58599         var v = this.parseDate(this.getRawValue());\r
58600         if(v){\r
58601             this.setValue(v.dateFormat(this.format));\r
58602         }\r
58603         Ext.form.TimeField.superclass.beforeBlur.call(this);\r
58604     }\r
58605 \r
58606     /**\r
58607      * @cfg {Boolean} grow @hide\r
58608      */\r
58609     /**\r
58610      * @cfg {Number} growMin @hide\r
58611      */\r
58612     /**\r
58613      * @cfg {Number} growMax @hide\r
58614      */\r
58615     /**\r
58616      * @hide\r
58617      * @method autoSize\r
58618      */\r
58619 });\r
58620 Ext.reg('timefield', Ext.form.TimeField);/**
58621  * @class Ext.form.Label
58622  * @extends Ext.BoxComponent
58623  * Basic Label field.
58624  * @constructor
58625  * Creates a new Label
58626  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
58627  * 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
58628  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
58629  * @xtype label
58630  */
58631 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
58632     /**
58633      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
58634      * tags within the label's innerHTML, use the {@link #html} config instead.
58635      */
58636     /**
58637      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
58638      * attribute. If not specified, the attribute will not be added to the label.
58639      */
58640     /**
58641      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
58642      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
58643      */
58644
58645     // private
58646     onRender : function(ct, position){
58647         if(!this.el){
58648             this.el = document.createElement('label');
58649             this.el.id = this.getId();
58650             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
58651             if(this.forId){
58652                 this.el.setAttribute('for', this.forId);
58653             }
58654         }
58655         Ext.form.Label.superclass.onRender.call(this, ct, position);
58656     },
58657
58658     /**
58659      * Updates the label's innerHTML with the specified string.
58660      * @param {String} text The new label text
58661      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
58662      * to the label (defaults to true which encodes the value). This might be useful if you want to include
58663      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
58664      * @return {Label} this
58665      */
58666     setText : function(t, encode){
58667         var e = encode === false;
58668         this[!e ? 'text' : 'html'] = t;
58669         delete this[e ? 'text' : 'html'];
58670         if(this.rendered){
58671             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
58672         }
58673         return this;
58674     }
58675 });
58676
58677 Ext.reg('label', Ext.form.Label);/**
58678  * @class Ext.form.Action
58679  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
58680  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
58681  * the Form needs to perform an action such as submit or load. The Configuration options
58682  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
58683  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
58684  * <p>The instance of Action which performed the action is passed to the success
58685  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
58686  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
58687  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
58688  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
58689  */
58690 Ext.form.Action = function(form, options){
58691     this.form = form;
58692     this.options = options || {};
58693 };
58694
58695 /**
58696  * Failure type returned when client side validation of the Form fails
58697  * thus aborting a submit action. Client side validation is performed unless
58698  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
58699  * @type {String}
58700  * @static
58701  */
58702 Ext.form.Action.CLIENT_INVALID = 'client';
58703 /**
58704  * <p>Failure type returned when server side processing fails and the {@link #result}'s
58705  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
58706  * <p>In the case of a form submission, field-specific error messages may be returned in the
58707  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
58708  * @type {String}
58709  * @static
58710  */
58711 Ext.form.Action.SERVER_INVALID = 'server';
58712 /**
58713  * Failure type returned when a communication error happens when attempting
58714  * to send a request to the remote server. The {@link #response} may be examined to
58715  * provide further information.
58716  * @type {String}
58717  * @static
58718  */
58719 Ext.form.Action.CONNECT_FAILURE = 'connect';
58720 /**
58721  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
58722  * property is set to <tt>false</tt>, or no field values are returned in the response's
58723  * <tt style="font-weight:bold">data</tt> property.
58724  * @type {String}
58725  * @static
58726  */
58727 Ext.form.Action.LOAD_FAILURE = 'load';
58728
58729 Ext.form.Action.prototype = {
58730 /**
58731  * @cfg {String} url The URL that the Action is to invoke.
58732  */
58733 /**
58734  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
58735  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
58736  * <b>before</b> the {@link #success} callback is called and before the Form's
58737  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
58738  */
58739 /**
58740  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
58741  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
58742  */
58743 /**
58744  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
58745  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
58746  * input fields.</p>
58747  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
58748  */
58749 /**
58750  * @cfg {Number} timeout The number of seconds to wait for a server response before
58751  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
58752  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
58753  * {@link Ext.form.BasicForm form}.
58754  */
58755 /**
58756  * @cfg {Function} success The function to call when a valid success return packet is recieved.
58757  * The function is passed the following parameters:<ul class="mdetail-params">
58758  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
58759  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
58760  * property of this object may be examined to perform custom postprocessing.</div></li>
58761  * </ul>
58762  */
58763 /**
58764  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
58765  * error ocurred in the Ajax communication.
58766  * The function is passed the following parameters:<ul class="mdetail-params">
58767  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
58768  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
58769  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
58770  * property of this object may be examined to perform custom postprocessing.</div></li>
58771  * </ul>
58772  */
58773 /**
58774  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
58775  * for the callback functions).
58776  */
58777 /**
58778  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
58779  * during the time the action is being processed.
58780  */
58781 /**
58782  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
58783  * during the time the action is being processed.
58784  */
58785
58786 /**
58787  * The type of action this Action instance performs.
58788  * Currently only "submit" and "load" are supported.
58789  * @type {String}
58790  */
58791     type : 'default',
58792 /**
58793  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
58794  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
58795  * <pre><code>
58796 var fp = new Ext.form.FormPanel({
58797 ...
58798 buttons: [{
58799     text: 'Save',
58800     formBind: true,
58801     handler: function(){
58802         if(fp.getForm().isValid()){
58803             fp.getForm().submit({
58804                 url: 'form-submit.php',
58805                 waitMsg: 'Submitting your data...',
58806                 success: function(form, action){
58807                     // server responded with success = true
58808                     var result = action.{@link #result};
58809                 },
58810                 failure: function(form, action){
58811                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
58812                         Ext.Msg.alert('Error',
58813                             'Status:'+action.{@link #response}.status+': '+
58814                             action.{@link #response}.statusText);
58815                     }
58816                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
58817                         // server responded with success = false
58818                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
58819                     }
58820                 }
58821             });
58822         }
58823     }
58824 },{
58825     text: 'Reset',
58826     handler: function(){
58827         fp.getForm().reset();
58828     }
58829 }]
58830  * </code></pre>
58831  * @property failureType
58832  * @type {String}
58833  */
58834  /**
58835  * The XMLHttpRequest object used to perform the action.
58836  * @property response
58837  * @type {Object}
58838  */
58839  /**
58840  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
58841  * other, action-specific properties.
58842  * @property result
58843  * @type {Object}
58844  */
58845
58846     // interface method
58847     run : function(options){
58848
58849     },
58850
58851     // interface method
58852     success : function(response){
58853
58854     },
58855
58856     // interface method
58857     handleResponse : function(response){
58858
58859     },
58860
58861     // default connection failure
58862     failure : function(response){
58863         this.response = response;
58864         this.failureType = Ext.form.Action.CONNECT_FAILURE;
58865         this.form.afterAction(this, false);
58866     },
58867
58868     // private
58869     // shared code among all Actions to validate that there was a response
58870     // with either responseText or responseXml
58871     processResponse : function(response){
58872         this.response = response;
58873         if(!response.responseText && !response.responseXML){
58874             return true;
58875         }
58876         this.result = this.handleResponse(response);
58877         return this.result;
58878     },
58879
58880     // utility functions used internally
58881     getUrl : function(appendParams){
58882         var url = this.options.url || this.form.url || this.form.el.dom.action;
58883         if(appendParams){
58884             var p = this.getParams();
58885             if(p){
58886                 url = Ext.urlAppend(url, p);
58887             }
58888         }
58889         return url;
58890     },
58891
58892     // private
58893     getMethod : function(){
58894         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
58895     },
58896
58897     // private
58898     getParams : function(){
58899         var bp = this.form.baseParams;
58900         var p = this.options.params;
58901         if(p){
58902             if(typeof p == "object"){
58903                 p = Ext.urlEncode(Ext.applyIf(p, bp));
58904             }else if(typeof p == 'string' && bp){
58905                 p += '&' + Ext.urlEncode(bp);
58906             }
58907         }else if(bp){
58908             p = Ext.urlEncode(bp);
58909         }
58910         return p;
58911     },
58912
58913     // private
58914     createCallback : function(opts){
58915         var opts = opts || {};
58916         return {
58917             success: this.success,
58918             failure: this.failure,
58919             scope: this,
58920             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
58921             upload: this.form.fileUpload ? this.success : undefined
58922         };
58923     }
58924 };
58925
58926 /**
58927  * @class Ext.form.Action.Submit
58928  * @extends Ext.form.Action
58929  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
58930  * and processes the returned response.</p>
58931  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
58932  * {@link Ext.form.BasicForm#submit submit}ting.</p>
58933  * <p><u><b>Response Packet Criteria</b></u></p>
58934  * <p>A response packet may contain:
58935  * <div class="mdetail-params"><ul>
58936  * <li><b><code>success</code></b> property : Boolean
58937  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
58938  * <li><b><code>errors</code></b> property : Object
58939  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
58940  * which is optional, contains error messages for invalid fields.</div></li>
58941  * </ul></div>
58942  * <p><u><b>JSON Packets</b></u></p>
58943  * <p>By default, response packets are assumed to be JSON, so a typical response
58944  * packet may look like this:</p><pre><code>
58945 {
58946     success: false,
58947     errors: {
58948         clientCode: "Client not found",
58949         portOfLoading: "This field must not be null"
58950     }
58951 }</code></pre>
58952  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
58953  * or event handler methods. The object decoded from this JSON is available in the
58954  * {@link Ext.form.Action#result result} property.</p>
58955  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
58956     errorReader: new Ext.data.XmlReader({
58957             record : 'field',
58958             success: '@success'
58959         }, [
58960             'id', 'msg'
58961         ]
58962     )
58963 </code></pre>
58964  * <p>then the results may be sent back in XML format:</p><pre><code>
58965 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
58966 &lt;message success="false"&gt;
58967 &lt;errors&gt;
58968     &lt;field&gt;
58969         &lt;id&gt;clientCode&lt;/id&gt;
58970         &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;
58971     &lt;/field&gt;
58972     &lt;field&gt;
58973         &lt;id&gt;portOfLoading&lt;/id&gt;
58974         &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;
58975     &lt;/field&gt;
58976 &lt;/errors&gt;
58977 &lt;/message&gt;
58978 </code></pre>
58979  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
58980  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
58981  */
58982 Ext.form.Action.Submit = function(form, options){
58983     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
58984 };
58985
58986 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
58987     /**
58988      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
58989      * no need for an errorReader.</b></p>
58990      * <p>A Reader which reads a single record from the returned data. The DataReader's
58991      * <b>success</b> property specifies how submission success is determined. The Record's
58992      * data provides the error messages to apply to any invalid form Fields.</p>
58993      */
58994     /**
58995      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
58996      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
58997      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
58998      * is performed.
58999      */
59000     type : 'submit',
59001
59002     // private
59003     run : function(){
59004         var o = this.options;
59005         var method = this.getMethod();
59006         var isGet = method == 'GET';
59007         if(o.clientValidation === false || this.form.isValid()){
59008             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
59009                 form:this.form.el.dom,
59010                 url:this.getUrl(isGet),
59011                 method: method,
59012                 headers: o.headers,
59013                 params:!isGet ? this.getParams() : null,
59014                 isUpload: this.form.fileUpload
59015             }));
59016         }else if (o.clientValidation !== false){ // client validation failed
59017             this.failureType = Ext.form.Action.CLIENT_INVALID;
59018             this.form.afterAction(this, false);
59019         }
59020     },
59021
59022     // private
59023     success : function(response){
59024         var result = this.processResponse(response);
59025         if(result === true || result.success){
59026             this.form.afterAction(this, true);
59027             return;
59028         }
59029         if(result.errors){
59030             this.form.markInvalid(result.errors);
59031             this.failureType = Ext.form.Action.SERVER_INVALID;
59032         }
59033         this.form.afterAction(this, false);
59034     },
59035
59036     // private
59037     handleResponse : function(response){
59038         if(this.form.errorReader){
59039             var rs = this.form.errorReader.read(response);
59040             var errors = [];
59041             if(rs.records){
59042                 for(var i = 0, len = rs.records.length; i < len; i++) {
59043                     var r = rs.records[i];
59044                     errors[i] = r.data;
59045                 }
59046             }
59047             if(errors.length < 1){
59048                 errors = null;
59049             }
59050             return {
59051                 success : rs.success,
59052                 errors : errors
59053             };
59054         }
59055         return Ext.decode(response.responseText);
59056     }
59057 });
59058
59059
59060 /**
59061  * @class Ext.form.Action.Load
59062  * @extends Ext.form.Action
59063  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
59064  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
59065  * {@link Ext.form.BasicForm#load load}ing.</p>
59066  * <p><u><b>Response Packet Criteria</b></u></p>
59067  * <p>A response packet <b>must</b> contain:
59068  * <div class="mdetail-params"><ul>
59069  * <li><b><code>success</code></b> property : Boolean</li>
59070  * <li><b><code>data</code></b> property : Object</li>
59071  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
59072  * The individual value object for each Field is passed to the Field's
59073  * {@link Ext.form.Field#setValue setValue} method.</div></li>
59074  * </ul></div>
59075  * <p><u><b>JSON Packets</b></u></p>
59076  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
59077 var myFormPanel = new Ext.form.FormPanel({
59078     title: 'Client and routing info',
59079     items: [{
59080         fieldLabel: 'Client',
59081         name: 'clientName'
59082     }, {
59083         fieldLabel: 'Port of loading',
59084         name: 'portOfLoading'
59085     }, {
59086         fieldLabel: 'Port of discharge',
59087         name: 'portOfDischarge'
59088     }]
59089 });
59090 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
59091     url: '/getRoutingInfo.php',
59092     params: {
59093         consignmentRef: myConsignmentRef
59094     },
59095     failure: function(form, action() {
59096         Ext.Msg.alert("Load failed", action.result.errorMessage);
59097     }
59098 });
59099 </code></pre>
59100  * a <b>success response</b> packet may look like this:</p><pre><code>
59101 {
59102     success: true,
59103     data: {
59104         clientName: "Fred. Olsen Lines",
59105         portOfLoading: "FXT",
59106         portOfDischarge: "OSL"
59107     }
59108 }</code></pre>
59109  * while a <b>failure response</b> packet may look like this:</p><pre><code>
59110 {
59111     success: false,
59112     errorMessage: "Consignment reference not found"
59113 }</code></pre>
59114  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
59115  * callback or event handler methods. The object decoded from this JSON is available in the
59116  * {@link Ext.form.Action#result result} property.</p>
59117  */
59118 Ext.form.Action.Load = function(form, options){
59119     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
59120     this.reader = this.form.reader;
59121 };
59122
59123 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
59124     // private
59125     type : 'load',
59126
59127     // private
59128     run : function(){
59129         Ext.Ajax.request(Ext.apply(
59130                 this.createCallback(this.options), {
59131                     method:this.getMethod(),
59132                     url:this.getUrl(false),
59133                     headers: this.options.headers,
59134                     params:this.getParams()
59135         }));
59136     },
59137
59138     // private
59139     success : function(response){
59140         var result = this.processResponse(response);
59141         if(result === true || !result.success || !result.data){
59142             this.failureType = Ext.form.Action.LOAD_FAILURE;
59143             this.form.afterAction(this, false);
59144             return;
59145         }
59146         this.form.clearInvalid();
59147         this.form.setValues(result.data);
59148         this.form.afterAction(this, true);
59149     },
59150
59151     // private
59152     handleResponse : function(response){
59153         if(this.form.reader){
59154             var rs = this.form.reader.read(response);
59155             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
59156             return {
59157                 success : rs.success,
59158                 data : data
59159             };
59160         }
59161         return Ext.decode(response.responseText);
59162     }
59163 });
59164
59165
59166
59167 /**
59168  * @class Ext.form.Action.DirectLoad
59169  * @extends Ext.form.Action.Load
59170  * Provides Ext.direct support for loading form data. This example illustrates usage
59171  * of Ext.Direct to load a submit a form through Ext.Direct.
59172  * <pre><code>
59173 var myFormPanel = new Ext.form.FormPanel({
59174     // configs for FormPanel
59175     title: 'Basic Information',
59176     border: false,
59177     padding: 10,
59178     buttons:[{
59179         text: 'Submit',
59180         handler: function(){
59181             basicInfo.getForm().submit({
59182                 params: {
59183                     uid: 5
59184                 }
59185             });
59186         }
59187     }],
59188     
59189     // configs apply to child items
59190     defaults: {anchor: '100%'},
59191     defaultType: 'textfield',
59192     items: [
59193         // form fields go here
59194     ],
59195     
59196     // configs for BasicForm
59197     api: {
59198         load: Profile.getBasicInfo,
59199         // The server-side must mark the submit handler as a 'formHandler'
59200         submit: Profile.updateBasicInfo
59201     },    
59202     paramOrder: ['uid']
59203 });
59204
59205 // load the form
59206 myFormPanel.getForm().load({
59207     params: {
59208         uid: 5
59209     }
59210 });
59211  * </code></pre>
59212  */
59213 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
59214     constructor: function(form, opts) {        
59215         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
59216     },
59217     type: 'directload',
59218     
59219     run : function(){
59220         var args = this.getParams();
59221         args.push(this.success, this);                
59222         this.form.api.load.apply(window, args);
59223     },
59224     
59225     getParams: function() {
59226         var buf = [], o = {};
59227         var bp = this.form.baseParams;
59228         var p = this.options.params;
59229         Ext.apply(o, p, bp);
59230         var paramOrder = this.form.paramOrder;
59231         if(paramOrder){
59232             for(var i = 0, len = paramOrder.length; i < len; i++){
59233                 buf.push(o[paramOrder[i]]);
59234             }
59235         }else if(this.form.paramsAsHash){
59236             buf.push(o);
59237         }
59238         return buf;
59239     },
59240     // Direct actions have already been processed and therefore
59241     // we can directly set the result; Direct Actions do not have
59242     // a this.response property.
59243     processResponse: function(result) {
59244         this.result = result;
59245         return result;          
59246     }
59247 });
59248
59249 /**
59250  * @class Ext.form.Action.DirectSubmit
59251  * @extends Ext.form.Action.Submit
59252  * Provides Ext.direct support for submitting form data.
59253  * See {@link Ext.form.Action.DirectLoad}.
59254  */
59255 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
59256     constructor: function(form, opts) {
59257         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
59258     },
59259     type: 'directsubmit',
59260     // override of Submit
59261     run : function(){
59262         var o = this.options;
59263         if(o.clientValidation === false || this.form.isValid()){
59264             // tag on any additional params to be posted in the
59265             // form scope
59266             this.success.params = this.getParams();
59267             this.form.api.submit(this.form.el.dom, this.success, this);
59268         }else if (o.clientValidation !== false){ // client validation failed
59269             this.failureType = Ext.form.Action.CLIENT_INVALID;
59270             this.form.afterAction(this, false);
59271         }
59272     },
59273     
59274     getParams: function() {
59275         var o = {};
59276         var bp = this.form.baseParams;
59277         var p = this.options.params;
59278         Ext.apply(o, p, bp);
59279         return o;
59280     },    
59281     // Direct actions have already been processed and therefore
59282     // we can directly set the result; Direct Actions do not have
59283     // a this.response property.
59284     processResponse: function(result) {
59285         this.result = result;
59286         return result;          
59287     }
59288 });
59289
59290
59291 Ext.form.Action.ACTION_TYPES = {
59292     'load' : Ext.form.Action.Load,
59293     'submit' : Ext.form.Action.Submit,
59294     'directload': Ext.form.Action.DirectLoad,
59295     'directsubmit': Ext.form.Action.DirectSubmit
59296 };
59297 /**
59298  * @class Ext.form.VTypes
59299  * <p>This is a singleton object which contains a set of commonly used field validation functions.
59300  * The validations provided are basic and intended to be easily customizable and extended.</p>
59301  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
59302  * test function, and optionally specify any corresponding error text to display and any keystroke
59303  * filtering mask to apply. For example:</p>
59304  * <pre><code>
59305 // custom Vtype for vtype:'time'
59306 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
59307 Ext.apply(Ext.form.VTypes, {
59308     //  vtype validation function
59309     time: function(val, field) {
59310         return timeTest.test(val);
59311     },
59312     // vtype Text property: The error text to display when the validation function returns false
59313     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
59314     // vtype Mask property: The keystroke filter mask
59315     timeMask: /[\d\s:amp]/i
59316 });
59317  * </code></pre>
59318  * Another example: 
59319  * <pre><code>
59320 // custom Vtype for vtype:'IPAddress'
59321 Ext.apply(Ext.form.VTypes, {
59322     IPAddress:  function(v) {
59323         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
59324     },
59325     IPAddressText: 'Must be a numeric IP address',
59326     IPAddressMask: /[\d\.]/i
59327 });
59328  * </code></pre>
59329  * @singleton
59330  */
59331 Ext.form.VTypes = function(){
59332     // closure these in so they are only created once.
59333     var alpha = /^[a-zA-Z_]+$/;
59334     var alphanum = /^[a-zA-Z0-9_]+$/;
59335     var email = /^(\w+)([-+.][\w]+)*@(\w[-\w]*\.){1,5}([A-Za-z]){2,4}$/;
59336     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
59337
59338     // All these messages and functions are configurable
59339     return {
59340         /**
59341          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
59342          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
59343          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
59344          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a> 
59345          * for additional information.  This implementation is intended to validate the following emails:<tt>
59346          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
59347          * </tt>.
59348          * @param {String} value The email address
59349          * @return {Boolean} true if the RegExp test passed, and false if not.
59350          */
59351         'email' : function(v){
59352             return email.test(v);
59353         },
59354         /**
59355          * The error text to display when the email validation function returns false.  Defaults to:
59356          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
59357          * @type String
59358          */
59359         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
59360         /**
59361          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for 
59362          * information about more complex email validation. Defaults to:
59363          * <tt>/[a-z0-9_\.\-@]/i</tt>
59364          * @type RegExp
59365          */
59366         'emailMask' : /[a-z0-9_\.\-@]/i,
59367
59368         /**
59369          * The function used to validate URLs
59370          * @param {String} value The URL
59371          * @return {Boolean} true if the RegExp test passed, and false if not.
59372          */
59373         'url' : function(v){
59374             return url.test(v);
59375         },
59376         /**
59377          * The error text to display when the url validation function returns false.  Defaults to:
59378          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
59379          * @type String
59380          */
59381         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
59382         
59383         /**
59384          * The function used to validate alpha values
59385          * @param {String} value The value
59386          * @return {Boolean} true if the RegExp test passed, and false if not.
59387          */
59388         'alpha' : function(v){
59389             return alpha.test(v);
59390         },
59391         /**
59392          * The error text to display when the alpha validation function returns false.  Defaults to:
59393          * <tt>'This field should only contain letters and _'</tt>
59394          * @type String
59395          */
59396         'alphaText' : 'This field should only contain letters and _',
59397         /**
59398          * The keystroke filter mask to be applied on alpha input.  Defaults to:
59399          * <tt>/[a-z_]/i</tt>
59400          * @type RegExp
59401          */
59402         'alphaMask' : /[a-z_]/i,
59403
59404         /**
59405          * The function used to validate alphanumeric values
59406          * @param {String} value The value
59407          * @return {Boolean} true if the RegExp test passed, and false if not.
59408          */
59409         'alphanum' : function(v){
59410             return alphanum.test(v);
59411         },
59412         /**
59413          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
59414          * <tt>'This field should only contain letters, numbers and _'</tt>
59415          * @type String
59416          */
59417         'alphanumText' : 'This field should only contain letters, numbers and _',
59418         /**
59419          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
59420          * <tt>/[a-z0-9_]/i</tt>
59421          * @type RegExp
59422          */
59423         'alphanumMask' : /[a-z0-9_]/i
59424     };
59425 }();/**\r
59426  * @class Ext.grid.GridPanel\r
59427  * @extends Ext.Panel\r
59428  * <p>This class represents the primary interface of a component based grid control to represent data\r
59429  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>\r
59430  * <div class="mdetail-params"><ul>\r
59431  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)\r
59432  * <div class="sub-desc"></div></li>\r
59433  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup\r
59434  * <div class="sub-desc"></div></li>\r
59435  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface \r
59436  * <div class="sub-desc"></div></li>\r
59437  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior \r
59438  * <div class="sub-desc"></div></li>\r
59439  * </ul></div>\r
59440  * <p>Example usage:</p>\r
59441  * <pre><code>\r
59442 var grid = new Ext.grid.GridPanel({\r
59443     {@link #store}: new (@link Ext.data.Store}({\r
59444         {@link Ext.data.Store#autoDestroy autoDestroy}: true,\r
59445         {@link Ext.data.Store#reader reader}: reader,\r
59446         {@link Ext.data.Store#data data}: xg.dummyData\r
59447     }),\r
59448     {@link #columns}: [\r
59449         {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},\r
59450         {header: 'Price', width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},\r
59451         {header: 'Change', width: 120, sortable: true, dataIndex: 'change'},\r
59452         {header: '% Change', width: 120, sortable: true, dataIndex: 'pctChange'},\r
59453         // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype\r
59454         {header: 'Last Updated', width: 135, sortable: true, dataIndex: 'lastChange', xtype: 'datecolumn', format: 'M d, Y'}\r
59455     ],\r
59456     {@link #viewConfig}: {\r
59457         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
59458 \r
59459 //      Return CSS class to apply to rows depending upon data values\r
59460         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {\r
59461             var c = record.{@link Ext.data.Record#get get}('change');\r
59462             if (c < 0) {\r
59463                 return 'price-fall';\r
59464             } else if (c > 0) {\r
59465                 return 'price-rise';\r
59466             }\r
59467         }\r
59468     },\r
59469     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),\r
59470     width: 600,\r
59471     height: 300,\r
59472     frame: true,\r
59473     title: 'Framed with Row Selection and Horizontal Scrolling',\r
59474     iconCls: 'icon-grid'\r
59475 });\r
59476  * </code></pre>\r
59477  * <p><b><u>Notes:</u></b></p>\r
59478  * <div class="mdetail-params"><ul>\r
59479  * <li>Although this class inherits many configuration options from base classes, some of them\r
59480  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will\r
59481  * have no effect.</li>\r
59482  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to\r
59483  * scroll its rows. These dimensions can either be set explicitly through the\r
59484  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>\r
59485  * configuration options or implicitly set by using the grid as a child item of a\r
59486  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}\r
59487  * provide the sizing of its child items (for example the Container of the Grid may specify\r
59488  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>\r
59489  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated\r
59490  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>\r
59491  * </ul></div>\r
59492  * @constructor\r
59493  * @param {Object} config The config object\r
59494  * @xtype grid\r
59495  */\r
59496 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {\r
59497     /**\r
59498      * @cfg {String} autoExpandColumn\r
59499      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in\r
59500      * this grid that should expand to fill unused space. This value specified here can not\r
59501      * be <tt>0</tt>.</p>\r
59502      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with\r
59503      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>\r
59504      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>\r
59505      * for additional details.</p>\r
59506      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>\r
59507      */\r
59508     autoExpandColumn : false,\r
59509     /**\r
59510      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>\r
59511      * can have (if enabled). Defaults to <tt>1000</tt>.\r
59512      */\r
59513     autoExpandMax : 1000,\r
59514     /**\r
59515      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>\r
59516      * can have (if enabled). Defaults to <tt>50</tt>.\r
59517      */\r
59518     autoExpandMin : 50,\r
59519     /**\r
59520      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.\r
59521      * Default is <tt>false</tt>.\r
59522      */\r
59523     columnLines : false,\r
59524     /**\r
59525      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.\r
59526      */\r
59527     /**\r
59528      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).\r
59529      */\r
59530     /**\r
59531      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a\r
59532      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the\r
59533      * <tt>{@link #colModel}</tt> configuration property.\r
59534      */\r
59535     /**\r
59536      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.\r
59537      */\r
59538     /**\r
59539      * @cfg {String} ddText\r
59540      * Configures the text in the drag proxy.  Defaults to:\r
59541      * <pre><code>\r
59542      * ddText : '{0} selected row{1}'\r
59543      * </code></pre>\r
59544      * <tt>{0}</tt> is replaced with the number of selected rows.\r
59545      */\r
59546     ddText : '{0} selected row{1}',\r
59547     /**\r
59548      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>\r
59549      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row\r
59550      * structure deferred so that layouts with GridPanels appear more quickly.</p>\r
59551      */\r
59552     deferRowRender : true,\r
59553     /**\r
59554      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>\r
59555      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>\r
59556      */\r
59557     /**\r
59558      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.\r
59559      */\r
59560     /**\r
59561      * @cfg {Boolean} enableColumnHide Defaults to <tt>true</tt> to enable hiding of columns with the header context menu.\r
59562      */\r
59563     enableColumnHide : true,\r
59564     /**\r
59565      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>\r
59566      * to turn off column reordering via drag drop.\r
59567      */\r
59568     enableColumnMove : true,\r
59569     /**\r
59570      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>\r
59571      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to\r
59572      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>\r
59573      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>\r
59574      * property.</p>\r
59575      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of\r
59576      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},\r
59577      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able\r
59578      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>\r
59579      */\r
59580     enableDragDrop : false,\r
59581     /**\r
59582      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.\r
59583      */\r
59584     enableHdMenu : true,\r
59585     /**\r
59586      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.\r
59587      */\r
59588     /**\r
59589      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while\r
59590      * loading. Defaults to <code>false</code>.\r
59591      */\r
59592     loadMask : false,\r
59593     /**\r
59594      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.\r
59595      */\r
59596     /**\r
59597      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.\r
59598      */\r
59599     minColumnWidth : 25,\r
59600     /**\r
59601      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.\r
59602      */\r
59603     /**\r
59604      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide\r
59605      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).\r
59606      */\r
59607     /**\r
59608      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).\r
59609      */\r
59610     /**\r
59611      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.\r
59612      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of\r
59613      * the grid. A default CSS rule is provided which sets a background colour, but you can override this\r
59614      * with a rule which either overrides the <b>background-color</b> style using the '!important'\r
59615      * modifier, or which uses a CSS selector of higher specificity.</p>\r
59616      */\r
59617     stripeRows : false,\r
59618     /**\r
59619      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>\r
59620      * for GridPanel, but <tt>false</tt> for EditorGridPanel.\r
59621      */\r
59622     trackMouseOver : true,\r
59623     /**\r
59624      * @cfg {Array} stateEvents\r
59625      * An array of events that, when fired, should trigger this component to save its state.\r
59626      * Defaults to:<pre><code>\r
59627      * stateEvents: ['columnmove', 'columnresize', 'sortchange']\r
59628      * </code></pre>\r
59629      * <p>These can be any types of events supported by this component, including browser or\r
59630      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>\r
59631      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring\r
59632      * Component state.</p>\r
59633      */\r
59634     stateEvents : ['columnmove', 'columnresize', 'sortchange'],\r
59635     /**\r
59636      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set\r
59637      * before a call to {@link Ext.Component#render render()}.\r
59638      */\r
59639     view : null,\r
59640     /**\r
59641      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of\r
59642      * the config options available for {@link Ext.grid.GridView} can be specified here. This option\r
59643      * is ignored if <tt>{@link #view}</tt> is specified.\r
59644      */\r
59645 \r
59646     // private\r
59647     rendered : false,\r
59648     // private\r
59649     viewReady : false,\r
59650 \r
59651     // private\r
59652     initComponent : function(){\r
59653         Ext.grid.GridPanel.superclass.initComponent.call(this);\r
59654 \r
59655         if(this.columnLines){\r
59656             this.cls = (this.cls || '') + ' x-grid-with-col-lines';\r
59657         }\r
59658         // override any provided value since it isn't valid\r
59659         // and is causing too many bug reports ;)\r
59660         this.autoScroll = false;\r
59661         this.autoWidth = false;\r
59662 \r
59663         if(Ext.isArray(this.columns)){\r
59664             this.colModel = new Ext.grid.ColumnModel(this.columns);\r
59665             delete this.columns;\r
59666         }\r
59667 \r
59668         // check and correct shorthanded configs\r
59669         if(this.ds){\r
59670             this.store = this.ds;\r
59671             delete this.ds;\r
59672         }\r
59673         if(this.cm){\r
59674             this.colModel = this.cm;\r
59675             delete this.cm;\r
59676         }\r
59677         if(this.sm){\r
59678             this.selModel = this.sm;\r
59679             delete this.sm;\r
59680         }\r
59681         this.store = Ext.StoreMgr.lookup(this.store);\r
59682 \r
59683         this.addEvents(\r
59684             // raw events\r
59685             /**\r
59686              * @event click\r
59687              * The raw click event for the entire grid.\r
59688              * @param {Ext.EventObject} e\r
59689              */\r
59690             'click',\r
59691             /**\r
59692              * @event dblclick\r
59693              * The raw dblclick event for the entire grid.\r
59694              * @param {Ext.EventObject} e\r
59695              */\r
59696             'dblclick',\r
59697             /**\r
59698              * @event contextmenu\r
59699              * The raw contextmenu event for the entire grid.\r
59700              * @param {Ext.EventObject} e\r
59701              */\r
59702             'contextmenu',\r
59703             /**\r
59704              * @event mousedown\r
59705              * The raw mousedown event for the entire grid.\r
59706              * @param {Ext.EventObject} e\r
59707              */\r
59708             'mousedown',\r
59709             /**\r
59710              * @event mouseup\r
59711              * The raw mouseup event for the entire grid.\r
59712              * @param {Ext.EventObject} e\r
59713              */\r
59714             'mouseup',\r
59715             /**\r
59716              * @event mouseover\r
59717              * The raw mouseover event for the entire grid.\r
59718              * @param {Ext.EventObject} e\r
59719              */\r
59720             'mouseover',\r
59721             /**\r
59722              * @event mouseout\r
59723              * The raw mouseout event for the entire grid.\r
59724              * @param {Ext.EventObject} e\r
59725              */\r
59726             'mouseout',\r
59727             /**\r
59728              * @event keypress\r
59729              * The raw keypress event for the entire grid.\r
59730              * @param {Ext.EventObject} e\r
59731              */\r
59732             'keypress',\r
59733             /**\r
59734              * @event keydown\r
59735              * The raw keydown event for the entire grid.\r
59736              * @param {Ext.EventObject} e\r
59737              */\r
59738             'keydown',\r
59739 \r
59740             // custom events\r
59741             /**\r
59742              * @event cellmousedown\r
59743              * Fires before a cell is clicked\r
59744              * @param {Grid} this\r
59745              * @param {Number} rowIndex\r
59746              * @param {Number} columnIndex\r
59747              * @param {Ext.EventObject} e\r
59748              */\r
59749             'cellmousedown',\r
59750             /**\r
59751              * @event rowmousedown\r
59752              * Fires before a row is clicked\r
59753              * @param {Grid} this\r
59754              * @param {Number} rowIndex\r
59755              * @param {Ext.EventObject} e\r
59756              */\r
59757             'rowmousedown',\r
59758             /**\r
59759              * @event headermousedown\r
59760              * Fires before a header is clicked\r
59761              * @param {Grid} this\r
59762              * @param {Number} columnIndex\r
59763              * @param {Ext.EventObject} e\r
59764              */\r
59765             'headermousedown',\r
59766 \r
59767             /**\r
59768              * @event cellclick\r
59769              * Fires when a cell is clicked.\r
59770              * The data for the cell is drawn from the {@link Ext.data.Record Record}\r
59771              * for this row. To access the data in the listener function use the\r
59772              * following technique:\r
59773              * <pre><code>\r
59774 function(grid, rowIndex, columnIndex, e) {\r
59775     var record = grid.getStore().getAt(rowIndex);  // Get the Record\r
59776     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name\r
59777     var data = record.get(fieldName);\r
59778 }\r
59779 </code></pre>\r
59780              * @param {Grid} this\r
59781              * @param {Number} rowIndex\r
59782              * @param {Number} columnIndex\r
59783              * @param {Ext.EventObject} e\r
59784              */\r
59785             'cellclick',\r
59786             /**\r
59787              * @event celldblclick\r
59788              * Fires when a cell is double clicked\r
59789              * @param {Grid} this\r
59790              * @param {Number} rowIndex\r
59791              * @param {Number} columnIndex\r
59792              * @param {Ext.EventObject} e\r
59793              */\r
59794             'celldblclick',\r
59795             /**\r
59796              * @event rowclick\r
59797              * Fires when a row is clicked\r
59798              * @param {Grid} this\r
59799              * @param {Number} rowIndex\r
59800              * @param {Ext.EventObject} e\r
59801              */\r
59802             'rowclick',\r
59803             /**\r
59804              * @event rowdblclick\r
59805              * Fires when a row is double clicked\r
59806              * @param {Grid} this\r
59807              * @param {Number} rowIndex\r
59808              * @param {Ext.EventObject} e\r
59809              */\r
59810             'rowdblclick',\r
59811             /**\r
59812              * @event headerclick\r
59813              * Fires when a header is clicked\r
59814              * @param {Grid} this\r
59815              * @param {Number} columnIndex\r
59816              * @param {Ext.EventObject} e\r
59817              */\r
59818             'headerclick',\r
59819             /**\r
59820              * @event headerdblclick\r
59821              * Fires when a header cell is double clicked\r
59822              * @param {Grid} this\r
59823              * @param {Number} columnIndex\r
59824              * @param {Ext.EventObject} e\r
59825              */\r
59826             'headerdblclick',\r
59827             /**\r
59828              * @event rowcontextmenu\r
59829              * Fires when a row is right clicked\r
59830              * @param {Grid} this\r
59831              * @param {Number} rowIndex\r
59832              * @param {Ext.EventObject} e\r
59833              */\r
59834             'rowcontextmenu',\r
59835             /**\r
59836              * @event cellcontextmenu\r
59837              * Fires when a cell is right clicked\r
59838              * @param {Grid} this\r
59839              * @param {Number} rowIndex\r
59840              * @param {Number} cellIndex\r
59841              * @param {Ext.EventObject} e\r
59842              */\r
59843             'cellcontextmenu',\r
59844             /**\r
59845              * @event headercontextmenu\r
59846              * Fires when a header is right clicked\r
59847              * @param {Grid} this\r
59848              * @param {Number} columnIndex\r
59849              * @param {Ext.EventObject} e\r
59850              */\r
59851             'headercontextmenu',\r
59852             /**\r
59853              * @event bodyscroll\r
59854              * Fires when the body element is scrolled\r
59855              * @param {Number} scrollLeft\r
59856              * @param {Number} scrollTop\r
59857              */\r
59858             'bodyscroll',\r
59859             /**\r
59860              * @event columnresize\r
59861              * Fires when the user resizes a column\r
59862              * @param {Number} columnIndex\r
59863              * @param {Number} newSize\r
59864              */\r
59865             'columnresize',\r
59866             /**\r
59867              * @event columnmove\r
59868              * Fires when the user moves a column\r
59869              * @param {Number} oldIndex\r
59870              * @param {Number} newIndex\r
59871              */\r
59872             'columnmove',\r
59873             /**\r
59874              * @event sortchange\r
59875              * Fires when the grid's store sort changes\r
59876              * @param {Grid} this\r
59877              * @param {Object} sortInfo An object with the keys field and direction\r
59878              */\r
59879             'sortchange',\r
59880             /**\r
59881              * @event reconfigure\r
59882              * Fires when the grid is reconfigured with a new store and/or column model.\r
59883              * @param {Grid} this\r
59884              * @param {Ext.data.Store} store The new store\r
59885              * @param {Ext.grid.ColumnModel} colModel The new column model\r
59886              */\r
59887             'reconfigure'\r
59888         );\r
59889     },\r
59890 \r
59891     // private\r
59892     onRender : function(ct, position){\r
59893         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);\r
59894 \r
59895         var c = this.body;\r
59896 \r
59897         this.el.addClass('x-grid-panel');\r
59898 \r
59899         var view = this.getView();\r
59900         view.init(this);\r
59901 \r
59902         this.mon(c, {\r
59903             mousedown: this.onMouseDown,\r
59904             click: this.onClick,\r
59905             dblclick: this.onDblClick,\r
59906             contextmenu: this.onContextMenu,\r
59907             keydown: this.onKeyDown,\r
59908             scope: this\r
59909         });\r
59910 \r
59911         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress']);\r
59912 \r
59913         this.getSelectionModel().init(this);\r
59914         this.view.render();\r
59915     },\r
59916 \r
59917     // private\r
59918     initEvents : function(){\r
59919         Ext.grid.GridPanel.superclass.initEvents.call(this);\r
59920 \r
59921         if(this.loadMask){\r
59922             this.loadMask = new Ext.LoadMask(this.bwrap,\r
59923                     Ext.apply({store:this.store}, this.loadMask));\r
59924         }\r
59925     },\r
59926 \r
59927     initStateEvents : function(){\r
59928         Ext.grid.GridPanel.superclass.initStateEvents.call(this);\r
59929         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});\r
59930     },\r
59931 \r
59932     applyState : function(state){\r
59933         var cm = this.colModel;\r
59934         var cs = state.columns;\r
59935         if(cs){\r
59936             for(var i = 0, len = cs.length; i < len; i++){\r
59937                 var s = cs[i];\r
59938                 var c = cm.getColumnById(s.id);\r
59939                 if(c){\r
59940                     c.hidden = s.hidden;\r
59941                     c.width = s.width;\r
59942                     var oldIndex = cm.getIndexById(s.id);\r
59943                     if(oldIndex != i){\r
59944                         cm.moveColumn(oldIndex, i);\r
59945                     }\r
59946                 }\r
59947             }\r
59948         }\r
59949         if(state.sort && this.store){\r
59950             this.store[this.store.remoteSort ? 'setDefaultSort' : 'sort'](state.sort.field, state.sort.direction);\r
59951         }\r
59952         delete state.columns;\r
59953         delete state.sort;\r
59954         Ext.grid.GridPanel.superclass.applyState.call(this, state);\r
59955     },\r
59956 \r
59957     getState : function(){\r
59958         var o = {columns: []};\r
59959         for(var i = 0, c; (c = this.colModel.config[i]); i++){\r
59960             o.columns[i] = {\r
59961                 id: c.id,\r
59962                 width: c.width\r
59963             };\r
59964             if(c.hidden){\r
59965                 o.columns[i].hidden = true;\r
59966             }\r
59967         }\r
59968         if(this.store){\r
59969             var ss = this.store.getSortState();\r
59970             if(ss){\r
59971                 o.sort = ss;\r
59972             }\r
59973         }\r
59974         return o;\r
59975     },\r
59976 \r
59977     // private\r
59978     afterRender : function(){\r
59979         Ext.grid.GridPanel.superclass.afterRender.call(this);\r
59980         this.view.layout();\r
59981         if(this.deferRowRender){\r
59982             this.view.afterRender.defer(10, this.view);\r
59983         }else{\r
59984             this.view.afterRender();\r
59985         }\r
59986         this.viewReady = true;\r
59987     },\r
59988 \r
59989     /**\r
59990      * <p>Reconfigures the grid to use a different Store and Column Model\r
59991      * and fires the 'reconfigure' event. The View will be bound to the new\r
59992      * objects and refreshed.</p>\r
59993      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become\r
59994      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the\r
59995      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound\r
59996      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring\r
59997      * with the new data.</p>\r
59998      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object\r
59999      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object\r
60000      */\r
60001     reconfigure : function(store, colModel){\r
60002         if(this.loadMask){\r
60003             this.loadMask.destroy();\r
60004             this.loadMask = new Ext.LoadMask(this.bwrap,\r
60005                     Ext.apply({}, {store:store}, this.initialConfig.loadMask));\r
60006         }\r
60007         this.view.initData(store, colModel);\r
60008         this.store = store;\r
60009         this.colModel = colModel;\r
60010         if(this.rendered){\r
60011             this.view.refresh(true);\r
60012         }\r
60013         this.fireEvent('reconfigure', this, store, colModel);\r
60014     },\r
60015 \r
60016     // private\r
60017     onKeyDown : function(e){\r
60018         this.fireEvent('keydown', e);\r
60019     },\r
60020 \r
60021     // private\r
60022     onDestroy : function(){\r
60023         if(this.rendered){\r
60024             var c = this.body;\r
60025             c.removeAllListeners();\r
60026             c.update('');\r
60027             Ext.destroy(this.view, this.loadMask);\r
60028         }else if(this.store && this.store.autoDestroy){\r
60029             this.store.destroy();\r
60030         }\r
60031         Ext.destroy(this.colModel, this.selModel);\r
60032         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;\r
60033         Ext.grid.GridPanel.superclass.onDestroy.call(this);\r
60034     },\r
60035 \r
60036     // private\r
60037     processEvent : function(name, e){\r
60038         this.fireEvent(name, e);\r
60039         var t = e.getTarget();\r
60040         var v = this.view;\r
60041         var header = v.findHeaderIndex(t);\r
60042         if(header !== false){\r
60043             this.fireEvent('header' + name, this, header, e);\r
60044         }else{\r
60045             var row = v.findRowIndex(t);\r
60046             var cell = v.findCellIndex(t);\r
60047             if(row !== false){\r
60048                 this.fireEvent('row' + name, this, row, e);\r
60049                 if(cell !== false){\r
60050                     this.fireEvent('cell' + name, this, row, cell, e);\r
60051                 }\r
60052             }\r
60053         }\r
60054     },\r
60055 \r
60056     // private\r
60057     onClick : function(e){\r
60058         this.processEvent('click', e);\r
60059     },\r
60060 \r
60061     // private\r
60062     onMouseDown : function(e){\r
60063         this.processEvent('mousedown', e);\r
60064     },\r
60065 \r
60066     // private\r
60067     onContextMenu : function(e, t){\r
60068         this.processEvent('contextmenu', e);\r
60069     },\r
60070 \r
60071     // private\r
60072     onDblClick : function(e){\r
60073         this.processEvent('dblclick', e);\r
60074     },\r
60075 \r
60076     // private\r
60077     walkCells : function(row, col, step, fn, scope){\r
60078         var cm = this.colModel, clen = cm.getColumnCount();\r
60079         var ds = this.store, rlen = ds.getCount(), first = true;\r
60080         if(step < 0){\r
60081             if(col < 0){\r
60082                 row--;\r
60083                 first = false;\r
60084             }\r
60085             while(row >= 0){\r
60086                 if(!first){\r
60087                     col = clen-1;\r
60088                 }\r
60089                 first = false;\r
60090                 while(col >= 0){\r
60091                     if(fn.call(scope || this, row, col, cm) === true){\r
60092                         return [row, col];\r
60093                     }\r
60094                     col--;\r
60095                 }\r
60096                 row--;\r
60097             }\r
60098         } else {\r
60099             if(col >= clen){\r
60100                 row++;\r
60101                 first = false;\r
60102             }\r
60103             while(row < rlen){\r
60104                 if(!first){\r
60105                     col = 0;\r
60106                 }\r
60107                 first = false;\r
60108                 while(col < clen){\r
60109                     if(fn.call(scope || this, row, col, cm) === true){\r
60110                         return [row, col];\r
60111                     }\r
60112                     col++;\r
60113                 }\r
60114                 row++;\r
60115             }\r
60116         }\r
60117         return null;\r
60118     },\r
60119 \r
60120     // private\r
60121     onResize : function(){\r
60122         Ext.grid.GridPanel.superclass.onResize.apply(this, arguments);\r
60123         if(this.viewReady){\r
60124             this.view.layout();\r
60125         }\r
60126     },\r
60127 \r
60128     /**\r
60129      * Returns the grid's underlying element.\r
60130      * @return {Element} The element\r
60131      */\r
60132     getGridEl : function(){\r
60133         return this.body;\r
60134     },\r
60135 \r
60136     // private for compatibility, overridden by editor grid\r
60137     stopEditing : Ext.emptyFn,\r
60138 \r
60139     /**\r
60140      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>\r
60141      * configuration option. If no selection model was configured, this will create\r
60142      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.\r
60143      * @return {SelectionModel}\r
60144      */\r
60145     getSelectionModel : function(){\r
60146         if(!this.selModel){\r
60147             this.selModel = new Ext.grid.RowSelectionModel(\r
60148                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);\r
60149         }\r
60150         return this.selModel;\r
60151     },\r
60152 \r
60153     /**\r
60154      * Returns the grid's data store.\r
60155      * @return {Ext.data.Store} The store\r
60156      */\r
60157     getStore : function(){\r
60158         return this.store;\r
60159     },\r
60160 \r
60161     /**\r
60162      * Returns the grid's ColumnModel.\r
60163      * @return {Ext.grid.ColumnModel} The column model\r
60164      */\r
60165     getColumnModel : function(){\r
60166         return this.colModel;\r
60167     },\r
60168 \r
60169     /**\r
60170      * Returns the grid's GridView object.\r
60171      * @return {Ext.grid.GridView} The grid view\r
60172      */\r
60173     getView : function(){\r
60174         if(!this.view){\r
60175             this.view = new Ext.grid.GridView(this.viewConfig);\r
60176         }\r
60177         return this.view;\r
60178     },\r
60179     /**\r
60180      * Called to get grid's drag proxy text, by default returns this.ddText.\r
60181      * @return {String} The text\r
60182      */\r
60183     getDragDropText : function(){\r
60184         var count = this.selModel.getCount();\r
60185         return String.format(this.ddText, count, count == 1 ? '' : 's');\r
60186     }\r
60187 \r
60188     /** \r
60189      * @cfg {String/Number} activeItem \r
60190      * @hide \r
60191      */\r
60192     /** \r
60193      * @cfg {Boolean} autoDestroy \r
60194      * @hide \r
60195      */\r
60196     /** \r
60197      * @cfg {Object/String/Function} autoLoad \r
60198      * @hide \r
60199      */\r
60200     /** \r
60201      * @cfg {Boolean} autoWidth \r
60202      * @hide \r
60203      */\r
60204     /** \r
60205      * @cfg {Boolean/Number} bufferResize \r
60206      * @hide \r
60207      */\r
60208     /** \r
60209      * @cfg {String} defaultType \r
60210      * @hide \r
60211      */\r
60212     /** \r
60213      * @cfg {Object} defaults \r
60214      * @hide \r
60215      */\r
60216     /** \r
60217      * @cfg {Boolean} hideBorders \r
60218      * @hide \r
60219      */\r
60220     /** \r
60221      * @cfg {Mixed} items \r
60222      * @hide \r
60223      */\r
60224     /** \r
60225      * @cfg {String} layout \r
60226      * @hide \r
60227      */\r
60228     /** \r
60229      * @cfg {Object} layoutConfig \r
60230      * @hide \r
60231      */\r
60232     /** \r
60233      * @cfg {Boolean} monitorResize \r
60234      * @hide \r
60235      */\r
60236     /** \r
60237      * @property items \r
60238      * @hide \r
60239      */\r
60240     /** \r
60241      * @method add \r
60242      * @hide \r
60243      */\r
60244     /** \r
60245      * @method cascade \r
60246      * @hide \r
60247      */\r
60248     /** \r
60249      * @method doLayout \r
60250      * @hide \r
60251      */\r
60252     /** \r
60253      * @method find \r
60254      * @hide \r
60255      */\r
60256     /** \r
60257      * @method findBy \r
60258      * @hide \r
60259      */\r
60260     /** \r
60261      * @method findById \r
60262      * @hide \r
60263      */\r
60264     /** \r
60265      * @method findByType \r
60266      * @hide \r
60267      */\r
60268     /** \r
60269      * @method getComponent \r
60270      * @hide \r
60271      */\r
60272     /** \r
60273      * @method getLayout \r
60274      * @hide \r
60275      */\r
60276     /** \r
60277      * @method getUpdater \r
60278      * @hide \r
60279      */\r
60280     /** \r
60281      * @method insert \r
60282      * @hide \r
60283      */\r
60284     /** \r
60285      * @method load \r
60286      * @hide \r
60287      */\r
60288     /** \r
60289      * @method remove \r
60290      * @hide \r
60291      */\r
60292     /** \r
60293      * @event add \r
60294      * @hide \r
60295      */\r
60296     /** \r
60297      * @event afterLayout \r
60298      * @hide \r
60299      */\r
60300     /** \r
60301      * @event beforeadd \r
60302      * @hide \r
60303      */\r
60304     /** \r
60305      * @event beforeremove \r
60306      * @hide \r
60307      */\r
60308     /** \r
60309      * @event remove \r
60310      * @hide \r
60311      */\r
60312 \r
60313 \r
60314 \r
60315     /**\r
60316      * @cfg {String} allowDomMove  @hide\r
60317      */\r
60318     /**\r
60319      * @cfg {String} autoEl @hide\r
60320      */\r
60321     /**\r
60322      * @cfg {String} applyTo  @hide\r
60323      */\r
60324     /**\r
60325      * @cfg {String} autoScroll  @hide\r
60326      */\r
60327     /**\r
60328      * @cfg {String} bodyBorder  @hide\r
60329      */\r
60330     /**\r
60331      * @cfg {String} bodyStyle  @hide\r
60332      */\r
60333     /**\r
60334      * @cfg {String} contentEl  @hide\r
60335      */\r
60336     /**\r
60337      * @cfg {String} disabledClass  @hide\r
60338      */\r
60339     /**\r
60340      * @cfg {String} elements  @hide\r
60341      */\r
60342     /**\r
60343      * @cfg {String} html  @hide\r
60344      */\r
60345     /**\r
60346      * @cfg {Boolean} preventBodyReset\r
60347      * @hide\r
60348      */\r
60349     /**\r
60350      * @property disabled\r
60351      * @hide\r
60352      */\r
60353     /**\r
60354      * @method applyToMarkup\r
60355      * @hide\r
60356      */\r
60357     /**\r
60358      * @method enable\r
60359      * @hide\r
60360      */\r
60361     /**\r
60362      * @method disable\r
60363      * @hide\r
60364      */\r
60365     /**\r
60366      * @method setDisabled\r
60367      * @hide\r
60368      */\r
60369 });\r
60370 Ext.reg('grid', Ext.grid.GridPanel);/**
60371  * @class Ext.grid.GridView
60372  * @extends Ext.util.Observable
60373  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
60374  * Methods of this class may be used to access user interface elements to enable
60375  * special display effects. Do not change the DOM structure of the user interface.</p>
60376  * <p>This class does not provide ways to manipulate the underlying data. The data
60377  * model of a Grid is held in an {@link Ext.data.Store}.</p>
60378  * @constructor
60379  * @param {Object} config
60380  */
60381 Ext.grid.GridView = function(config){
60382     Ext.apply(this, config);
60383     // These events are only used internally by the grid components
60384     this.addEvents(
60385         /**
60386          * @event beforerowremoved
60387          * Internal UI Event. Fired before a row is removed.
60388          * @param {Ext.grid.GridView} view
60389          * @param {Number} rowIndex The index of the row to be removed.
60390          * @param {Ext.data.Record} record The Record to be removed
60391          */
60392         "beforerowremoved",
60393         /**
60394          * @event beforerowsinserted
60395          * Internal UI Event. Fired before rows are inserted.
60396          * @param {Ext.grid.GridView} view
60397          * @param {Number} firstRow The index of the first row to be inserted.
60398          * @param {Number} lastRow The index of the last row to be inserted.
60399          */
60400         "beforerowsinserted",
60401         /**
60402          * @event beforerefresh
60403          * Internal UI Event. Fired before the view is refreshed.
60404          * @param {Ext.grid.GridView} view
60405          */
60406         "beforerefresh",
60407         /**
60408          * @event rowremoved
60409          * Internal UI Event. Fired after a row is removed.
60410          * @param {Ext.grid.GridView} view
60411          * @param {Number} rowIndex The index of the row that was removed.
60412          * @param {Ext.data.Record} record The Record that was removed
60413          */
60414         "rowremoved",
60415         /**
60416          * @event rowsinserted
60417          * Internal UI Event. Fired after rows are inserted.
60418          * @param {Ext.grid.GridView} view
60419          * @param {Number} firstRow The index of the first inserted.
60420          * @param {Number} lastRow The index of the last row inserted.
60421          */
60422         "rowsinserted",
60423         /**
60424          * @event rowupdated
60425          * Internal UI Event. Fired after a row has been updated.
60426          * @param {Ext.grid.GridView} view
60427          * @param {Number} firstRow The index of the row updated.
60428          * @param {Ext.data.record} record The Record backing the row updated.
60429          */
60430         "rowupdated",
60431         /**
60432          * @event refresh
60433          * Internal UI Event. Fired after the GridView's body has been refreshed.
60434          * @param {Ext.grid.GridView} view
60435          */
60436         "refresh"
60437     );
60438     Ext.grid.GridView.superclass.constructor.call(this);
60439 };
60440
60441 Ext.extend(Ext.grid.GridView, Ext.util.Observable, {
60442     /**
60443      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
60444      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
60445      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
60446      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
60447      * (e.g., 'my-class another-class'). Example usage:
60448     <pre><code>
60449 viewConfig: {
60450     forceFit: true,
60451     showPreview: true, // custom property
60452     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
60453     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
60454         if(this.showPreview){
60455             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
60456             return 'x-grid3-row-expanded';
60457         }
60458         return 'x-grid3-row-collapsed';
60459     }
60460 },     
60461     </code></pre>
60462      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
60463      * @param {Number} index The row index.
60464      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
60465      * customization of various aspects of a grid row.
60466      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
60467      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
60468      * <ul>
60469      * <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>
60470      * <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>
60471      * </ul>
60472      * The following property will be passed in, and may be appended to:
60473      * <ul>
60474      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
60475      * both the standard grid row, and any expansion row.</div></li>
60476      * </ul>
60477      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
60478      * @method getRowClass
60479      * @return {String} a CSS class name to add to the row.
60480      */
60481     /**
60482      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
60483      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
60484      */
60485     /**
60486      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
60487      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
60488     <pre><code>
60489     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
60490     </code></pre>
60491      */
60492     /**
60493      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>). 
60494      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
60495      * config to disable the <i>menu</i> for individual columns.  While this config is true the
60496      * following will be disabled:<div class="mdetail-params"><ul>
60497      * <li>clicking on header to sort</li>
60498      * <li>the trigger to reveal the menu.</li>
60499      * </ul></div>
60500      */
60501     /**
60502      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
60503      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
60504      * See {@link Ext.grid.GridDragZone} for details.</p>
60505      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
60506      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
60507      * <li><i>after</i> the owning GridPanel has been rendered.</li>
60508      * </ul></div>
60509      * @property dragZone
60510      * @type {Ext.grid.GridDragZone}
60511      */
60512     /**
60513      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
60514      * first load (defaults to <tt>true</tt>).
60515      */
60516     deferEmptyText : true,
60517     /**
60518      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
60519      * (defaults to <tt>19</tt> pixels).
60520      */
60521     scrollOffset : 19,
60522     /**
60523      * @cfg {Boolean} autoFill
60524      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
60525      * when the grid is <b>initially rendered</b>.  The 
60526      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
60527      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
60528      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
60529      * See <tt>{@link #forceFit}</tt> also.
60530      */
60531     autoFill : false,
60532     /**
60533      * @cfg {Boolean} forceFit
60534      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
60535      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
60536      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
60537      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
60538      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
60539      */
60540     forceFit : false,
60541     /**
60542      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
60543      */
60544     sortClasses : ["sort-asc", "sort-desc"],
60545     /**
60546      * @cfg {String} sortAscText The text displayed in the "Sort Ascending" menu item (defaults to <tt>"Sort Ascending"</tt>)
60547      */
60548     sortAscText : "Sort Ascending",
60549     /**
60550      * @cfg {String} sortDescText The text displayed in the "Sort Descending" menu item (defaults to <tt>"Sort Descending"</tt>)
60551      */
60552     sortDescText : "Sort Descending",
60553     /**
60554      * @cfg {String} columnsText The text displayed in the "Columns" menu item (defaults to <tt>"Columns"</tt>)
60555      */
60556     columnsText : "Columns",
60557
60558     /**
60559      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>"x-grid3-row-selected"</tt>). An
60560      * example overriding the default styling:
60561     <pre><code>
60562     .x-grid3-row-selected {background-color: yellow;}
60563     </code></pre>
60564      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
60565      * facets (like text) use something like:
60566     <pre><code>
60567     .x-grid3-row-selected .x-grid3-cell-inner {
60568         color: #FFCC00;
60569     }
60570     </code></pre>
60571      * @type String
60572      */
60573     selectedRowClass : "x-grid3-row-selected",
60574
60575     // private
60576     borderWidth : 2,
60577     tdClass : 'x-grid3-cell',
60578     hdCls : 'x-grid3-hd',
60579     markDirty : true,
60580
60581     /**
60582      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
60583      */
60584     cellSelectorDepth : 4,
60585     /**
60586      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
60587      */
60588     rowSelectorDepth : 10,
60589
60590     /**
60591      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
60592      */
60593     cellSelector : 'td.x-grid3-cell',
60594     /**
60595      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
60596      */
60597     rowSelector : 'div.x-grid3-row',
60598     
60599     // private
60600     firstRowCls: 'x-grid3-row-first',
60601     lastRowCls: 'x-grid3-row-last',
60602     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
60603
60604     /* -------------------------------- UI Specific ----------------------------- */
60605
60606     // private
60607     initTemplates : function(){
60608         var ts = this.templates || {};
60609         if(!ts.master){
60610             ts.master = new Ext.Template(
60611                     '<div class="x-grid3" hidefocus="true">',
60612                         '<div class="x-grid3-viewport">',
60613                             '<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>',
60614                             '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
60615                         '</div>',
60616                         '<div class="x-grid3-resize-marker">&#160;</div>',
60617                         '<div class="x-grid3-resize-proxy">&#160;</div>',
60618                     '</div>'
60619                     );
60620         }
60621
60622         if(!ts.header){
60623             ts.header = new Ext.Template(
60624                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
60625                     '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
60626                     '</table>'
60627                     );
60628         }
60629
60630         if(!ts.hcell){
60631             ts.hcell = new Ext.Template(
60632                     '<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>' : '',
60633                     '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
60634                     '</div></td>'
60635                     );
60636         }
60637
60638         if(!ts.body){
60639             ts.body = new Ext.Template('{rows}');
60640         }
60641
60642         if(!ts.row){
60643             ts.row = new Ext.Template(
60644                     '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
60645                     '<tbody><tr>{cells}</tr>',
60646                     (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>' : ''),
60647                     '</tbody></table></div>'
60648                     );
60649         }
60650
60651         if(!ts.cell){
60652             ts.cell = new Ext.Template(
60653                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
60654                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
60655                     '</td>'
60656                     );
60657         }
60658
60659         for(var k in ts){
60660             var t = ts[k];
60661             if(t && typeof t.compile == 'function' && !t.compiled){
60662                 t.disableFormats = true;
60663                 t.compile();
60664             }
60665         }
60666
60667         this.templates = ts;
60668         this.colRe = new RegExp("x-grid3-td-([^\\s]+)", "");
60669     },
60670
60671     // private
60672     fly : function(el){
60673         if(!this._flyweight){
60674             this._flyweight = new Ext.Element.Flyweight(document.body);
60675         }
60676         this._flyweight.dom = el;
60677         return this._flyweight;
60678     },
60679
60680     // private
60681     getEditorParent : function(){
60682         return this.scroller.dom;
60683     },
60684
60685     // private
60686     initElements : function(){
60687         var E = Ext.Element;
60688
60689         var el = this.grid.getGridEl().dom.firstChild;
60690         var cs = el.childNodes;
60691
60692         this.el = new E(el);
60693
60694         this.mainWrap = new E(cs[0]);
60695         this.mainHd = new E(this.mainWrap.dom.firstChild);
60696
60697         if(this.grid.hideHeaders){
60698             this.mainHd.setDisplayed(false);
60699         }
60700
60701         this.innerHd = this.mainHd.dom.firstChild;
60702         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
60703         if(this.forceFit){
60704             this.scroller.setStyle('overflow-x', 'hidden');
60705         }
60706         /**
60707          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
60708          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
60709          * @type Ext.Element
60710          * @property mainBody
60711          */
60712         this.mainBody = new E(this.scroller.dom.firstChild);
60713
60714         this.focusEl = new E(this.scroller.dom.childNodes[1]);
60715         this.focusEl.swallowEvent("click", true);
60716
60717         this.resizeMarker = new E(cs[1]);
60718         this.resizeProxy = new E(cs[2]);
60719     },
60720
60721     // private
60722     getRows : function(){
60723         return this.hasRows() ? this.mainBody.dom.childNodes : [];
60724     },
60725
60726     // finder methods, used with delegation
60727
60728     // private
60729     findCell : function(el){
60730         if(!el){
60731             return false;
60732         }
60733         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
60734     },
60735
60736 /**
60737  * <p>Return the index of the grid column which contains the passed element.</p>
60738  * See also {@link #findRowIndex}
60739  * @param {Element} el The target element
60740  * @return The column index, or <b>false</b> if the target element is not within a row of this GridView.
60741  */
60742     findCellIndex : function(el, requiredCls){
60743         var cell = this.findCell(el);
60744         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
60745             return this.getCellIndex(cell);
60746         }
60747         return false;
60748     },
60749
60750     // private
60751     getCellIndex : function(el){
60752         if(el){
60753             var m = el.className.match(this.colRe);
60754             if(m && m[1]){
60755                 return this.cm.getIndexById(m[1]);
60756             }
60757         }
60758         return false;
60759     },
60760
60761     // private
60762     findHeaderCell : function(el){
60763         var cell = this.findCell(el);
60764         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
60765     },
60766
60767     // private
60768     findHeaderIndex : function(el){
60769         return this.findCellIndex(el, this.hdCls);
60770     },
60771
60772 /**
60773  * Return the HtmlElement representing the grid row which contains the passed element.
60774  * @param {Element} el The target element
60775  * @return The row element, or null if the target element is not within a row of this GridView.
60776  */
60777     findRow : function(el){
60778         if(!el){
60779             return false;
60780         }
60781         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
60782     },
60783
60784 /**
60785  * <p>Return the index of the grid row which contains the passed element.</p>
60786  * See also {@link #findCellIndex}
60787  * @param {Element} el The target element
60788  * @return The row index, or <b>false</b> if the target element is not within a row of this GridView.
60789  */
60790     findRowIndex : function(el){
60791         var r = this.findRow(el);
60792         return r ? r.rowIndex : false;
60793     },
60794
60795     // getter methods for fetching elements dynamically in the grid
60796
60797 /**
60798  * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
60799  * @param {Number} index The row index
60800  * @return {HtmlElement} The div element.
60801  */
60802     getRow : function(row){
60803         return this.getRows()[row];
60804     },
60805
60806 /**
60807  * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
60808  * @param {Number} row The row index in which to find the cell.
60809  * @param {Number} col The column index of the cell.
60810  * @return {HtmlElement} The td at the specified coordinates.
60811  */
60812     getCell : function(row, col){
60813         return this.getRow(row).getElementsByTagName('td')[col];
60814     },
60815
60816 /**
60817  * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
60818  * @param {Number} index The column index
60819  * @return {HtmlElement} The td element.
60820  */
60821     getHeaderCell : function(index){
60822       return this.mainHd.dom.getElementsByTagName('td')[index];
60823     },
60824
60825     // manipulating elements
60826
60827     // private - use getRowClass to apply custom row classes
60828     addRowClass : function(row, cls){
60829         var r = this.getRow(row);
60830         if(r){
60831             this.fly(r).addClass(cls);
60832         }
60833     },
60834
60835     // private
60836     removeRowClass : function(row, cls){
60837         var r = this.getRow(row);
60838         if(r){
60839             this.fly(r).removeClass(cls);
60840         }
60841     },
60842
60843     // private
60844     removeRow : function(row){
60845         Ext.removeNode(this.getRow(row));
60846         this.syncFocusEl(row);
60847     },
60848     
60849     // private
60850     removeRows : function(firstRow, lastRow){
60851         var bd = this.mainBody.dom;
60852         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
60853             Ext.removeNode(bd.childNodes[firstRow]);
60854         }
60855         this.syncFocusEl(firstRow);
60856     },
60857
60858     // scrolling stuff
60859
60860     // private
60861     getScrollState : function(){
60862         var sb = this.scroller.dom;
60863         return {left: sb.scrollLeft, top: sb.scrollTop};
60864     },
60865
60866     // private
60867     restoreScroll : function(state){
60868         var sb = this.scroller.dom;
60869         sb.scrollLeft = state.left;
60870         sb.scrollTop = state.top;
60871     },
60872
60873     /**
60874      * Scrolls the grid to the top
60875      */
60876     scrollToTop : function(){
60877         this.scroller.dom.scrollTop = 0;
60878         this.scroller.dom.scrollLeft = 0;
60879     },
60880
60881     // private
60882     syncScroll : function(){
60883       this.syncHeaderScroll();
60884       var mb = this.scroller.dom;
60885         this.grid.fireEvent("bodyscroll", mb.scrollLeft, mb.scrollTop);
60886     },
60887
60888     // private
60889     syncHeaderScroll : function(){
60890         var mb = this.scroller.dom;
60891         this.innerHd.scrollLeft = mb.scrollLeft;
60892         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
60893     },
60894
60895     // private
60896     updateSortIcon : function(col, dir){
60897         var sc = this.sortClasses;
60898         var hds = this.mainHd.select('td').removeClass(sc);
60899         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
60900     },
60901
60902     // private
60903     updateAllColumnWidths : function(){
60904         var tw = this.getTotalWidth(),
60905             clen = this.cm.getColumnCount(),
60906             ws = [],
60907             len,
60908             i;
60909         for(i = 0; i < clen; i++){
60910             ws[i] = this.getColumnWidth(i);
60911         }
60912         this.innerHd.firstChild.style.width = this.getOffsetWidth();
60913         this.innerHd.firstChild.firstChild.style.width = tw;
60914         this.mainBody.dom.style.width = tw;
60915         for(i = 0; i < clen; i++){
60916             var hd = this.getHeaderCell(i);
60917             hd.style.width = ws[i];
60918         }
60919
60920         var ns = this.getRows(), row, trow;
60921         for(i = 0, len = ns.length; i < len; i++){
60922             row = ns[i];
60923             row.style.width = tw;
60924             if(row.firstChild){
60925                 row.firstChild.style.width = tw;
60926                 trow = row.firstChild.rows[0];
60927                 for (var j = 0; j < clen; j++) {
60928                    trow.childNodes[j].style.width = ws[j];
60929                 }
60930             }
60931         }
60932
60933         this.onAllColumnWidthsUpdated(ws, tw);
60934     },
60935
60936     // private
60937     updateColumnWidth : function(col, width){
60938         var w = this.getColumnWidth(col);
60939         var tw = this.getTotalWidth();
60940         this.innerHd.firstChild.style.width = this.getOffsetWidth();
60941         this.innerHd.firstChild.firstChild.style.width = tw;
60942         this.mainBody.dom.style.width = tw;
60943         var hd = this.getHeaderCell(col);
60944         hd.style.width = w;
60945
60946         var ns = this.getRows(), row;
60947         for(var i = 0, len = ns.length; i < len; i++){
60948             row = ns[i];
60949             row.style.width = tw;
60950             if(row.firstChild){
60951                 row.firstChild.style.width = tw;
60952                 row.firstChild.rows[0].childNodes[col].style.width = w;
60953             }
60954         }
60955
60956         this.onColumnWidthUpdated(col, w, tw);
60957     },
60958
60959     // private
60960     updateColumnHidden : function(col, hidden){
60961         var tw = this.getTotalWidth();
60962         this.innerHd.firstChild.style.width = this.getOffsetWidth();
60963         this.innerHd.firstChild.firstChild.style.width = tw;
60964         this.mainBody.dom.style.width = tw;
60965         var display = hidden ? 'none' : '';
60966
60967         var hd = this.getHeaderCell(col);
60968         hd.style.display = display;
60969
60970         var ns = this.getRows(), row;
60971         for(var i = 0, len = ns.length; i < len; i++){
60972             row = ns[i];
60973             row.style.width = tw;
60974             if(row.firstChild){
60975                 row.firstChild.style.width = tw;
60976                 row.firstChild.rows[0].childNodes[col].style.display = display;
60977             }
60978         }
60979
60980         this.onColumnHiddenUpdated(col, hidden, tw);
60981         delete this.lastViewWidth; // force recalc
60982         this.layout();
60983     },
60984
60985     // private
60986     doRender : function(cs, rs, ds, startRow, colCount, stripe){
60987         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
60988         var tstyle = 'width:'+this.getTotalWidth()+';';
60989         // buffers
60990         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
60991         for(var j = 0, len = rs.length; j < len; j++){
60992             r = rs[j]; cb = [];
60993             var rowIndex = (j+startRow);
60994             for(var i = 0; i < colCount; i++){
60995                 c = cs[i];
60996                 p.id = c.id;
60997                 p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
60998                 p.attr = p.cellAttr = "";
60999                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
61000                 p.style = c.style;
61001                 if(Ext.isEmpty(p.value)){
61002                     p.value = "&#160;";
61003                 }
61004                 if(this.markDirty && r.dirty && typeof r.modified[c.name] !== 'undefined'){
61005                     p.css += ' x-grid3-dirty-cell';
61006                 }
61007                 cb[cb.length] = ct.apply(p);
61008             }
61009             var alt = [];
61010             if(stripe && ((rowIndex+1) % 2 === 0)){
61011                 alt[0] = "x-grid3-row-alt";
61012             }
61013             if(r.dirty){
61014                 alt[1] = " x-grid3-dirty-row";
61015             }
61016             rp.cols = colCount;
61017             if(this.getRowClass){
61018                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
61019             }
61020             rp.alt = alt.join(" ");
61021             rp.cells = cb.join("");
61022             buf[buf.length] =  rt.apply(rp);
61023         }
61024         return buf.join("");
61025     },
61026
61027     // private
61028     processRows : function(startRow, skipStripe){
61029         if(!this.ds || this.ds.getCount() < 1){
61030             return;
61031         }
61032         var rows = this.getRows();
61033         skipStripe = skipStripe || !this.grid.stripeRows;
61034         startRow = startRow || 0;
61035         Ext.each(rows, function(row, idx){
61036             row.rowIndex = idx;
61037             row.className = row.className.replace(this.rowClsRe, ' ');
61038             if (!skipStripe && (idx + 1) % 2 === 0) {
61039                 row.className += ' x-grid3-row-alt';
61040             }
61041         });
61042         // add first/last-row classes
61043         if(startRow === 0){
61044             Ext.fly(rows[0]).addClass(this.firstRowCls);
61045         }
61046         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
61047     },
61048
61049     afterRender : function(){
61050         if(!this.ds || !this.cm){
61051             return;
61052         }
61053         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
61054         this.processRows(0, true);
61055
61056         if(this.deferEmptyText !== true){
61057             this.applyEmptyText();
61058         }
61059     },
61060
61061     // private
61062     renderUI : function(){
61063
61064         var header = this.renderHeaders();
61065         var body = this.templates.body.apply({rows:'&#160;'});
61066
61067
61068         var html = this.templates.master.apply({
61069             body: body,
61070             header: header,
61071             ostyle: 'width:'+this.getOffsetWidth()+';',
61072             bstyle: 'width:'+this.getTotalWidth()+';'
61073         });
61074
61075         var g = this.grid;
61076
61077         g.getGridEl().dom.innerHTML = html;
61078
61079         this.initElements();
61080
61081         // get mousedowns early
61082         Ext.fly(this.innerHd).on("click", this.handleHdDown, this);
61083         this.mainHd.on({
61084             scope: this,
61085             mouseover: this.handleHdOver,
61086             mouseout: this.handleHdOut,
61087             mousemove: this.handleHdMove
61088         });
61089
61090         this.scroller.on('scroll', this.syncScroll,  this);
61091         if(g.enableColumnResize !== false){
61092             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
61093         }
61094
61095         if(g.enableColumnMove){
61096             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
61097             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
61098         }
61099
61100         if(g.enableHdMenu !== false){
61101             this.hmenu = new Ext.menu.Menu({id: g.id + "-hctx"});
61102             this.hmenu.add(
61103                 {itemId:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
61104                 {itemId:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
61105             );
61106             if(g.enableColumnHide !== false){
61107                 this.colMenu = new Ext.menu.Menu({id:g.id + "-hcols-menu"});
61108                 this.colMenu.on({
61109                     scope: this,
61110                     beforeshow: this.beforeColMenuShow,
61111                     itemclick: this.handleHdMenuClick
61112                 });
61113                 this.hmenu.add('-', {
61114                     itemId:"columns",
61115                     hideOnClick: false,
61116                     text: this.columnsText,
61117                     menu: this.colMenu,
61118                     iconCls: 'x-cols-icon'
61119                 });
61120             }
61121             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
61122         }
61123
61124         if(g.trackMouseOver){
61125             this.mainBody.on({
61126                 scope: this,
61127                 mouseover: this.onRowOver,
61128                 mouseout: this.onRowOut
61129             });
61130         }
61131
61132         if(g.enableDragDrop || g.enableDrag){
61133             this.dragZone = new Ext.grid.GridDragZone(g, {
61134                 ddGroup : g.ddGroup || 'GridDD'
61135             });
61136         }
61137
61138         this.updateHeaderSortState();
61139
61140     },
61141
61142     // private
61143     layout : function(){
61144         if(!this.mainBody){
61145             return; // not rendered
61146         }
61147         var g = this.grid;
61148         var c = g.getGridEl();
61149         var csize = c.getSize(true);
61150         var vw = csize.width;
61151
61152         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
61153             return;
61154         }
61155         
61156         if(g.autoHeight){
61157             this.scroller.dom.style.overflow = 'visible';
61158             if(Ext.isWebKit){
61159                 this.scroller.dom.style.position = 'static';
61160             }
61161         }else{
61162             this.el.setSize(csize.width, csize.height);
61163
61164             var hdHeight = this.mainHd.getHeight();
61165             var vh = csize.height - (hdHeight);
61166
61167             this.scroller.setSize(vw, vh);
61168             if(this.innerHd){
61169                 this.innerHd.style.width = (vw)+'px';
61170             }
61171         }
61172         if(this.forceFit){
61173             if(this.lastViewWidth != vw){
61174                 this.fitColumns(false, false);
61175                 this.lastViewWidth = vw;
61176             }
61177         }else {
61178             this.autoExpand();
61179             this.syncHeaderScroll();
61180         }
61181         this.onLayout(vw, vh);
61182     },
61183
61184     // template functions for subclasses and plugins
61185     // these functions include precalculated values
61186     onLayout : function(vw, vh){
61187         // do nothing
61188     },
61189
61190     onColumnWidthUpdated : function(col, w, tw){
61191         //template method
61192     },
61193
61194     onAllColumnWidthsUpdated : function(ws, tw){
61195         //template method
61196     },
61197
61198     onColumnHiddenUpdated : function(col, hidden, tw){
61199         // template method
61200     },
61201
61202     updateColumnText : function(col, text){
61203         // template method
61204     },
61205
61206     afterMove : function(colIndex){
61207         // template method
61208     },
61209
61210     /* ----------------------------------- Core Specific -------------------------------------------*/
61211     // private
61212     init : function(grid){
61213         this.grid = grid;
61214
61215         this.initTemplates();
61216         this.initData(grid.store, grid.colModel);
61217         this.initUI(grid);
61218     },
61219
61220     // private
61221     getColumnId : function(index){
61222       return this.cm.getColumnId(index);
61223     },
61224     
61225     // private 
61226     getOffsetWidth : function() {
61227         return (this.cm.getTotalWidth() + this.scrollOffset) + 'px';
61228     },
61229
61230     // private
61231     renderHeaders : function(){
61232         var cm = this.cm, 
61233             ts = this.templates,
61234             ct = ts.hcell,
61235             cb = [], 
61236             p = {},
61237             len = cm.getColumnCount(),
61238             last = len - 1;
61239             
61240         for(var i = 0; i < len; i++){
61241             p.id = cm.getColumnId(i);
61242             p.value = cm.getColumnHeader(i) || "";
61243             p.style = this.getColumnStyle(i, true);
61244             p.tooltip = this.getColumnTooltip(i);
61245             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
61246             if(cm.config[i].align == 'right'){
61247                 p.istyle = 'padding-right:16px';
61248             } else {
61249                 delete p.istyle;
61250             }
61251             cb[cb.length] = ct.apply(p);
61252         }
61253         return ts.header.apply({cells: cb.join(""), tstyle:'width:'+this.getTotalWidth()+';'});
61254     },
61255
61256     // private
61257     getColumnTooltip : function(i){
61258         var tt = this.cm.getColumnTooltip(i);
61259         if(tt){
61260             if(Ext.QuickTips.isEnabled()){
61261                 return 'ext:qtip="'+tt+'"';
61262             }else{
61263                 return 'title="'+tt+'"';
61264             }
61265         }
61266         return "";
61267     },
61268
61269     // private
61270     beforeUpdate : function(){
61271         this.grid.stopEditing(true);
61272     },
61273
61274     // private
61275     updateHeaders : function(){
61276         this.innerHd.firstChild.innerHTML = this.renderHeaders();
61277         this.innerHd.firstChild.style.width = this.getOffsetWidth();
61278         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
61279     },
61280
61281     /**
61282      * Focuses the specified row.
61283      * @param {Number} row The row index
61284      */
61285     focusRow : function(row){
61286         this.focusCell(row, 0, false);
61287     },
61288
61289     /**
61290      * Focuses the specified cell.
61291      * @param {Number} row The row index
61292      * @param {Number} col The column index
61293      */
61294     focusCell : function(row, col, hscroll){
61295         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
61296         if(Ext.isGecko){
61297             this.focusEl.focus();
61298         }else{
61299             this.focusEl.focus.defer(1, this.focusEl);
61300         }
61301     },
61302
61303     resolveCell : function(row, col, hscroll){
61304         if(typeof row != "number"){
61305             row = row.rowIndex;
61306         }
61307         if(!this.ds){
61308             return null;
61309         }
61310         if(row < 0 || row >= this.ds.getCount()){
61311             return null;
61312         }
61313         col = (col !== undefined ? col : 0);
61314
61315         var rowEl = this.getRow(row),
61316             cm = this.cm,
61317             colCount = cm.getColumnCount(),
61318             cellEl;
61319         if(!(hscroll === false && col === 0)){
61320             while(col < colCount && cm.isHidden(col)){
61321                 col++;
61322             }
61323             cellEl = this.getCell(row, col);
61324         }
61325
61326         return {row: rowEl, cell: cellEl};
61327     },
61328
61329     getResolvedXY : function(resolved){
61330         if(!resolved){
61331             return null;
61332         }
61333         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
61334         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
61335     },
61336
61337     syncFocusEl : function(row, col, hscroll){
61338         var xy = row;
61339         if(!Ext.isArray(xy)){
61340             row = Math.min(row, Math.max(0, this.getRows().length-1));
61341             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
61342         }
61343         this.focusEl.setXY(xy||this.scroller.getXY());
61344     },
61345
61346     ensureVisible : function(row, col, hscroll){
61347         var resolved = this.resolveCell(row, col, hscroll);
61348         if(!resolved || !resolved.row){
61349             return;
61350         }
61351
61352         var rowEl = resolved.row, 
61353             cellEl = resolved.cell,
61354             c = this.scroller.dom,
61355             ctop = 0,
61356             p = rowEl, 
61357             stop = this.el.dom;
61358             
61359         while(p && p != stop){
61360             ctop += p.offsetTop;
61361             p = p.offsetParent;
61362         }
61363         ctop -= this.mainHd.dom.offsetHeight;
61364
61365         var cbot = ctop + rowEl.offsetHeight,
61366             ch = c.clientHeight,
61367             sbot = stop + ch;
61368             
61369         stop = parseInt(c.scrollTop, 10);
61370         
61371
61372         if(ctop < stop){
61373           c.scrollTop = ctop;
61374         }else if(cbot > sbot){
61375             c.scrollTop = cbot-ch;
61376         }
61377
61378         if(hscroll !== false){
61379             var cleft = parseInt(cellEl.offsetLeft, 10);
61380             var cright = cleft + cellEl.offsetWidth;
61381
61382             var sleft = parseInt(c.scrollLeft, 10);
61383             var sright = sleft + c.clientWidth;
61384             if(cleft < sleft){
61385                 c.scrollLeft = cleft;
61386             }else if(cright > sright){
61387                 c.scrollLeft = cright-c.clientWidth;
61388             }
61389         }
61390         return this.getResolvedXY(resolved);
61391     },
61392
61393     // private
61394     insertRows : function(dm, firstRow, lastRow, isUpdate){
61395         var last = dm.getCount() - 1;
61396         if(!isUpdate && firstRow === 0 && lastRow >= last){
61397             this.refresh();
61398         }else{
61399             if(!isUpdate){
61400                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
61401             }
61402             var html = this.renderRows(firstRow, lastRow),
61403                 before = this.getRow(firstRow);
61404             if(before){
61405                 if(firstRow === 0){
61406                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
61407                 }
61408                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
61409             }else{
61410                 var r = this.getRow(last - 1);
61411                 if(r){
61412                     Ext.fly(r).removeClass(this.lastRowCls);
61413                 }
61414                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
61415             }
61416             if(!isUpdate){
61417                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
61418                 this.processRows(firstRow);
61419             }else if(firstRow === 0 || firstRow >= last){
61420                 //ensure first/last row is kept after an update.
61421                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
61422             }
61423         }
61424         this.syncFocusEl(firstRow);
61425     },
61426
61427     // private
61428     deleteRows : function(dm, firstRow, lastRow){
61429         if(dm.getRowCount()<1){
61430             this.refresh();
61431         }else{
61432             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
61433
61434             this.removeRows(firstRow, lastRow);
61435
61436             this.processRows(firstRow);
61437             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
61438         }
61439     },
61440
61441     // private
61442     getColumnStyle : function(col, isHeader){
61443         var style = !isHeader ? (this.cm.config[col].css || '') : '';
61444         style += 'width:'+this.getColumnWidth(col)+';';
61445         if(this.cm.isHidden(col)){
61446             style += 'display:none;';
61447         }
61448         var align = this.cm.config[col].align;
61449         if(align){
61450             style += 'text-align:'+align+';';
61451         }
61452         return style;
61453     },
61454
61455     // private
61456     getColumnWidth : function(col){
61457         var w = this.cm.getColumnWidth(col);
61458         if(typeof w == 'number'){
61459             return (Ext.isBorderBox ? w : (w-this.borderWidth > 0 ? w-this.borderWidth:0)) + 'px';
61460         }
61461         return w;
61462     },
61463
61464     // private
61465     getTotalWidth : function(){
61466         return this.cm.getTotalWidth()+'px';
61467     },
61468
61469     // private
61470     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
61471         var cm = this.cm, i;
61472         var tw = cm.getTotalWidth(false);
61473         var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;
61474
61475         if(aw < 20){ // not initialized, so don't screw up the default widths
61476             return;
61477         }
61478         var extra = aw - tw;
61479
61480         if(extra === 0){
61481             return false;
61482         }
61483
61484         var vc = cm.getColumnCount(true);
61485         var ac = vc-(typeof omitColumn == 'number' ? 1 : 0);
61486         if(ac === 0){
61487             ac = 1;
61488             omitColumn = undefined;
61489         }
61490         var colCount = cm.getColumnCount();
61491         var cols = [];
61492         var extraCol = 0;
61493         var width = 0;
61494         var w;
61495         for (i = 0; i < colCount; i++){
61496             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
61497                 w = cm.getColumnWidth(i);
61498                 cols.push(i);
61499                 extraCol = i;
61500                 cols.push(w);
61501                 width += w;
61502             }
61503         }
61504         var frac = (aw - cm.getTotalWidth())/width;
61505         while (cols.length){
61506             w = cols.pop();
61507             i = cols.pop();
61508             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
61509         }
61510
61511         if((tw = cm.getTotalWidth(false)) > aw){
61512             var adjustCol = ac != vc ? omitColumn : extraCol;
61513              cm.setColumnWidth(adjustCol, Math.max(1,
61514                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
61515         }
61516
61517         if(preventRefresh !== true){
61518             this.updateAllColumnWidths();
61519         }
61520
61521
61522         return true;
61523     },
61524
61525     // private
61526     autoExpand : function(preventUpdate){
61527         var g = this.grid, cm = this.cm;
61528         if(!this.userResized && g.autoExpandColumn){
61529             var tw = cm.getTotalWidth(false);
61530             var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;
61531             if(tw != aw){
61532                 var ci = cm.getIndexById(g.autoExpandColumn);
61533                 var currentWidth = cm.getColumnWidth(ci);
61534                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
61535                 if(cw != currentWidth){
61536                     cm.setColumnWidth(ci, cw, true);
61537                     if(preventUpdate !== true){
61538                         this.updateColumnWidth(ci, cw);
61539                     }
61540                 }
61541             }
61542         }
61543     },
61544
61545     // private
61546     getColumnData : function(){
61547         // build a map for all the columns
61548         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
61549         for(var i = 0; i < colCount; i++){
61550             var name = cm.getDataIndex(i);
61551             cs[i] = {
61552                 name : (typeof name == 'undefined' ? this.ds.fields.get(i).name : name),
61553                 renderer : cm.getRenderer(i),
61554                 id : cm.getColumnId(i),
61555                 style : this.getColumnStyle(i)
61556             };
61557         }
61558         return cs;
61559     },
61560
61561     // private
61562     renderRows : function(startRow, endRow){
61563         // pull in all the crap needed to render rows
61564         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
61565         var colCount = cm.getColumnCount();
61566
61567         if(ds.getCount() < 1){
61568             return "";
61569         }
61570
61571         var cs = this.getColumnData();
61572
61573         startRow = startRow || 0;
61574         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
61575
61576         // records to render
61577         var rs = ds.getRange(startRow, endRow);
61578
61579         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
61580     },
61581
61582     // private
61583     renderBody : function(){
61584         var markup = this.renderRows() || '&#160;';
61585         return this.templates.body.apply({rows: markup});
61586     },
61587
61588     // private
61589     refreshRow : function(record){
61590         var ds = this.ds, index;
61591         if(typeof record == 'number'){
61592             index = record;
61593             record = ds.getAt(index);
61594             if(!record){
61595                 return;
61596             }
61597         }else{
61598             index = ds.indexOf(record);
61599             if(index < 0){
61600                 return;
61601             }
61602         }
61603         this.insertRows(ds, index, index, true);
61604         this.getRow(index).rowIndex = index;
61605         this.onRemove(ds, record, index+1, true);
61606         this.fireEvent("rowupdated", this, index, record);
61607     },
61608
61609     /**
61610      * Refreshs the grid UI
61611      * @param {Boolean} headersToo (optional) True to also refresh the headers
61612      */
61613     refresh : function(headersToo){
61614         this.fireEvent("beforerefresh", this);
61615         this.grid.stopEditing(true);
61616
61617         var result = this.renderBody();
61618         this.mainBody.update(result).setWidth(this.getTotalWidth());
61619         if(headersToo === true){
61620             this.updateHeaders();
61621             this.updateHeaderSortState();
61622         }
61623         this.processRows(0, true);
61624         this.layout();
61625         this.applyEmptyText();
61626         this.fireEvent("refresh", this);
61627     },
61628
61629     // private
61630     applyEmptyText : function(){
61631         if(this.emptyText && !this.hasRows()){
61632             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
61633         }
61634     },
61635
61636     // private
61637     updateHeaderSortState : function(){
61638         var state = this.ds.getSortState();
61639         if(!state){
61640             return;
61641         }
61642         if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){
61643             this.grid.fireEvent('sortchange', this.grid, state);
61644         }
61645         this.sortState = state;
61646         var sortColumn = this.cm.findColumnIndex(state.field);
61647         if(sortColumn != -1){
61648             var sortDir = state.direction;
61649             this.updateSortIcon(sortColumn, sortDir);
61650         }
61651     },
61652
61653     // private
61654     destroy : function(){
61655         if(this.colMenu){
61656             Ext.menu.MenuMgr.unregister(this.colMenu);
61657             this.colMenu.destroy();
61658             delete this.colMenu;
61659         }
61660         if(this.hmenu){
61661             Ext.menu.MenuMgr.unregister(this.hmenu);
61662             this.hmenu.destroy();
61663             delete this.hmenu;
61664         }
61665         if(this.grid.enableColumnMove){
61666             var dds = Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
61667             if(dds){
61668                 for(var dd in dds){
61669                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
61670                         var elid = dds[dd].dragElId;
61671                         dds[dd].unreg();
61672                         Ext.get(elid).remove();
61673                     } else if(dds[dd].config.isTarget){
61674                         dds[dd].proxyTop.remove();
61675                         dds[dd].proxyBottom.remove();
61676                         dds[dd].unreg();
61677                     }
61678                     if(Ext.dd.DDM.locationCache[dd]){
61679                         delete Ext.dd.DDM.locationCache[dd];
61680                     }
61681                 }
61682                 delete Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
61683             }
61684         }
61685
61686         if(this.dragZone){
61687             this.dragZone.unreg();
61688         }
61689         
61690         Ext.fly(this.innerHd).removeAllListeners();
61691         Ext.removeNode(this.innerHd);
61692         
61693         Ext.destroy(this.resizeMarker, this.resizeProxy, this.focusEl, this.mainBody, 
61694                     this.scroller, this.mainHd, this.mainWrap, this.dragZone, 
61695                     this.splitZone, this.columnDrag, this.columnDrop);
61696
61697         this.initData(null, null);
61698         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
61699         this.purgeListeners();
61700     },
61701
61702     // private
61703     onDenyColumnHide : function(){
61704
61705     },
61706
61707     // private
61708     render : function(){
61709         if(this.autoFill){
61710             var ct = this.grid.ownerCt;
61711             if (ct && ct.getLayout()){
61712                 ct.on('afterlayout', function(){ 
61713                     this.fitColumns(true, true);
61714                     this.updateHeaders(); 
61715                 }, this, {single: true}); 
61716             }else{ 
61717                 this.fitColumns(true, true); 
61718             }
61719         }else if(this.forceFit){
61720             this.fitColumns(true, false);
61721         }else if(this.grid.autoExpandColumn){
61722             this.autoExpand(true);
61723         }
61724
61725         this.renderUI();
61726     },
61727
61728     /* --------------------------------- Model Events and Handlers --------------------------------*/
61729     // private
61730     initData : function(ds, cm){
61731         if(this.ds){
61732             this.ds.un("load", this.onLoad, this);
61733             this.ds.un("datachanged", this.onDataChange, this);
61734             this.ds.un("add", this.onAdd, this);
61735             this.ds.un("remove", this.onRemove, this);
61736             this.ds.un("update", this.onUpdate, this);
61737             this.ds.un("clear", this.onClear, this);
61738             if(this.ds !== ds && this.ds.autoDestroy){
61739                 this.ds.destroy();
61740             }
61741         }
61742         if(ds){
61743             ds.on({
61744                 scope: this,
61745                 load: this.onLoad,
61746                 datachanged: this.onDataChange,
61747                 add: this.onAdd,
61748                 remove: this.onRemove,
61749                 update: this.onUpdate,
61750                 clear: this.onClear
61751             });
61752         }
61753         this.ds = ds;
61754
61755         if(this.cm){
61756             this.cm.un("configchange", this.onColConfigChange, this);
61757             this.cm.un("widthchange", this.onColWidthChange, this);
61758             this.cm.un("headerchange", this.onHeaderChange, this);
61759             this.cm.un("hiddenchange", this.onHiddenChange, this);
61760             this.cm.un("columnmoved", this.onColumnMove, this);
61761         }
61762         if(cm){
61763             delete this.lastViewWidth;
61764             cm.on({
61765                 scope: this,
61766                 configchange: this.onColConfigChange,
61767                 widthchange: this.onColWidthChange,
61768                 headerchange: this.onHeaderChange,
61769                 hiddenchange: this.onHiddenChange,
61770                 columnmoved: this.onColumnMove
61771             });
61772         }
61773         this.cm = cm;
61774     },
61775
61776     // private
61777     onDataChange : function(){
61778         this.refresh();
61779         this.updateHeaderSortState();
61780         this.syncFocusEl(0);
61781     },
61782
61783     // private
61784     onClear : function(){
61785         this.refresh();
61786         this.syncFocusEl(0);
61787     },
61788
61789     // private
61790     onUpdate : function(ds, record){
61791         this.refreshRow(record);
61792     },
61793
61794     // private
61795     onAdd : function(ds, records, index){
61796         this.insertRows(ds, index, index + (records.length-1));
61797     },
61798
61799     // private
61800     onRemove : function(ds, record, index, isUpdate){
61801         if(isUpdate !== true){
61802             this.fireEvent("beforerowremoved", this, index, record);
61803         }
61804         this.removeRow(index);
61805         if(isUpdate !== true){
61806             this.processRows(index);
61807             this.applyEmptyText();
61808             this.fireEvent("rowremoved", this, index, record);
61809         }
61810     },
61811
61812     // private
61813     onLoad : function(){
61814         this.scrollToTop();
61815     },
61816
61817     // private
61818     onColWidthChange : function(cm, col, width){
61819         this.updateColumnWidth(col, width);
61820     },
61821
61822     // private
61823     onHeaderChange : function(cm, col, text){
61824         this.updateHeaders();
61825     },
61826
61827     // private
61828     onHiddenChange : function(cm, col, hidden){
61829         this.updateColumnHidden(col, hidden);
61830     },
61831
61832     // private
61833     onColumnMove : function(cm, oldIndex, newIndex){
61834         this.indexMap = null;
61835         var s = this.getScrollState();
61836         this.refresh(true);
61837         this.restoreScroll(s);
61838         this.afterMove(newIndex);
61839         this.grid.fireEvent('columnmove', oldIndex, newIndex);
61840     },
61841
61842     // private
61843     onColConfigChange : function(){
61844         delete this.lastViewWidth;
61845         this.indexMap = null;
61846         this.refresh(true);
61847     },
61848
61849     /* -------------------- UI Events and Handlers ------------------------------ */
61850     // private
61851     initUI : function(grid){
61852         grid.on("headerclick", this.onHeaderClick, this);
61853     },
61854
61855     // private
61856     initEvents : function(){
61857     },
61858
61859     // private
61860     onHeaderClick : function(g, index){
61861         if(this.headersDisabled || !this.cm.isSortable(index)){
61862             return;
61863         }
61864         g.stopEditing(true);
61865         g.store.sort(this.cm.getDataIndex(index));
61866     },
61867
61868     // private
61869     onRowOver : function(e, t){
61870         var row;
61871         if((row = this.findRowIndex(t)) !== false){
61872             this.addRowClass(row, "x-grid3-row-over");
61873         }
61874     },
61875
61876     // private
61877     onRowOut : function(e, t){
61878         var row;
61879         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
61880             this.removeRowClass(row, "x-grid3-row-over");
61881         }
61882     },
61883
61884     // private
61885     handleWheel : function(e){
61886         e.stopPropagation();
61887     },
61888
61889     // private
61890     onRowSelect : function(row){
61891         this.addRowClass(row, this.selectedRowClass);
61892     },
61893
61894     // private
61895     onRowDeselect : function(row){
61896         this.removeRowClass(row, this.selectedRowClass);
61897     },
61898
61899     // private
61900     onCellSelect : function(row, col){
61901         var cell = this.getCell(row, col);
61902         if(cell){
61903             this.fly(cell).addClass("x-grid3-cell-selected");
61904         }
61905     },
61906
61907     // private
61908     onCellDeselect : function(row, col){
61909         var cell = this.getCell(row, col);
61910         if(cell){
61911             this.fly(cell).removeClass("x-grid3-cell-selected");
61912         }
61913     },
61914
61915     // private
61916     onColumnSplitterMoved : function(i, w){
61917         this.userResized = true;
61918         var cm = this.grid.colModel;
61919         cm.setColumnWidth(i, w, true);
61920
61921         if(this.forceFit){
61922             this.fitColumns(true, false, i);
61923             this.updateAllColumnWidths();
61924         }else{
61925             this.updateColumnWidth(i, w);
61926             this.syncHeaderScroll();
61927         }
61928
61929         this.grid.fireEvent("columnresize", i, w);
61930     },
61931
61932     // private
61933     handleHdMenuClick : function(item){
61934         var index = this.hdCtxIndex;
61935         var cm = this.cm, ds = this.ds;
61936         switch(item.itemId){
61937             case "asc":
61938                 ds.sort(cm.getDataIndex(index), "ASC");
61939                 break;
61940             case "desc":
61941                 ds.sort(cm.getDataIndex(index), "DESC");
61942                 break;
61943             default:
61944                 index = cm.getIndexById(item.itemId.substr(4));
61945                 if(index != -1){
61946                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
61947                         this.onDenyColumnHide();
61948                         return false;
61949                     }
61950                     cm.setHidden(index, item.checked);
61951                 }
61952         }
61953         return true;
61954     },
61955
61956     // private
61957     isHideableColumn : function(c){
61958         return !c.hidden && !c.fixed;
61959     },
61960
61961     // private
61962     beforeColMenuShow : function(){
61963         var cm = this.cm,  colCount = cm.getColumnCount();
61964         this.colMenu.removeAll();
61965         for(var i = 0; i < colCount; i++){
61966             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
61967                 this.colMenu.add(new Ext.menu.CheckItem({
61968                     itemId: "col-"+cm.getColumnId(i),
61969                     text: cm.getColumnHeader(i),
61970                     checked: !cm.isHidden(i),
61971                     hideOnClick:false,
61972                     disabled: cm.config[i].hideable === false
61973                 }));
61974             }
61975         }
61976     },
61977
61978     // private
61979     handleHdDown : function(e, t){
61980         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
61981             e.stopEvent();
61982             var hd = this.findHeaderCell(t);
61983             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
61984             var index = this.getCellIndex(hd);
61985             this.hdCtxIndex = index;
61986             var ms = this.hmenu.items, cm = this.cm;
61987             ms.get("asc").setDisabled(!cm.isSortable(index));
61988             ms.get("desc").setDisabled(!cm.isSortable(index));
61989             this.hmenu.on("hide", function(){
61990                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
61991             }, this, {single:true});
61992             this.hmenu.show(t, "tl-bl?");
61993         }
61994     },
61995
61996     // private
61997     handleHdOver : function(e, t){
61998         var hd = this.findHeaderCell(t);
61999         if(hd && !this.headersDisabled){
62000             this.activeHd = hd;
62001             this.activeHdIndex = this.getCellIndex(hd);
62002             var fly = this.fly(hd);
62003             this.activeHdRegion = fly.getRegion();
62004             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
62005                 fly.addClass("x-grid3-hd-over");
62006                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
62007                 if(this.activeHdBtn){
62008                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
62009                 }
62010             }
62011         }
62012     },
62013
62014     // private
62015     handleHdMove : function(e, t){
62016         if(this.activeHd && !this.headersDisabled){
62017             var hw = this.splitHandleWidth || 5;
62018             var r = this.activeHdRegion;
62019             var x = e.getPageX();
62020             var ss = this.activeHd.style;
62021             if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
62022                 ss.cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
62023             }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
62024                 ss.cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
62025             }else{
62026                 ss.cursor = '';
62027             }
62028         }
62029     },
62030
62031     // private
62032     handleHdOut : function(e, t){
62033         var hd = this.findHeaderCell(t);
62034         if(hd && (!Ext.isIE || !e.within(hd, true))){
62035             this.activeHd = null;
62036             this.fly(hd).removeClass("x-grid3-hd-over");
62037             hd.style.cursor = '';
62038         }
62039     },
62040
62041     // private
62042     hasRows : function(){
62043         var fc = this.mainBody.dom.firstChild;
62044         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
62045     },
62046
62047     // back compat
62048     bind : function(d, c){
62049         this.initData(d, c);
62050     }
62051 });
62052
62053
62054 // private
62055 // This is a support class used internally by the Grid components
62056 Ext.grid.GridView.SplitDragZone = function(grid, hd){
62057     this.grid = grid;
62058     this.view = grid.getView();
62059     this.marker = this.view.resizeMarker;
62060     this.proxy = this.view.resizeProxy;
62061     Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
62062         "gridSplitters" + this.grid.getGridEl().id, {
62063         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
62064     });
62065     this.scroll = false;
62066     this.hw = this.view.splitHandleWidth || 5;
62067 };
62068 Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {
62069
62070     b4StartDrag : function(x, y){
62071         this.view.headersDisabled = true;
62072         var h = this.view.mainWrap.getHeight();
62073         this.marker.setHeight(h);
62074         this.marker.show();
62075         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
62076         this.proxy.setHeight(h);
62077         var w = this.cm.getColumnWidth(this.cellIndex);
62078         var minw = Math.max(w-this.grid.minColumnWidth, 0);
62079         this.resetConstraints();
62080         this.setXConstraint(minw, 1000);
62081         this.setYConstraint(0, 0);
62082         this.minX = x - minw;
62083         this.maxX = x + 1000;
62084         this.startPos = x;
62085         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
62086     },
62087
62088
62089     handleMouseDown : function(e){
62090         var t = this.view.findHeaderCell(e.getTarget());
62091         if(t){
62092             var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];
62093             var exy = e.getXY(), ex = exy[0];
62094             var w = t.offsetWidth, adjust = false;
62095             if((ex - x) <= this.hw){
62096                 adjust = -1;
62097             }else if((x+w) - ex <= this.hw){
62098                 adjust = 0;
62099             }
62100             if(adjust !== false){
62101                 this.cm = this.grid.colModel;
62102                 var ci = this.view.getCellIndex(t);
62103                 if(adjust == -1){
62104                   if (ci + adjust < 0) {
62105                     return;
62106                   }
62107                     while(this.cm.isHidden(ci+adjust)){
62108                         --adjust;
62109                         if(ci+adjust < 0){
62110                             return;
62111                         }
62112                     }
62113                 }
62114                 this.cellIndex = ci+adjust;
62115                 this.split = t.dom;
62116                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
62117                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
62118                 }
62119             }else if(this.view.columnDrag){
62120                 this.view.columnDrag.callHandleMouseDown(e);
62121             }
62122         }
62123     },
62124
62125     endDrag : function(e){
62126         this.marker.hide();
62127         var v = this.view;
62128         var endX = Math.max(this.minX, e.getPageX());
62129         var diff = endX - this.startPos;
62130         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
62131         setTimeout(function(){
62132             v.headersDisabled = false;
62133         }, 50);
62134     },
62135
62136     autoOffset : function(){
62137         this.setDelta(0,0);
62138     }
62139 });
62140 // private\r
62141 // This is a support class used internally by the Grid components\r
62142 Ext.grid.HeaderDragZone = function(grid, hd, hd2){\r
62143     this.grid = grid;\r
62144     this.view = grid.getView();\r
62145     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
62146     Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);\r
62147     if(hd2){\r
62148         this.setHandleElId(Ext.id(hd));\r
62149         this.setOuterHandleElId(Ext.id(hd2));\r
62150     }\r
62151     this.scroll = false;\r
62152 };\r
62153 Ext.extend(Ext.grid.HeaderDragZone, Ext.dd.DragZone, {\r
62154     maxDragWidth: 120,\r
62155     getDragData : function(e){\r
62156         var t = Ext.lib.Event.getTarget(e);\r
62157         var h = this.view.findHeaderCell(t);\r
62158         if(h){\r
62159             return {ddel: h.firstChild, header:h};\r
62160         }\r
62161         return false;\r
62162     },\r
62163 \r
62164     onInitDrag : function(e){\r
62165         this.view.headersDisabled = true;\r
62166         var clone = this.dragData.ddel.cloneNode(true);\r
62167         clone.id = Ext.id();\r
62168         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";\r
62169         this.proxy.update(clone);\r
62170         return true;\r
62171     },\r
62172 \r
62173     afterValidDrop : function(){\r
62174         var v = this.view;\r
62175         setTimeout(function(){\r
62176             v.headersDisabled = false;\r
62177         }, 50);\r
62178     },\r
62179 \r
62180     afterInvalidDrop : function(){\r
62181         var v = this.view;\r
62182         setTimeout(function(){\r
62183             v.headersDisabled = false;\r
62184         }, 50);\r
62185     }\r
62186 });\r
62187 \r
62188 // private\r
62189 // This is a support class used internally by the Grid components\r
62190 Ext.grid.HeaderDropZone = function(grid, hd, hd2){\r
62191     this.grid = grid;\r
62192     this.view = grid.getView();\r
62193     // split the proxies so they don't interfere with mouse events\r
62194     this.proxyTop = Ext.DomHelper.append(document.body, {\r
62195         cls:"col-move-top", html:"&#160;"\r
62196     }, true);\r
62197     this.proxyBottom = Ext.DomHelper.append(document.body, {\r
62198         cls:"col-move-bottom", html:"&#160;"\r
62199     }, true);\r
62200     this.proxyTop.hide = this.proxyBottom.hide = function(){\r
62201         this.setLeftTop(-100,-100);\r
62202         this.setStyle("visibility", "hidden");\r
62203     };\r
62204     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
62205     // temporarily disabled\r
62206     //Ext.dd.ScrollManager.register(this.view.scroller.dom);\r
62207     Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);\r
62208 };\r
62209 Ext.extend(Ext.grid.HeaderDropZone, Ext.dd.DropZone, {\r
62210     proxyOffsets : [-4, -9],\r
62211     fly: Ext.Element.fly,\r
62212 \r
62213     getTargetFromEvent : function(e){\r
62214         var t = Ext.lib.Event.getTarget(e);\r
62215         var cindex = this.view.findCellIndex(t);\r
62216         if(cindex !== false){\r
62217             return this.view.getHeaderCell(cindex);\r
62218         }\r
62219     },\r
62220 \r
62221     nextVisible : function(h){\r
62222         var v = this.view, cm = this.grid.colModel;\r
62223         h = h.nextSibling;\r
62224         while(h){\r
62225             if(!cm.isHidden(v.getCellIndex(h))){\r
62226                 return h;\r
62227             }\r
62228             h = h.nextSibling;\r
62229         }\r
62230         return null;\r
62231     },\r
62232 \r
62233     prevVisible : function(h){\r
62234         var v = this.view, cm = this.grid.colModel;\r
62235         h = h.prevSibling;\r
62236         while(h){\r
62237             if(!cm.isHidden(v.getCellIndex(h))){\r
62238                 return h;\r
62239             }\r
62240             h = h.prevSibling;\r
62241         }\r
62242         return null;\r
62243     },\r
62244 \r
62245     positionIndicator : function(h, n, e){\r
62246         var x = Ext.lib.Event.getPageX(e);\r
62247         var r = Ext.lib.Dom.getRegion(n.firstChild);\r
62248         var px, pt, py = r.top + this.proxyOffsets[1];\r
62249         if((r.right - x) <= (r.right-r.left)/2){\r
62250             px = r.right+this.view.borderWidth;\r
62251             pt = "after";\r
62252         }else{\r
62253             px = r.left;\r
62254             pt = "before";\r
62255         }\r
62256 \r
62257         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){\r
62258             return false;\r
62259         }\r
62260 \r
62261         px +=  this.proxyOffsets[0];\r
62262         this.proxyTop.setLeftTop(px, py);\r
62263         this.proxyTop.show();\r
62264         if(!this.bottomOffset){\r
62265             this.bottomOffset = this.view.mainHd.getHeight();\r
62266         }\r
62267         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);\r
62268         this.proxyBottom.show();\r
62269         return pt;\r
62270     },\r
62271 \r
62272     onNodeEnter : function(n, dd, e, data){\r
62273         if(data.header != n){\r
62274             this.positionIndicator(data.header, n, e);\r
62275         }\r
62276     },\r
62277 \r
62278     onNodeOver : function(n, dd, e, data){\r
62279         var result = false;\r
62280         if(data.header != n){\r
62281             result = this.positionIndicator(data.header, n, e);\r
62282         }\r
62283         if(!result){\r
62284             this.proxyTop.hide();\r
62285             this.proxyBottom.hide();\r
62286         }\r
62287         return result ? this.dropAllowed : this.dropNotAllowed;\r
62288     },\r
62289 \r
62290     onNodeOut : function(n, dd, e, data){\r
62291         this.proxyTop.hide();\r
62292         this.proxyBottom.hide();\r
62293     },\r
62294 \r
62295     onNodeDrop : function(n, dd, e, data){\r
62296         var h = data.header;\r
62297         if(h != n){\r
62298             var cm = this.grid.colModel;\r
62299             var x = Ext.lib.Event.getPageX(e);\r
62300             var r = Ext.lib.Dom.getRegion(n.firstChild);\r
62301             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";\r
62302             var oldIndex = this.view.getCellIndex(h);\r
62303             var newIndex = this.view.getCellIndex(n);\r
62304             if(pt == "after"){\r
62305                 newIndex++;\r
62306             }\r
62307             if(oldIndex < newIndex){\r
62308                 newIndex--;\r
62309             }\r
62310             cm.moveColumn(oldIndex, newIndex);\r
62311             this.grid.fireEvent("columnmove", oldIndex, newIndex);\r
62312             return true;\r
62313         }\r
62314         return false;\r
62315     }\r
62316 });\r
62317 \r
62318 \r
62319 Ext.grid.GridView.ColumnDragZone = function(grid, hd){\r
62320     Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);\r
62321     this.proxy.el.addClass('x-grid3-col-dd');\r
62322 };\r
62323 \r
62324 Ext.extend(Ext.grid.GridView.ColumnDragZone, Ext.grid.HeaderDragZone, {\r
62325     handleMouseDown : function(e){\r
62326 \r
62327     },\r
62328 \r
62329     callHandleMouseDown : function(e){\r
62330         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);\r
62331     }\r
62332 });// private
62333 // This is a support class used internally by the Grid components
62334 Ext.grid.SplitDragZone = function(grid, hd, hd2){
62335     this.grid = grid;
62336     this.view = grid.getView();
62337     this.proxy = this.view.resizeProxy;
62338     Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
62339         "gridSplitters" + this.grid.getGridEl().id, {
62340         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
62341     });
62342     this.setHandleElId(Ext.id(hd));
62343     this.setOuterHandleElId(Ext.id(hd2));
62344     this.scroll = false;
62345 };
62346 Ext.extend(Ext.grid.SplitDragZone, Ext.dd.DDProxy, {
62347     fly: Ext.Element.fly,
62348
62349     b4StartDrag : function(x, y){
62350         this.view.headersDisabled = true;
62351         this.proxy.setHeight(this.view.mainWrap.getHeight());
62352         var w = this.cm.getColumnWidth(this.cellIndex);
62353         var minw = Math.max(w-this.grid.minColumnWidth, 0);
62354         this.resetConstraints();
62355         this.setXConstraint(minw, 1000);
62356         this.setYConstraint(0, 0);
62357         this.minX = x - minw;
62358         this.maxX = x + 1000;
62359         this.startPos = x;
62360         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
62361     },
62362
62363
62364     handleMouseDown : function(e){
62365         var ev = Ext.EventObject.setEvent(e);
62366         var t = this.fly(ev.getTarget());
62367         if(t.hasClass("x-grid-split")){
62368             this.cellIndex = this.view.getCellIndex(t.dom);
62369             this.split = t.dom;
62370             this.cm = this.grid.colModel;
62371             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
62372                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
62373             }
62374         }
62375     },
62376
62377     endDrag : function(e){
62378         this.view.headersDisabled = false;
62379         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
62380         var diff = endX - this.startPos;
62381         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
62382     },
62383
62384     autoOffset : function(){
62385         this.setDelta(0,0);
62386     }
62387 });/**
62388  * @class Ext.grid.GridDragZone
62389  * @extends Ext.dd.DragZone
62390  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
62391  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
62392  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
62393  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
62394  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
62395  * to process the {@link #getDragData data} which is provided.
62396  */
62397 Ext.grid.GridDragZone = function(grid, config){
62398     this.view = grid.getView();
62399     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
62400     this.scroll = false;
62401     this.grid = grid;
62402     this.ddel = document.createElement('div');
62403     this.ddel.className = 'x-grid-dd-wrap';
62404 };
62405
62406 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
62407     ddGroup : "GridDD",
62408
62409     /**
62410      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
62411      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
62412      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
62413      * <p>The data object contains the following properties:<ul>
62414      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
62415      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
62416      * <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>
62417      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
62418      * </ul></p>
62419      */
62420     getDragData : function(e){
62421         var t = Ext.lib.Event.getTarget(e);
62422         var rowIndex = this.view.findRowIndex(t);
62423         if(rowIndex !== false){
62424             var sm = this.grid.selModel;
62425             if(!sm.isSelected(rowIndex) || e.hasModifier()){
62426                 sm.handleMouseDown(this.grid, rowIndex, e);
62427             }
62428             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
62429         }
62430         return false;
62431     },
62432
62433     /**
62434      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
62435      * of the data being dragged.</p>
62436      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
62437      */
62438     onInitDrag : function(e){
62439         var data = this.dragData;
62440         this.ddel.innerHTML = this.grid.getDragDropText();
62441         this.proxy.update(this.ddel);
62442         // fire start drag?
62443     },
62444
62445     /**
62446      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
62447      * the selected rows to show that they have not been dragged.
62448      */
62449     afterRepair : function(){
62450         this.dragging = false;
62451     },
62452
62453     /**
62454      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
62455      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
62456      * @param {EventObject} e The mouse up event
62457      * @return {Array} The xy location (e.g. [100, 200])
62458      */
62459     getRepairXY : function(e, data){
62460         return false;
62461     },
62462
62463     onEndDrag : function(data, e){
62464         // fire end drag?
62465     },
62466
62467     onValidDrop : function(dd, e, id){
62468         // fire drag drop?
62469         this.hideProxy();
62470     },
62471
62472     beforeInvalidDrop : function(e, id){
62473
62474     }
62475 });
62476 /**
62477  * @class Ext.grid.ColumnModel
62478  * @extends Ext.util.Observable
62479  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
62480  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
62481  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
62482  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
62483  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
62484  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
62485  * <pre><code>
62486 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
62487  * </code></pre>
62488  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
62489  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
62490  * each record in the store is indexed into the ColumnModel.</p>
62491  * <p>There are two ways to initialize the ColumnModel class:</p>
62492  * <p><u>Initialization Method 1: an Array</u></p>
62493 <pre><code>
62494  var colModel = new Ext.grid.ColumnModel([
62495     { header: "Ticker", width: 60, sortable: true},
62496     { header: "Company Name", width: 150, sortable: true, id: 'company'},
62497     { header: "Market Cap.", width: 100, sortable: true},
62498     { header: "$ Sales", width: 100, sortable: true, renderer: money},
62499     { header: "Employees", width: 100, sortable: true, resizable: false}
62500  ]);
62501  </code></pre>
62502  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
62503  * objects to define the initial layout / display of the columns in the Grid. The order of each
62504  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
62505  * order of the column display.  A Column's display may be initially hidden using the
62506  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
62507  * header menu).  Field's that are not included in the ColumnModel will not be displayable at all.</p>
62508  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
62509  * {@link Ext.data.Store Store} the column draws its data from is configured through the
62510  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
62511  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
62512  * example above) it will use the column configuration's index in the Array as the index.</p>
62513  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
62514  * <p><u>Initialization Method 2: an Object</u></p>
62515  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
62516  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
62517  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
62518  * for all columns, e.g.:</p><pre><code>
62519  var colModel = new Ext.grid.ColumnModel({
62520     columns: [
62521         { header: "Ticker", width: 60, menuDisabled: false},
62522         { header: "Company Name", width: 150, id: 'company'},
62523         { header: "Market Cap."},
62524         { header: "$ Sales", renderer: money},
62525         { header: "Employees", resizable: false}
62526     ],
62527     defaults: {
62528         sortable: true,
62529         menuDisabled: true,
62530         width: 100
62531     },
62532     listeners: {
62533         {@link #hiddenchange}: function(cm, colIndex, hidden) {
62534             saveConfig(colIndex, hidden);
62535         }
62536     }
62537 });
62538  </code></pre>
62539  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
62540  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
62541  * option. This column could be styled by including the following css:</p><pre><code>
62542  //add this css *after* the core css is loaded
62543 .x-grid3-td-company {
62544     color: red; // entire column will have red font
62545 }
62546 // modify the header row only, adding an icon to the column header
62547 .x-grid3-hd-company {
62548     background: transparent
62549         url(../../resources/images/icons/silk/building.png)
62550         no-repeat 3px 3px ! important;
62551         padding-left:20px;
62552 }
62553  </code></pre>
62554  * Note that the "Company Name" column could be specified as the
62555  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
62556  * @constructor
62557  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
62558  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
62559  */
62560 Ext.grid.ColumnModel = function(config){
62561     /**
62562      * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
62563      * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
62564      * be specified.
62565      * @property config
62566      * @type Array
62567      */
62568     if(config.columns){
62569         Ext.apply(this, config);
62570         this.setConfig(config.columns, true);
62571     }else{
62572         this.setConfig(config, true);
62573     }
62574     this.addEvents(
62575         /**
62576          * @event widthchange
62577          * Fires when the width of a column is programmaticially changed using
62578          * <code>{@link #setColumnWidth}</code>.
62579          * Note internal resizing suppresses the event from firing. See also
62580          * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
62581          * @param {ColumnModel} this
62582          * @param {Number} columnIndex The column index
62583          * @param {Number} newWidth The new width
62584          */
62585         "widthchange",
62586         /**
62587          * @event headerchange
62588          * Fires when the text of a header changes.
62589          * @param {ColumnModel} this
62590          * @param {Number} columnIndex The column index
62591          * @param {String} newText The new header text
62592          */
62593         "headerchange",
62594         /**
62595          * @event hiddenchange
62596          * Fires when a column is hidden or "unhidden".
62597          * @param {ColumnModel} this
62598          * @param {Number} columnIndex The column index
62599          * @param {Boolean} hidden true if hidden, false otherwise
62600          */
62601         "hiddenchange",
62602         /**
62603          * @event columnmoved
62604          * Fires when a column is moved.
62605          * @param {ColumnModel} this
62606          * @param {Number} oldIndex
62607          * @param {Number} newIndex
62608          */
62609         "columnmoved",
62610         /**
62611          * @event configchange
62612          * Fires when the configuration is changed
62613          * @param {ColumnModel} this
62614          */
62615         "configchange"
62616     );
62617     Ext.grid.ColumnModel.superclass.constructor.call(this);
62618 };
62619 Ext.extend(Ext.grid.ColumnModel, Ext.util.Observable, {
62620     /**
62621      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
62622      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
62623      * <tt><b>{@link #defaults}</b></tt> config property.
62624      */
62625     defaultWidth: 100,
62626     /**
62627      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
62628      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
62629      * through the <tt><b>{@link #defaults}</b></tt> config property.
62630      */
62631     defaultSortable: false,
62632     /**
62633      * @cfg {Array} columns An Array of object literals.  The config options defined by
62634      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
62635      * individual column definition.
62636      */
62637     /**
62638      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
62639      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
62640      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
62641      */
62642
62643     /**
62644      * Returns the id of the column at the specified index.
62645      * @param {Number} index The column index
62646      * @return {String} the id
62647      */
62648     getColumnId : function(index){
62649         return this.config[index].id;
62650     },
62651
62652     getColumnAt : function(index){
62653         return this.config[index];
62654     },
62655
62656     /**
62657      * <p>Reconfigures this column model according to the passed Array of column definition objects.
62658      * For a description of the individual properties of a column definition object, see the
62659      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
62660      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
62661      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
62662      * @param {Array} config Array of Column definition objects.
62663      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
62664      * and destroys existing editors.
62665      */
62666     setConfig : function(config, initial){
62667         var i, c, len;
62668         if(!initial){ // cleanup
62669             delete this.totalWidth;
62670             for(i = 0, len = this.config.length; i < len; i++){
62671                 c = this.config[i];
62672                 if(c.editor){
62673                     c.editor.destroy();
62674                 }
62675             }
62676         }
62677
62678         // backward compatibility
62679         this.defaults = Ext.apply({
62680             width: this.defaultWidth,
62681             sortable: this.defaultSortable
62682         }, this.defaults);
62683
62684         this.config = config;
62685         this.lookup = {};
62686         // if no id, create one
62687         for(i = 0, len = config.length; i < len; i++){
62688             c = Ext.applyIf(config[i], this.defaults);
62689             if(!c.isColumn){
62690                 var cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
62691                 c = new cls(c);
62692                 config[i] = c;
62693             }
62694             this.lookup[c.id] = c;
62695         }
62696         if(!initial){
62697             this.fireEvent('configchange', this);
62698         }
62699     },
62700
62701     /**
62702      * Returns the column for a specified id.
62703      * @param {String} id The column id
62704      * @return {Object} the column
62705      */
62706     getColumnById : function(id){
62707         return this.lookup[id];
62708     },
62709
62710     /**
62711      * Returns the index for a specified column id.
62712      * @param {String} id The column id
62713      * @return {Number} the index, or -1 if not found
62714      */
62715     getIndexById : function(id){
62716         for(var i = 0, len = this.config.length; i < len; i++){
62717             if(this.config[i].id == id){
62718                 return i;
62719             }
62720         }
62721         return -1;
62722     },
62723
62724     /**
62725      * Moves a column from one position to another.
62726      * @param {Number} oldIndex The index of the column to move.
62727      * @param {Number} newIndex The position at which to reinsert the coolumn.
62728      */
62729     moveColumn : function(oldIndex, newIndex){
62730         var c = this.config[oldIndex];
62731         this.config.splice(oldIndex, 1);
62732         this.config.splice(newIndex, 0, c);
62733         this.dataMap = null;
62734         this.fireEvent("columnmoved", this, oldIndex, newIndex);
62735     },
62736
62737     /**
62738      * Returns the number of columns.
62739      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
62740      * @return {Number}
62741      */
62742     getColumnCount : function(visibleOnly){
62743         if(visibleOnly === true){
62744             var c = 0;
62745             for(var i = 0, len = this.config.length; i < len; i++){
62746                 if(!this.isHidden(i)){
62747                     c++;
62748                 }
62749             }
62750             return c;
62751         }
62752         return this.config.length;
62753     },
62754
62755     /**
62756      * Returns the column configs that return true by the passed function that is called
62757      * with (columnConfig, index)
62758 <pre><code>
62759 // returns an array of column config objects for all hidden columns
62760 var columns = grid.getColumnModel().getColumnsBy(function(c){
62761   return c.hidden;
62762 });
62763 </code></pre>
62764      * @param {Function} fn
62765      * @param {Object} scope (optional)
62766      * @return {Array} result
62767      */
62768     getColumnsBy : function(fn, scope){
62769         var r = [];
62770         for(var i = 0, len = this.config.length; i < len; i++){
62771             var c = this.config[i];
62772             if(fn.call(scope||this, c, i) === true){
62773                 r[r.length] = c;
62774             }
62775         }
62776         return r;
62777     },
62778
62779     /**
62780      * Returns true if the specified column is sortable.
62781      * @param {Number} col The column index
62782      * @return {Boolean}
62783      */
62784     isSortable : function(col){
62785         return this.config[col].sortable;
62786     },
62787
62788     /**
62789      * Returns true if the specified column menu is disabled.
62790      * @param {Number} col The column index
62791      * @return {Boolean}
62792      */
62793     isMenuDisabled : function(col){
62794         return !!this.config[col].menuDisabled;
62795     },
62796
62797     /**
62798      * Returns the rendering (formatting) function defined for the column.
62799      * @param {Number} col The column index.
62800      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
62801      */
62802     getRenderer : function(col){
62803         if(!this.config[col].renderer){
62804             return Ext.grid.ColumnModel.defaultRenderer;
62805         }
62806         return this.config[col].renderer;
62807     },
62808
62809     /**
62810      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
62811      * default formatting functions.
62812      * @param {Number} col The column index
62813      * @param {Function} fn The function to use to process the cell's raw data
62814      * to return HTML markup for the grid view. The render function is called with
62815      * the following parameters:<ul>
62816      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
62817      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
62818      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
62819      * <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
62820      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
62821      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
62822      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
62823      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
62824      * <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>
62825      */
62826     setRenderer : function(col, fn){
62827         this.config[col].renderer = fn;
62828     },
62829
62830     /**
62831      * Returns the width for the specified column.
62832      * @param {Number} col The column index
62833      * @return {Number}
62834      */
62835     getColumnWidth : function(col){
62836         return this.config[col].width;
62837     },
62838
62839     /**
62840      * Sets the width for a column.
62841      * @param {Number} col The column index
62842      * @param {Number} width The new width
62843      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
62844      * event. Defaults to false.
62845      */
62846     setColumnWidth : function(col, width, suppressEvent){
62847         this.config[col].width = width;
62848         this.totalWidth = null;
62849         if(!suppressEvent){
62850              this.fireEvent("widthchange", this, col, width);
62851         }
62852     },
62853
62854     /**
62855      * Returns the total width of all columns.
62856      * @param {Boolean} includeHidden True to include hidden column widths
62857      * @return {Number}
62858      */
62859     getTotalWidth : function(includeHidden){
62860         if(!this.totalWidth){
62861             this.totalWidth = 0;
62862             for(var i = 0, len = this.config.length; i < len; i++){
62863                 if(includeHidden || !this.isHidden(i)){
62864                     this.totalWidth += this.getColumnWidth(i);
62865                 }
62866             }
62867         }
62868         return this.totalWidth;
62869     },
62870
62871     /**
62872      * Returns the header for the specified column.
62873      * @param {Number} col The column index
62874      * @return {String}
62875      */
62876     getColumnHeader : function(col){
62877         return this.config[col].header;
62878     },
62879
62880     /**
62881      * Sets the header for a column.
62882      * @param {Number} col The column index
62883      * @param {String} header The new header
62884      */
62885     setColumnHeader : function(col, header){
62886         this.config[col].header = header;
62887         this.fireEvent("headerchange", this, col, header);
62888     },
62889
62890     /**
62891      * Returns the tooltip for the specified column.
62892      * @param {Number} col The column index
62893      * @return {String}
62894      */
62895     getColumnTooltip : function(col){
62896             return this.config[col].tooltip;
62897     },
62898     /**
62899      * Sets the tooltip for a column.
62900      * @param {Number} col The column index
62901      * @param {String} tooltip The new tooltip
62902      */
62903     setColumnTooltip : function(col, tooltip){
62904             this.config[col].tooltip = tooltip;
62905     },
62906
62907     /**
62908      * Returns the dataIndex for the specified column.
62909 <pre><code>
62910 // Get field name for the column
62911 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
62912 </code></pre>
62913      * @param {Number} col The column index
62914      * @return {String} The column's dataIndex
62915      */
62916     getDataIndex : function(col){
62917         return this.config[col].dataIndex;
62918     },
62919
62920     /**
62921      * Sets the dataIndex for a column.
62922      * @param {Number} col The column index
62923      * @param {String} dataIndex The new dataIndex
62924      */
62925     setDataIndex : function(col, dataIndex){
62926         this.config[col].dataIndex = dataIndex;
62927     },
62928
62929     /**
62930      * Finds the index of the first matching column for the given dataIndex.
62931      * @param {String} col The dataIndex to find
62932      * @return {Number} The column index, or -1 if no match was found
62933      */
62934     findColumnIndex : function(dataIndex){
62935         var c = this.config;
62936         for(var i = 0, len = c.length; i < len; i++){
62937             if(c[i].dataIndex == dataIndex){
62938                 return i;
62939             }
62940         }
62941         return -1;
62942     },
62943
62944     /**
62945      * Returns true if the cell is editable.
62946 <pre><code>
62947 var store = new Ext.data.Store({...});
62948 var colModel = new Ext.grid.ColumnModel({
62949   columns: [...],
62950   isCellEditable: function(col, row) {
62951     var record = store.getAt(row);
62952     if (record.get('readonly')) { // replace with your condition
62953       return false;
62954     }
62955     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
62956   }
62957 });
62958 var grid = new Ext.grid.GridPanel({
62959   store: store,
62960   colModel: colModel,
62961   ...
62962 });
62963 </code></pre>
62964      * @param {Number} colIndex The column index
62965      * @param {Number} rowIndex The row index
62966      * @return {Boolean}
62967      */
62968     isCellEditable : function(colIndex, rowIndex){
62969         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
62970     },
62971
62972     /**
62973      * Returns the editor defined for the cell/column.
62974      * @param {Number} colIndex The column index
62975      * @param {Number} rowIndex The row index
62976      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
62977      * the {@link Ext.form.Field Field} used to edit the cell.
62978      */
62979     getCellEditor : function(colIndex, rowIndex){
62980         return this.config[colIndex].getCellEditor(rowIndex);
62981     },
62982
62983     /**
62984      * Sets if a column is editable.
62985      * @param {Number} col The column index
62986      * @param {Boolean} editable True if the column is editable
62987      */
62988     setEditable : function(col, editable){
62989         this.config[col].editable = editable;
62990     },
62991
62992
62993     /**
62994      * Returns true if the column is hidden.
62995      * @param {Number} colIndex The column index
62996      * @return {Boolean}
62997      */
62998     isHidden : function(colIndex){
62999         return this.config[colIndex].hidden;
63000     },
63001
63002
63003     /**
63004      * Returns true if the column width cannot be changed
63005      */
63006     isFixed : function(colIndex){
63007         return this.config[colIndex].fixed;
63008     },
63009
63010     /**
63011      * Returns true if the column can be resized
63012      * @return {Boolean}
63013      */
63014     isResizable : function(colIndex){
63015         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
63016     },
63017     /**
63018      * Sets if a column is hidden.
63019 <pre><code>
63020 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
63021 </code></pre>
63022      * @param {Number} colIndex The column index
63023      * @param {Boolean} hidden True if the column is hidden
63024      */
63025     setHidden : function(colIndex, hidden){
63026         var c = this.config[colIndex];
63027         if(c.hidden !== hidden){
63028             c.hidden = hidden;
63029             this.totalWidth = null;
63030             this.fireEvent("hiddenchange", this, colIndex, hidden);
63031         }
63032     },
63033
63034     /**
63035      * Sets the editor for a column and destroys the prior editor.
63036      * @param {Number} col The column index
63037      * @param {Object} editor The editor object
63038      */
63039     setEditor : function(col, editor){
63040         Ext.destroy(this.config[col].editor);
63041         this.config[col].editor = editor;
63042     },
63043
63044     /**
63045      * Destroys this column model by purging any event listeners, and removing any editors.
63046      */
63047     destroy : function(){
63048         for(var i = 0, c = this.config, len = c.length; i < len; i++){
63049             Ext.destroy(c[i].editor);
63050         }
63051         this.purgeListeners();
63052     }
63053 });
63054
63055 // private
63056 Ext.grid.ColumnModel.defaultRenderer = function(value){
63057     if(typeof value == "string" && value.length < 1){
63058         return "&#160;";
63059     }
63060     return value;
63061 };/**\r
63062  * @class Ext.grid.AbstractSelectionModel\r
63063  * @extends Ext.util.Observable\r
63064  * Abstract base class for grid SelectionModels.  It provides the interface that should be\r
63065  * implemented by descendant classes.  This class should not be directly instantiated.\r
63066  * @constructor\r
63067  */\r
63068 Ext.grid.AbstractSelectionModel = function(){\r
63069     this.locked = false;\r
63070     Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);\r
63071 };\r
63072 \r
63073 Ext.extend(Ext.grid.AbstractSelectionModel, Ext.util.Observable,  {\r
63074     /**\r
63075      * The GridPanel for which this SelectionModel is handling selection. Read-only.\r
63076      * @type Object\r
63077      * @property grid\r
63078      */\r
63079 \r
63080     /** @ignore Called by the grid automatically. Do not call directly. */\r
63081     init : function(grid){\r
63082         this.grid = grid;\r
63083         this.initEvents();\r
63084     },\r
63085 \r
63086     /**\r
63087      * Locks the selections.\r
63088      */\r
63089     lock : function(){\r
63090         this.locked = true;\r
63091     },\r
63092 \r
63093     /**\r
63094      * Unlocks the selections.\r
63095      */\r
63096     unlock : function(){\r
63097         this.locked = false;\r
63098     },\r
63099 \r
63100     /**\r
63101      * Returns true if the selections are locked.\r
63102      * @return {Boolean}\r
63103      */\r
63104     isLocked : function(){\r
63105         return this.locked;\r
63106     },\r
63107     \r
63108     destroy: function(){\r
63109         this.purgeListeners();\r
63110     }\r
63111 });/**
63112  * @class Ext.grid.RowSelectionModel
63113  * @extends Ext.grid.AbstractSelectionModel
63114  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
63115  * It supports multiple selections and keyboard selection/navigation. The objects stored
63116  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
63117  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
63118  * @constructor
63119  * @param {Object} config
63120  */
63121 Ext.grid.RowSelectionModel = function(config){
63122     Ext.apply(this, config);
63123     this.selections = new Ext.util.MixedCollection(false, function(o){
63124         return o.id;
63125     });
63126
63127     this.last = false;
63128     this.lastActive = false;
63129
63130     this.addEvents(
63131         /**
63132          * @event selectionchange
63133          * Fires when the selection changes
63134          * @param {SelectionModel} this
63135          */
63136         "selectionchange",
63137         /**
63138          * @event beforerowselect
63139          * Fires before a row is selected, return false to cancel the selection.
63140          * @param {SelectionModel} this
63141          * @param {Number} rowIndex The index to be selected
63142          * @param {Boolean} keepExisting False if other selections will be cleared
63143          * @param {Record} record The record to be selected
63144          */
63145         "beforerowselect",
63146         /**
63147          * @event rowselect
63148          * Fires when a row is selected.
63149          * @param {SelectionModel} this
63150          * @param {Number} rowIndex The selected index
63151          * @param {Ext.data.Record} r The selected record
63152          */
63153         "rowselect",
63154         /**
63155          * @event rowdeselect
63156          * Fires when a row is deselected.  To prevent deselection
63157          * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
63158          * @param {SelectionModel} this
63159          * @param {Number} rowIndex
63160          * @param {Record} record
63161          */
63162         "rowdeselect"
63163     );
63164
63165     Ext.grid.RowSelectionModel.superclass.constructor.call(this);
63166 };
63167
63168 Ext.extend(Ext.grid.RowSelectionModel, Ext.grid.AbstractSelectionModel,  {
63169     /**
63170      * @cfg {Boolean} singleSelect
63171      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
63172      * allowing multiple selections)
63173      */
63174     singleSelect : false,
63175
63176     /**
63177      * @cfg {Boolean} moveEditorOnEnter
63178      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
63179      * or the next row up when shift + enter keys are pressed.
63180      */
63181     // private
63182     initEvents : function(){
63183
63184         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
63185             this.grid.on("rowmousedown", this.handleMouseDown, this);
63186         }else{ // allow click to work like normal
63187             this.grid.on("rowclick", function(grid, rowIndex, e) {
63188                 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
63189                     this.selectRow(rowIndex, false);
63190                     grid.view.focusRow(rowIndex);
63191                 }
63192             }, this);
63193         }
63194
63195         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
63196             "up" : function(e){
63197                 if(!e.shiftKey || this.singleSelect){
63198                     this.selectPrevious(false);
63199                 }else if(this.last !== false && this.lastActive !== false){
63200                     var last = this.last;
63201                     this.selectRange(this.last,  this.lastActive-1);
63202                     this.grid.getView().focusRow(this.lastActive);
63203                     if(last !== false){
63204                         this.last = last;
63205                     }
63206                 }else{
63207                     this.selectFirstRow();
63208                 }
63209             },
63210             "down" : function(e){
63211                 if(!e.shiftKey || this.singleSelect){
63212                     this.selectNext(false);
63213                 }else if(this.last !== false && this.lastActive !== false){
63214                     var last = this.last;
63215                     this.selectRange(this.last,  this.lastActive+1);
63216                     this.grid.getView().focusRow(this.lastActive);
63217                     if(last !== false){
63218                         this.last = last;
63219                     }
63220                 }else{
63221                     this.selectFirstRow();
63222                 }
63223             },
63224             scope: this
63225         });
63226
63227         var view = this.grid.view;
63228         view.on("refresh", this.onRefresh, this);
63229         view.on("rowupdated", this.onRowUpdated, this);
63230         view.on("rowremoved", this.onRemove, this);
63231     },
63232
63233     // private
63234     onRefresh : function(){
63235         var ds = this.grid.store, index;
63236         var s = this.getSelections();
63237         this.clearSelections(true);
63238         for(var i = 0, len = s.length; i < len; i++){
63239             var r = s[i];
63240             if((index = ds.indexOfId(r.id)) != -1){
63241                 this.selectRow(index, true);
63242             }
63243         }
63244         if(s.length != this.selections.getCount()){
63245             this.fireEvent("selectionchange", this);
63246         }
63247     },
63248
63249     // private
63250     onRemove : function(v, index, r){
63251         if(this.selections.remove(r) !== false){
63252             this.fireEvent('selectionchange', this);
63253         }
63254     },
63255
63256     // private
63257     onRowUpdated : function(v, index, r){
63258         if(this.isSelected(r)){
63259             v.onRowSelect(index);
63260         }
63261     },
63262
63263     /**
63264      * Select records.
63265      * @param {Array} records The records to select
63266      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
63267      */
63268     selectRecords : function(records, keepExisting){
63269         if(!keepExisting){
63270             this.clearSelections();
63271         }
63272         var ds = this.grid.store;
63273         for(var i = 0, len = records.length; i < len; i++){
63274             this.selectRow(ds.indexOf(records[i]), true);
63275         }
63276     },
63277
63278     /**
63279      * Gets the number of selected rows.
63280      * @return {Number}
63281      */
63282     getCount : function(){
63283         return this.selections.length;
63284     },
63285
63286     /**
63287      * Selects the first row in the grid.
63288      */
63289     selectFirstRow : function(){
63290         this.selectRow(0);
63291     },
63292
63293     /**
63294      * Select the last row.
63295      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
63296      */
63297     selectLastRow : function(keepExisting){
63298         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
63299     },
63300
63301     /**
63302      * Selects the row immediately following the last selected row.
63303      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
63304      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
63305      */
63306     selectNext : function(keepExisting){
63307         if(this.hasNext()){
63308             this.selectRow(this.last+1, keepExisting);
63309             this.grid.getView().focusRow(this.last);
63310             return true;
63311         }
63312         return false;
63313     },
63314
63315     /**
63316      * Selects the row that precedes the last selected row.
63317      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
63318      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
63319      */
63320     selectPrevious : function(keepExisting){
63321         if(this.hasPrevious()){
63322             this.selectRow(this.last-1, keepExisting);
63323             this.grid.getView().focusRow(this.last);
63324             return true;
63325         }
63326         return false;
63327     },
63328
63329     /**
63330      * Returns true if there is a next record to select
63331      * @return {Boolean}
63332      */
63333     hasNext : function(){
63334         return this.last !== false && (this.last+1) < this.grid.store.getCount();
63335     },
63336
63337     /**
63338      * Returns true if there is a previous record to select
63339      * @return {Boolean}
63340      */
63341     hasPrevious : function(){
63342         return !!this.last;
63343     },
63344
63345
63346     /**
63347      * Returns the selected records
63348      * @return {Array} Array of selected records
63349      */
63350     getSelections : function(){
63351         return [].concat(this.selections.items);
63352     },
63353
63354     /**
63355      * Returns the first selected record.
63356      * @return {Record}
63357      */
63358     getSelected : function(){
63359         return this.selections.itemAt(0);
63360     },
63361
63362     /**
63363      * Calls the passed function with each selection. If the function returns
63364      * <tt>false</tt>, iteration is stopped and this function returns
63365      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
63366      * @param {Function} fn
63367      * @param {Object} scope (optional)
63368      * @return {Boolean} true if all selections were iterated
63369      */
63370     each : function(fn, scope){
63371         var s = this.getSelections();
63372         for(var i = 0, len = s.length; i < len; i++){
63373             if(fn.call(scope || this, s[i], i) === false){
63374                 return false;
63375             }
63376         }
63377         return true;
63378     },
63379
63380     /**
63381      * Clears all selections if the selection model
63382      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
63383      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
63384      * conditional checks and events described in {@link #deselectRow}.
63385      */
63386     clearSelections : function(fast){
63387         if(this.isLocked()){
63388             return;
63389         }
63390         if(fast !== true){
63391             var ds = this.grid.store;
63392             var s = this.selections;
63393             s.each(function(r){
63394                 this.deselectRow(ds.indexOfId(r.id));
63395             }, this);
63396             s.clear();
63397         }else{
63398             this.selections.clear();
63399         }
63400         this.last = false;
63401     },
63402
63403
63404     /**
63405      * Selects all rows if the selection model
63406      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
63407      */
63408     selectAll : function(){
63409         if(this.isLocked()){
63410             return;
63411         }
63412         this.selections.clear();
63413         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
63414             this.selectRow(i, true);
63415         }
63416     },
63417
63418     /**
63419      * Returns <tt>true</tt> if there is a selection.
63420      * @return {Boolean}
63421      */
63422     hasSelection : function(){
63423         return this.selections.length > 0;
63424     },
63425
63426     /**
63427      * Returns <tt>true</tt> if the specified row is selected.
63428      * @param {Number/Record} index The record or index of the record to check
63429      * @return {Boolean}
63430      */
63431     isSelected : function(index){
63432         var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
63433         return (r && this.selections.key(r.id) ? true : false);
63434     },
63435
63436     /**
63437      * Returns <tt>true</tt> if the specified record id is selected.
63438      * @param {String} id The id of record to check
63439      * @return {Boolean}
63440      */
63441     isIdSelected : function(id){
63442         return (this.selections.key(id) ? true : false);
63443     },
63444
63445     // private
63446     handleMouseDown : function(g, rowIndex, e){
63447         if(e.button !== 0 || this.isLocked()){
63448             return;
63449         }
63450         var view = this.grid.getView();
63451         if(e.shiftKey && !this.singleSelect && this.last !== false){
63452             var last = this.last;
63453             this.selectRange(last, rowIndex, e.ctrlKey);
63454             this.last = last; // reset the last
63455             view.focusRow(rowIndex);
63456         }else{
63457             var isSelected = this.isSelected(rowIndex);
63458             if(e.ctrlKey && isSelected){
63459                 this.deselectRow(rowIndex);
63460             }else if(!isSelected || this.getCount() > 1){
63461                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
63462                 view.focusRow(rowIndex);
63463             }
63464         }
63465     },
63466
63467     /**
63468      * Selects multiple rows.
63469      * @param {Array} rows Array of the indexes of the row to select
63470      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
63471      * existing selections (defaults to <tt>false</tt>)
63472      */
63473     selectRows : function(rows, keepExisting){
63474         if(!keepExisting){
63475             this.clearSelections();
63476         }
63477         for(var i = 0, len = rows.length; i < len; i++){
63478             this.selectRow(rows[i], true);
63479         }
63480     },
63481
63482     /**
63483      * Selects a range of rows if the selection model
63484      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
63485      * All rows in between startRow and endRow are also selected.
63486      * @param {Number} startRow The index of the first row in the range
63487      * @param {Number} endRow The index of the last row in the range
63488      * @param {Boolean} keepExisting (optional) True to retain existing selections
63489      */
63490     selectRange : function(startRow, endRow, keepExisting){
63491         var i;
63492         if(this.isLocked()){
63493             return;
63494         }
63495         if(!keepExisting){
63496             this.clearSelections();
63497         }
63498         if(startRow <= endRow){
63499             for(i = startRow; i <= endRow; i++){
63500                 this.selectRow(i, true);
63501             }
63502         }else{
63503             for(i = startRow; i >= endRow; i--){
63504                 this.selectRow(i, true);
63505             }
63506         }
63507     },
63508
63509     /**
63510      * Deselects a range of rows if the selection model
63511      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
63512      * All rows in between startRow and endRow are also deselected.
63513      * @param {Number} startRow The index of the first row in the range
63514      * @param {Number} endRow The index of the last row in the range
63515      */
63516     deselectRange : function(startRow, endRow, preventViewNotify){
63517         if(this.isLocked()){
63518             return;
63519         }
63520         for(var i = startRow; i <= endRow; i++){
63521             this.deselectRow(i, preventViewNotify);
63522         }
63523     },
63524
63525     /**
63526      * Selects a row.  Before selecting a row, checks if the selection model
63527      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
63528      * {@link #beforerowselect} event.  If these checks are satisfied the row
63529      * will be selected and followed up by  firing the {@link #rowselect} and
63530      * {@link #selectionchange} events.
63531      * @param {Number} row The index of the row to select
63532      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
63533      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
63534      * prevent notifying the view (disables updating the selected appearance)
63535      */
63536     selectRow : function(index, keepExisting, preventViewNotify){
63537         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
63538             return;
63539         }
63540         var r = this.grid.store.getAt(index);
63541         if(r && this.fireEvent("beforerowselect", this, index, keepExisting, r) !== false){
63542             if(!keepExisting || this.singleSelect){
63543                 this.clearSelections();
63544             }
63545             this.selections.add(r);
63546             this.last = this.lastActive = index;
63547             if(!preventViewNotify){
63548                 this.grid.getView().onRowSelect(index);
63549             }
63550             this.fireEvent("rowselect", this, index, r);
63551             this.fireEvent("selectionchange", this);
63552         }
63553     },
63554
63555     /**
63556      * Deselects a row.  Before deselecting a row, checks if the selection model
63557      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
63558      * If this check is satisfied the row will be deselected and followed up by
63559      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
63560      * @param {Number} row The index of the row to deselect
63561      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
63562      * prevent notifying the view (disables updating the selected appearance)
63563      */
63564     deselectRow : function(index, preventViewNotify){
63565         if(this.isLocked()){
63566             return;
63567         }
63568         if(this.last == index){
63569             this.last = false;
63570         }
63571         if(this.lastActive == index){
63572             this.lastActive = false;
63573         }
63574         var r = this.grid.store.getAt(index);
63575         if(r){
63576             this.selections.remove(r);
63577             if(!preventViewNotify){
63578                 this.grid.getView().onRowDeselect(index);
63579             }
63580             this.fireEvent("rowdeselect", this, index, r);
63581             this.fireEvent("selectionchange", this);
63582         }
63583     },
63584
63585     // private
63586     restoreLast : function(){
63587         if(this._last){
63588             this.last = this._last;
63589         }
63590     },
63591
63592     // private
63593     acceptsNav : function(row, col, cm){
63594         return !cm.isHidden(col) && cm.isCellEditable(col, row);
63595     },
63596
63597     // private
63598     onEditorKey : function(field, e){
63599         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
63600         var shift = e.shiftKey;
63601         if(k == e.TAB){
63602             e.stopEvent();
63603             ed.completeEdit();
63604             if(shift){
63605                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
63606             }else{
63607                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
63608             }
63609         }else if(k == e.ENTER){
63610             e.stopEvent();
63611             ed.completeEdit();
63612             if(this.moveEditorOnEnter !== false){
63613                 if(shift){
63614                     newCell = g.walkCells(ed.row - 1, ed.col, -1, this.acceptsNav, this);
63615                 }else{
63616                     newCell = g.walkCells(ed.row + 1, ed.col, 1, this.acceptsNav, this);
63617                 }
63618             }
63619         }else if(k == e.ESC){
63620             ed.cancelEdit();
63621         }
63622         if(newCell){
63623             g.startEditing(newCell[0], newCell[1]);
63624         }
63625     },
63626     
63627     destroy: function(){
63628         if(this.rowNav){
63629             this.rowNav.disable();
63630             this.rowNav = null;
63631         }
63632         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
63633     }
63634 });/**\r
63635  * @class Ext.grid.Column\r
63636  * <p>This class encapsulates column configuration data to be used in the initialization of a\r
63637  * {@link Ext.grid.ColumnModel ColumnModel}.</p>\r
63638  * <p>While subclasses are provided to render data in different ways, this class renders a passed\r
63639  * data field unchanged and is usually used for textual columns.</p>\r
63640  */\r
63641 Ext.grid.Column = function(config){\r
63642     Ext.apply(this, config);\r
63643 \r
63644     if(typeof this.renderer == 'string'){\r
63645         this.renderer = Ext.util.Format[this.renderer];\r
63646     } else if(Ext.isObject(this.renderer)){\r
63647         this.scope = this.renderer.scope;\r
63648         this.renderer = this.renderer.fn;\r
63649     }\r
63650     this.renderer = this.renderer.createDelegate(this.scope || config);\r
63651 \r
63652     if(this.id === undefined){\r
63653         this.id = ++Ext.grid.Column.AUTO_ID;\r
63654     }\r
63655     if(this.editor){\r
63656         this.editor = Ext.create(this.editor, 'textfield');\r
63657     }\r
63658 };\r
63659 \r
63660 Ext.grid.Column.AUTO_ID = 0;\r
63661 \r
63662 Ext.grid.Column.prototype = {\r
63663     /**\r
63664      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured\r
63665      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.\r
63666      * The initial configuration may be dynamically altered using\r
63667      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.\r
63668      */\r
63669     /**\r
63670      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial\r
63671      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all\r
63672      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be\r
63673      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>\r
63674      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>\r
63675      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>\r
63676      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this\r
63677      * unique identifier.\r
63678      */\r
63679     /**\r
63680      * @cfg {String} header Optional. The header text to be used as innerHTML\r
63681      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to\r
63682      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.\r
63683      */\r
63684     /**\r
63685      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
63686      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,\r
63687      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the\r
63688      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.\r
63689      */\r
63690     /**\r
63691      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
63692      * may be used to specify the text with which to prefix the group field value in the group header line.\r
63693      * See also {@link #groupRenderer} and\r
63694      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.\r
63695      */\r
63696     /**\r
63697      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
63698      * may be used to specify the function used to format the grouping field value for display in the group\r
63699      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured\r
63700      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified\r
63701      * the new value of the group field will be used.</p>\r
63702      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be\r
63703      * passed the following parameters:\r
63704      * <div class="mdetail-params"><ul>\r
63705      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>\r
63706      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>\r
63707      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data\r
63708      * for the row which caused group change.</p></li>\r
63709      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
63710      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>\r
63711      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>\r
63712      * </ul></div></p>\r
63713      * <p>The function should return a string value.</p>\r
63714      */\r
63715     /**\r
63716      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
63717      * may be used to specify the text to display when there is an empty group value. Defaults to the\r
63718      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.\r
63719      */\r
63720     /**\r
63721      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the\r
63722      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from\r
63723      * which to draw the column's value.</p>\r
63724      */\r
63725     /**\r
63726      * @cfg {Number} width\r
63727      * Optional. The initial width in pixels of the column.\r
63728      * The width of each column can also be affected if any of the following are configured:\r
63729      * <div class="mdetail-params"><ul>\r
63730      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>\r
63731      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>\r
63732      * <div class="sub-desc">\r
63733      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be\r
63734      * re-proportioned (based on the relative initial widths) to fill the width of the grid so\r
63735      * that no horizontal scrollbar is shown.</p>\r
63736      * </div></li>\r
63737      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>\r
63738      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>\r
63739      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side\r
63740      * is reserved for the vertical scrollbar.  The\r
63741      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>\r
63742      * can be modified to reduce or eliminate the reserved offset.</p>\r
63743      */\r
63744     /**\r
63745      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.\r
63746      * Defaults to the value of the {@link #defaultSortable} property.\r
63747      * Whether local/remote sorting is used is specified in {@link Ext.data.Store#remoteSort}.\r
63748      */\r
63749     /**\r
63750      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.\r
63751      */\r
63752     /**\r
63753      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.\r
63754      */\r
63755     /**\r
63756      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.\r
63757      */\r
63758     /**\r
63759      * @cfg {Boolean} hidden Optional. <tt>true</tt> to hide the column. Defaults to <tt>false</tt>.\r
63760      */\r
63761     /**\r
63762      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips\r
63763      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the\r
63764      * header's HTML title attribute. Defaults to ''.\r
63765      */\r
63766     /**\r
63767      * @cfg {Mixed} renderer\r
63768      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>\r
63769      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,\r
63770      * appearance, etc.) before it is rendered). This may be specified in either of three ways:\r
63771      * <div class="mdetail-params"><ul>\r
63772      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>\r
63773      * <li>A string which references a property name of the {@link Ext.util.Format} class which\r
63774      * provides a renderer function.</li>\r
63775      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>\r
63776      * reference) e.g.:<pre style="margin-left:1.2em"><code>\r
63777 {\r
63778     fn: this.gridRenderer,\r
63779     scope: this\r
63780 }\r
63781 </code></pre></li></ul></div>\r
63782      * If not specified, the default renderer uses the raw data value.</p>\r
63783      * <p>For information about the renderer function (passed parameters, etc.), see\r
63784      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>\r
63785 var companyColumn = {\r
63786    header: 'Company Name',\r
63787    dataIndex: 'company',\r
63788    renderer: function(value, metaData, record, rowIndex, colIndex, store) {\r
63789       // provide the logic depending on business rules\r
63790       // name of your own choosing to manipulate the cell depending upon\r
63791       // the data in the underlying Record object.\r
63792       if (value == 'whatever') {\r
63793           //metaData.css : String : A CSS class name to add to the TD element of the cell.\r
63794           //metaData.attr : String : An html attribute definition string to apply to\r
63795           //                         the data container element within the table\r
63796           //                         cell (e.g. 'style="color:red;"').\r
63797           metaData.css = 'name-of-css-class-you-will-define';\r
63798       }\r
63799       return value;\r
63800    }\r
63801 }\r
63802      * </code></pre>\r
63803      * See also {@link #scope}.\r
63804      */\r
63805     /**\r
63806      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass\r
63807      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily\r
63808      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:\r
63809      * <div class="mdetail-params"><ul>\r
63810      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>\r
63811      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>\r
63812      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>\r
63813      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>\r
63814      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>\r
63815      * </ul></div>\r
63816      * <p>Configuration properties for the specified <code>xtype</code> may be specified with\r
63817      * the Column configuration properties, for example:</p>\r
63818      * <pre><code>\r
63819 var grid = new Ext.grid.GridPanel({\r
63820     ...\r
63821     columns: [{\r
63822         header: 'Last Updated',\r
63823         dataIndex: 'lastChange',\r
63824         width: 85,\r
63825         sortable: true,\r
63826         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),\r
63827         xtype: 'datecolumn', // use xtype instead of renderer\r
63828         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}\r
63829     }, {\r
63830         ...\r
63831     }]\r
63832 });\r
63833      * </code></pre>\r
63834      */\r
63835     /**\r
63836      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the\r
63837      * renderer.  Defaults to the Column configuration object.\r
63838      */\r
63839     /**\r
63840      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.\r
63841      */\r
63842     /**\r
63843      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column\r
63844      * (excluding headers). Defaults to undefined.\r
63845      */\r
63846     /**\r
63847      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column\r
63848      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use\r
63849      * {@link Ext.grid.GridPanel#enableColumnHide} instead.\r
63850      */\r
63851     /**\r
63852      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column\r
63853      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.\r
63854      */\r
63855 \r
63856     // private. Used by ColumnModel to avoid reprocessing\r
63857     isColumn : true,\r
63858     /**\r
63859      * Optional. A function which returns displayable data when passed the following parameters:\r
63860      * <div class="mdetail-params"><ul>\r
63861      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>\r
63862      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>\r
63863      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>\r
63864      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container\r
63865      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>\r
63866      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was\r
63867      * extracted.</p></li>\r
63868      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>\r
63869      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>\r
63870      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record\r
63871      * was extracted.</p></li>\r
63872      * </ul></div>\r
63873      * @property renderer\r
63874      * @type Function\r
63875      */\r
63876     renderer : function(value){\r
63877         if(typeof value == 'string' && value.length < 1){\r
63878             return '&#160;';\r
63879         }\r
63880         return value;\r
63881     },\r
63882 \r
63883     // private\r
63884     getEditor: function(rowIndex){\r
63885         return this.editable !== false ? this.editor : null;\r
63886     },\r
63887 \r
63888     /**\r
63889      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}\r
63890      * used to edit the cell.\r
63891      * @param {Number} rowIndex The row index\r
63892      * @return {Ext.Editor}\r
63893      */\r
63894     getCellEditor: function(rowIndex){\r
63895         var editor = this.getEditor(rowIndex);\r
63896         if(editor){\r
63897             if(!editor.startEdit){\r
63898                 if(!editor.gridEditor){\r
63899                     editor.gridEditor = new Ext.grid.GridEditor(editor);\r
63900                 }\r
63901                 return editor.gridEditor;\r
63902             }else if(editor.startEdit){\r
63903                 return editor;\r
63904             }\r
63905         }\r
63906         return null;\r
63907     }\r
63908 };\r
63909 \r
63910 /**\r
63911  * @class Ext.grid.BooleanColumn\r
63912  * @extends Ext.grid.Column\r
63913  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.ColumnModel#xtype xtype}\r
63914  * config option of {@link Ext.grid.ColumnModel} for more details.</p>\r
63915  */\r
63916 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {\r
63917     /**\r
63918      * @cfg {String} trueText\r
63919      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).\r
63920      */\r
63921     trueText: 'true',\r
63922     /**\r
63923      * @cfg {String} falseText\r
63924      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to\r
63925      * <tt>'false'</tt>).\r
63926      */\r
63927     falseText: 'false',\r
63928     /**\r
63929      * @cfg {String} undefinedText\r
63930      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).\r
63931      */\r
63932     undefinedText: '&#160;',\r
63933 \r
63934     constructor: function(cfg){\r
63935         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);\r
63936         var t = this.trueText, f = this.falseText, u = this.undefinedText;\r
63937         this.renderer = function(v){\r
63938             if(v === undefined){\r
63939                 return u;\r
63940             }\r
63941             if(!v || v === 'false'){\r
63942                 return f;\r
63943             }\r
63944             return t;\r
63945         };\r
63946     }\r
63947 });\r
63948 \r
63949 /**\r
63950  * @class Ext.grid.NumberColumn\r
63951  * @extends Ext.grid.Column\r
63952  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the\r
63953  * {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel} for more details.</p>\r
63954  */\r
63955 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {\r
63956     /**\r
63957      * @cfg {String} format\r
63958      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column\r
63959      * (defaults to <tt>'0,000.00'</tt>).\r
63960      */\r
63961     format : '0,000.00',\r
63962     constructor: function(cfg){\r
63963         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);\r
63964         this.renderer = Ext.util.Format.numberRenderer(this.format);\r
63965     }\r
63966 });\r
63967 \r
63968 /**\r
63969  * @class Ext.grid.DateColumn\r
63970  * @extends Ext.grid.Column\r
63971  * <p>A Column definition class which renders a passed date according to the default locale, or a configured\r
63972  * {@link #format}. See the {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel}\r
63973  * for more details.</p>\r
63974  */\r
63975 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {\r
63976     /**\r
63977      * @cfg {String} format\r
63978      * A formatting string as used by {@link Date#format} to format a Date for this Column\r
63979      * (defaults to <tt>'m/d/Y'</tt>).\r
63980      */\r
63981     format : 'm/d/Y',\r
63982     constructor: function(cfg){\r
63983         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);\r
63984         this.renderer = Ext.util.Format.dateRenderer(this.format);\r
63985     }\r
63986 });\r
63987 \r
63988 /**\r
63989  * @class Ext.grid.TemplateColumn\r
63990  * @extends Ext.grid.Column\r
63991  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s\r
63992  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.\r
63993  * See the {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel} for more\r
63994  * details.</p>\r
63995  */\r
63996 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {\r
63997     /**\r
63998      * @cfg {String/XTemplate} tpl\r
63999      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a\r
64000      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.\r
64001      */\r
64002     constructor: function(cfg){\r
64003         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);\r
64004         var tpl = typeof Ext.isObject(this.tpl) ? this.tpl : new Ext.XTemplate(this.tpl);\r
64005         this.renderer = function(value, p, r){\r
64006             return tpl.apply(r.data);\r
64007         };\r
64008         this.tpl = tpl;\r
64009     }\r
64010 });\r
64011 \r
64012 /*\r
64013  * @property types\r
64014  * @type Object\r
64015  * @member Ext.grid.Column\r
64016  * @static\r
64017  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced\r
64018  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>\r
64019  * <p>This contains the following properties</p><div class="mdesc-details"><ul>\r
64020  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>\r
64021  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>\r
64022  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>\r
64023  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>\r
64024  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>\r
64025  * </ul></div>\r
64026  */\r
64027 Ext.grid.Column.types = {\r
64028     gridcolumn : Ext.grid.Column,\r
64029     booleancolumn: Ext.grid.BooleanColumn,\r
64030     numbercolumn: Ext.grid.NumberColumn,\r
64031     datecolumn: Ext.grid.DateColumn,\r
64032     templatecolumn: Ext.grid.TemplateColumn\r
64033 };/**
64034  * @class Ext.grid.RowNumberer
64035  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
64036  * an automatic row numbering column.
64037  * <br>Usage:<br>
64038  <pre><code>
64039  // This is a typical column config with the first column providing row numbers
64040  var colModel = new Ext.grid.ColumnModel([
64041     new Ext.grid.RowNumberer(),
64042     {header: "Name", width: 80, sortable: true},
64043     {header: "Code", width: 50, sortable: true},
64044     {header: "Description", width: 200, sortable: true}
64045  ]);
64046  </code></pre>
64047  * @constructor
64048  * @param {Object} config The configuration options
64049  */
64050 Ext.grid.RowNumberer = function(config){
64051     Ext.apply(this, config);
64052     if(this.rowspan){
64053         this.renderer = this.renderer.createDelegate(this);
64054     }
64055 };
64056
64057 Ext.grid.RowNumberer.prototype = {
64058     /**
64059      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
64060      * number column (defaults to '').
64061      */
64062     header: "",
64063     /**
64064      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
64065      */
64066     width: 23,
64067     /**
64068      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
64069      * @hide
64070      */
64071     sortable: false,
64072
64073     // private
64074     fixed:true,
64075     menuDisabled:true,
64076     dataIndex: '',
64077     id: 'numberer',
64078     rowspan: undefined,
64079
64080     // private
64081     renderer : function(v, p, record, rowIndex){
64082         if(this.rowspan){
64083             p.cellAttr = 'rowspan="'+this.rowspan+'"';
64084         }
64085         return rowIndex+1;
64086     }
64087 };/**\r
64088  * @class Ext.grid.CheckboxSelectionModel\r
64089  * @extends Ext.grid.RowSelectionModel\r
64090  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.\r
64091  * @constructor\r
64092  * @param {Object} config The configuration options\r
64093  */\r
64094 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {\r
64095 \r
64096     /**\r
64097      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the\r
64098      * checkbox column (defaults to <tt>false</tt>).\r
64099      */\r
64100     /**\r
64101      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the\r
64102      * checkbox column.  Defaults to:<pre><code>\r
64103      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>\r
64104      * </code></pre>\r
64105      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header\r
64106      * and provides support for automatic check all/none behavior on header click. This string\r
64107      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,\r
64108      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the\r
64109      * <tt>'x-grid3-hd-checker'</tt> class is supplied.\r
64110      */\r
64111     header: '<div class="x-grid3-hd-checker">&#160;</div>',\r
64112     /**\r
64113      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).\r
64114      */\r
64115     width: 20,\r
64116     /**\r
64117      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to\r
64118      * <tt>false</tt>).\r
64119      */\r
64120     sortable: false,\r
64121 \r
64122     // private\r
64123     menuDisabled:true,\r
64124     fixed:true,\r
64125     dataIndex: '',\r
64126     id: 'checker',\r
64127 \r
64128     constructor: function(){\r
64129         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);\r
64130 \r
64131         if(this.checkOnly){\r
64132             this.handleMouseDown = Ext.emptyFn;\r
64133         }\r
64134     },\r
64135 \r
64136     // private\r
64137     initEvents : function(){\r
64138         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);\r
64139         this.grid.on('render', function(){\r
64140             var view = this.grid.getView();\r
64141             view.mainBody.on('mousedown', this.onMouseDown, this);\r
64142             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);\r
64143 \r
64144         }, this);\r
64145     },\r
64146 \r
64147     // private\r
64148     onMouseDown : function(e, t){\r
64149         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click\r
64150             e.stopEvent();\r
64151             var row = e.getTarget('.x-grid3-row');\r
64152             if(row){\r
64153                 var index = row.rowIndex;\r
64154                 if(this.isSelected(index)){\r
64155                     this.deselectRow(index);\r
64156                 }else{\r
64157                     this.selectRow(index, true);\r
64158                 }\r
64159             }\r
64160         }\r
64161     },\r
64162 \r
64163     // private\r
64164     onHdMouseDown : function(e, t){\r
64165         if(t.className == 'x-grid3-hd-checker'){\r
64166             e.stopEvent();\r
64167             var hd = Ext.fly(t.parentNode);\r
64168             var isChecked = hd.hasClass('x-grid3-hd-checker-on');\r
64169             if(isChecked){\r
64170                 hd.removeClass('x-grid3-hd-checker-on');\r
64171                 this.clearSelections();\r
64172             }else{\r
64173                 hd.addClass('x-grid3-hd-checker-on');\r
64174                 this.selectAll();\r
64175             }\r
64176         }\r
64177     },\r
64178 \r
64179     // private\r
64180     renderer : function(v, p, record){\r
64181         return '<div class="x-grid3-row-checker">&#160;</div>';\r
64182     }\r
64183 });/**
64184  * @class Ext.grid.CellSelectionModel
64185  * @extends Ext.grid.AbstractSelectionModel
64186  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
64187  * The object stored as the selection contains the following properties:
64188  * <div class="mdetail-params"><ul>
64189  * <li><b>cell</b> : see {@link #getSelectedCell} 
64190  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
64191  * which provides the data for the row containing the selection</li>
64192  * </ul></div>
64193  * @constructor
64194  * @param {Object} config The object containing the configuration of this model.
64195  */
64196 Ext.grid.CellSelectionModel = function(config){
64197     Ext.apply(this, config);
64198
64199     this.selection = null;
64200
64201     this.addEvents(
64202         /**
64203              * @event beforecellselect
64204              * Fires before a cell is selected, return false to cancel the selection.
64205              * @param {SelectionModel} this
64206              * @param {Number} rowIndex The selected row index
64207              * @param {Number} colIndex The selected cell index
64208              */
64209             "beforecellselect",
64210         /**
64211              * @event cellselect
64212              * Fires when a cell is selected.
64213              * @param {SelectionModel} this
64214              * @param {Number} rowIndex The selected row index
64215              * @param {Number} colIndex The selected cell index
64216              */
64217             "cellselect",
64218         /**
64219              * @event selectionchange
64220              * Fires when the active selection changes.
64221              * @param {SelectionModel} this
64222              * @param {Object} selection null for no selection or an object with two properties
64223          * <div class="mdetail-params"><ul>
64224          * <li><b>cell</b> : see {@link #getSelectedCell} 
64225          * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
64226          * which provides the data for the row containing the selection</p></li>
64227          * </ul></div>
64228              */
64229             "selectionchange"
64230     );
64231
64232     Ext.grid.CellSelectionModel.superclass.constructor.call(this);
64233 };
64234
64235 Ext.extend(Ext.grid.CellSelectionModel, Ext.grid.AbstractSelectionModel,  {
64236
64237     /** @ignore */
64238     initEvents : function(){
64239         this.grid.on("cellmousedown", this.handleMouseDown, this);
64240         this.grid.getGridEl().on(Ext.EventManager.useKeydown ? "keydown" : "keypress", this.handleKeyDown, this);
64241         var view = this.grid.view;
64242         view.on("refresh", this.onViewChange, this);
64243         view.on("rowupdated", this.onRowUpdated, this);
64244         view.on("beforerowremoved", this.clearSelections, this);
64245         view.on("beforerowsinserted", this.clearSelections, this);
64246         if(this.grid.isEditor){
64247             this.grid.on("beforeedit", this.beforeEdit,  this);
64248         }
64249     },
64250
64251         //private
64252     beforeEdit : function(e){
64253         this.select(e.row, e.column, false, true, e.record);
64254     },
64255
64256         //private
64257     onRowUpdated : function(v, index, r){
64258         if(this.selection && this.selection.record == r){
64259             v.onCellSelect(index, this.selection.cell[1]);
64260         }
64261     },
64262
64263         //private
64264     onViewChange : function(){
64265         this.clearSelections(true);
64266     },
64267
64268         /**
64269      * Returns an array containing the row and column indexes of the currently selected cell
64270      * (e.g., [0, 0]), or null if none selected. The array has elements:
64271      * <div class="mdetail-params"><ul>
64272      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
64273      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
64274      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
64275      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
64276      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
64277 // get name
64278 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
64279 // get data value based on name
64280 var data = record.get(fieldName);
64281      * </code></pre></p></li>
64282      * </ul></div>
64283      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
64284          */
64285     getSelectedCell : function(){
64286         return this.selection ? this.selection.cell : null;
64287     },
64288
64289     /**
64290      * If anything is selected, clears all selections and fires the selectionchange event.
64291      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
64292      * being notified about the change.
64293      */
64294     clearSelections : function(preventNotify){
64295         var s = this.selection;
64296         if(s){
64297             if(preventNotify !== true){
64298                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
64299             }
64300             this.selection = null;
64301             this.fireEvent("selectionchange", this, null);
64302         }
64303     },
64304
64305     /**
64306      * Returns <tt>true</tt> if there is a selection.
64307      * @return {Boolean}
64308      */
64309     hasSelection : function(){
64310         return this.selection ? true : false;
64311     },
64312
64313     /** @ignore */
64314     handleMouseDown : function(g, row, cell, e){
64315         if(e.button !== 0 || this.isLocked()){
64316             return;
64317         }
64318         this.select(row, cell);
64319     },
64320
64321     /**
64322      * Selects a cell.  Before selecting a cell, fires the
64323      * {@link #beforecellselect} event.  If this check is satisfied the cell
64324      * will be selected and followed up by  firing the {@link #cellselect} and
64325      * {@link #selectionchange} events.
64326      * @param {Number} rowIndex The index of the row to select
64327      * @param {Number} colIndex The index of the column to select
64328      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
64329      * prevent notifying the view (disables updating the selected appearance)
64330      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
64331      * the specified rowIndex / colIndex from being focused.
64332      * @param {Ext.data.Record} r (optional) The record to select
64333      */
64334     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
64335         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
64336             this.clearSelections();
64337             r = r || this.grid.store.getAt(rowIndex);
64338             this.selection = {
64339                 record : r,
64340                 cell : [rowIndex, colIndex]
64341             };
64342             if(!preventViewNotify){
64343                 var v = this.grid.getView();
64344                 v.onCellSelect(rowIndex, colIndex);
64345                 if(preventFocus !== true){
64346                     v.focusCell(rowIndex, colIndex);
64347                 }
64348             }
64349             this.fireEvent("cellselect", this, rowIndex, colIndex);
64350             this.fireEvent("selectionchange", this, this.selection);
64351         }
64352     },
64353
64354         //private
64355     isSelectable : function(rowIndex, colIndex, cm){
64356         return !cm.isHidden(colIndex);
64357     },
64358
64359     /** @ignore */
64360     handleKeyDown : function(e){
64361         if(!e.isNavKeyPress()){
64362             return;
64363         }
64364         var g = this.grid, s = this.selection;
64365         if(!s){
64366             e.stopEvent();
64367             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
64368             if(cell){
64369                 this.select(cell[0], cell[1]);
64370             }
64371             return;
64372         }
64373         var sm = this;
64374         var walk = function(row, col, step){
64375             return g.walkCells(row, col, step, sm.isSelectable,  sm);
64376         };
64377         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
64378         var newCell;
64379
64380         switch(k){
64381              case e.TAB:
64382                  if(e.shiftKey){
64383                      newCell = walk(r, c-1, -1);
64384                  }else{
64385                      newCell = walk(r, c+1, 1);
64386                  }
64387              break;
64388              case e.DOWN:
64389                  newCell = walk(r+1, c, 1);
64390              break;
64391              case e.UP:
64392                  newCell = walk(r-1, c, -1);
64393              break;
64394              case e.RIGHT:
64395                  newCell = walk(r, c+1, 1);
64396              break;
64397              case e.LEFT:
64398                  newCell = walk(r, c-1, -1);
64399              break;
64400              case e.ENTER:
64401                  if(g.isEditor && !g.editing){
64402                     g.startEditing(r, c);
64403                     e.stopEvent();
64404                     return;
64405                 }
64406              break;
64407         }
64408         if(newCell){
64409             this.select(newCell[0], newCell[1]);
64410             e.stopEvent();
64411         }
64412     },
64413
64414     acceptsNav : function(row, col, cm){
64415         return !cm.isHidden(col) && cm.isCellEditable(col, row);
64416     },
64417
64418     onEditorKey : function(field, e){
64419         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
64420         if(k == e.TAB){
64421             if(e.shiftKey){
64422                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
64423             }else{
64424                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
64425             }
64426             e.stopEvent();
64427         }else if(k == e.ENTER){
64428             ed.completeEdit();
64429             e.stopEvent();
64430         }else if(k == e.ESC){
64431                 e.stopEvent();
64432             ed.cancelEdit();
64433         }
64434         if(newCell){
64435             g.startEditing(newCell[0], newCell[1]);
64436         }
64437     }
64438 });/**
64439  * @class Ext.grid.EditorGridPanel
64440  * @extends Ext.grid.GridPanel
64441  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
64442  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
64443  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
64444  * <p>Editability of columns may be controlled programatically by inserting an implementation
64445  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
64446  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
64447  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
64448  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
64449  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
64450  * transformed data, this must be accounted for).</p>
64451  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
64452  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
64453  * mapping would be an appropriate editor.</p>
64454  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
64455  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
64456  * injected using the {@link #beforeedit} and {@link #afteredit} events.
64457  * @constructor
64458  * @param {Object} config The config object
64459  * @xtype editorgrid
64460  */
64461 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
64462     /**
64463      * @cfg {Number} clicksToEdit
64464      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
64465      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
64466      * editing that cell.</p>
64467      */
64468     clicksToEdit: 2,
64469     
64470     /**
64471     * @cfg {Boolean} forceValidation
64472     * True to force validation even if the value is unmodified (defaults to false)
64473     */
64474     forceValidation: false,
64475
64476     // private
64477     isEditor : true,
64478     // private
64479     detectEdit: false,
64480
64481         /**
64482          * @cfg {Boolean} autoEncode
64483          * True to automatically HTML encode and decode values pre and post edit (defaults to false)
64484          */
64485         autoEncode : false,
64486
64487         /**
64488          * @cfg {Boolean} trackMouseOver @hide
64489          */
64490     // private
64491     trackMouseOver: false, // causes very odd FF errors
64492
64493     // private
64494     initComponent : function(){
64495         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
64496
64497         if(!this.selModel){
64498             /**
64499              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
64500              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
64501              */
64502             this.selModel = new Ext.grid.CellSelectionModel();
64503         }
64504
64505         this.activeEditor = null;
64506
64507             this.addEvents(
64508             /**
64509              * @event beforeedit
64510              * Fires before cell editing is triggered. The edit event object has the following properties <br />
64511              * <ul style="padding:5px;padding-left:16px;">
64512              * <li>grid - This grid</li>
64513              * <li>record - The record being edited</li>
64514              * <li>field - The field name being edited</li>
64515              * <li>value - The value for the field being edited.</li>
64516              * <li>row - The grid row index</li>
64517              * <li>column - The grid column index</li>
64518              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
64519              * </ul>
64520              * @param {Object} e An edit event (see above for description)
64521              */
64522             "beforeedit",
64523             /**
64524              * @event afteredit
64525              * Fires after a cell is edited. The edit event object has the following properties <br />
64526              * <ul style="padding:5px;padding-left:16px;">
64527              * <li>grid - This grid</li>
64528              * <li>record - The record being edited</li>
64529              * <li>field - The field name being edited</li>
64530              * <li>value - The value being set</li>
64531              * <li>originalValue - The original value for the field, before the edit.</li>
64532              * <li>row - The grid row index</li>
64533              * <li>column - The grid column index</li>
64534              * </ul>
64535              *
64536              * <pre><code> 
64537 grid.on('afteredit', afterEdit, this );
64538
64539 function afterEdit(e) {
64540     // execute an XHR to send/commit data to the server, in callback do (if successful):
64541     e.record.commit();
64542 }; 
64543              * </code></pre>
64544              * @param {Object} e An edit event (see above for description)
64545              */
64546             "afteredit",
64547             /**
64548              * @event validateedit
64549              * Fires after a cell is edited, but before the value is set in the record. Return false
64550              * to cancel the change. The edit event object has the following properties <br />
64551              * <ul style="padding:5px;padding-left:16px;">
64552              * <li>grid - This grid</li>
64553              * <li>record - The record being edited</li>
64554              * <li>field - The field name being edited</li>
64555              * <li>value - The value being set</li>
64556              * <li>originalValue - The original value for the field, before the edit.</li>
64557              * <li>row - The grid row index</li>
64558              * <li>column - The grid column index</li>
64559              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
64560              * </ul>
64561              * Usage example showing how to remove the red triangle (dirty record indicator) from some
64562              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
64563              * the edit occurs on a targeted row (for example) and then setting the field's new value
64564              * in the Record directly:
64565              * <pre><code> 
64566 grid.on('validateedit', function(e) {
64567   var myTargetRow = 6;
64568  
64569   if (e.row == myTargetRow) {
64570     e.cancel = true;
64571     e.record.data[e.field] = e.value;
64572   }
64573 });
64574              * </code></pre>
64575              * @param {Object} e An edit event (see above for description)
64576              */
64577             "validateedit"
64578         );
64579     },
64580
64581     // private
64582     initEvents : function(){
64583         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
64584
64585         this.on("bodyscroll", this.stopEditing, this, [true]);
64586         this.on("columnresize", this.stopEditing, this, [true]);
64587
64588         if(this.clicksToEdit == 1){
64589             this.on("cellclick", this.onCellDblClick, this);
64590         }else {
64591             if(this.clicksToEdit == 'auto' && this.view.mainBody){
64592                 this.view.mainBody.on("mousedown", this.onAutoEditClick, this);
64593             }
64594             this.on("celldblclick", this.onCellDblClick, this);
64595         }
64596     },
64597
64598     // private
64599     onCellDblClick : function(g, row, col){
64600         this.startEditing(row, col);
64601     },
64602
64603     // private
64604     onAutoEditClick : function(e, t){
64605         if(e.button !== 0){
64606             return;
64607         }
64608         var row = this.view.findRowIndex(t);
64609         var col = this.view.findCellIndex(t);
64610         if(row !== false && col !== false){
64611             this.stopEditing();
64612             if(this.selModel.getSelectedCell){ // cell sm
64613                 var sc = this.selModel.getSelectedCell();
64614                 if(sc && sc[0] === row && sc[1] === col){
64615                     this.startEditing(row, col);
64616                 }
64617             }else{
64618                 if(this.selModel.isSelected(row)){
64619                     this.startEditing(row, col);
64620                 }
64621             }
64622         }
64623     },
64624
64625     // private
64626     onEditComplete : function(ed, value, startValue){
64627         this.editing = false;
64628         this.activeEditor = null;
64629         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
64630                 var r = ed.record;
64631         var field = this.colModel.getDataIndex(ed.col);
64632         value = this.postEditValue(value, startValue, r, field);
64633         if(this.forceValidation === true || String(value) !== String(startValue)){
64634             var e = {
64635                 grid: this,
64636                 record: r,
64637                 field: field,
64638                 originalValue: startValue,
64639                 value: value,
64640                 row: ed.row,
64641                 column: ed.col,
64642                 cancel:false
64643             };
64644             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
64645                 r.set(field, e.value);
64646                 delete e.cancel;
64647                 this.fireEvent("afteredit", e);
64648             }
64649         }
64650         this.view.focusCell(ed.row, ed.col);
64651     },
64652
64653     /**
64654      * Starts editing the specified for the specified row/column
64655      * @param {Number} rowIndex
64656      * @param {Number} colIndex
64657      */
64658     startEditing : function(row, col){
64659         this.stopEditing();
64660         if(this.colModel.isCellEditable(col, row)){
64661             this.view.ensureVisible(row, col, true);
64662             var r = this.store.getAt(row);
64663             var field = this.colModel.getDataIndex(col);
64664             var e = {
64665                 grid: this,
64666                 record: r,
64667                 field: field,
64668                 value: r.data[field],
64669                 row: row,
64670                 column: col,
64671                 cancel:false
64672             };
64673             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
64674                 this.editing = true;
64675                 var ed = this.colModel.getCellEditor(col, row);
64676                 if(!ed){
64677                     return;
64678                 }
64679                 if(!ed.rendered){
64680                     ed.render(this.view.getEditorParent(ed));
64681                 }
64682                 (function(){ // complex but required for focus issues in safari, ie and opera
64683                     ed.row = row;
64684                     ed.col = col;
64685                     ed.record = r;
64686                     ed.on("complete", this.onEditComplete, this, {single: true});
64687                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
64688                     /**
64689                      * The currently active editor or null
64690                       * @type Ext.Editor
64691                      */
64692                     this.activeEditor = ed;
64693                     var v = this.preEditValue(r, field);
64694                     ed.startEdit(this.view.getCell(row, col).firstChild, v === undefined ? '' : v);
64695                 }).defer(50, this);
64696             }
64697         }
64698     },
64699
64700     // private
64701     preEditValue : function(r, field){
64702         var value = r.data[field];
64703         return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlDecode(value) : value;
64704     },
64705
64706     // private
64707         postEditValue : function(value, originalValue, r, field){
64708                 return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
64709         },
64710
64711     /**
64712      * Stops any active editing
64713      * @param {Boolean} cancel (optional) True to cancel any changes
64714      */
64715     stopEditing : function(cancel){
64716         if(this.activeEditor){
64717             this.activeEditor[cancel === true ? 'cancelEdit' : 'completeEdit']();
64718         }
64719         this.activeEditor = null;
64720     }
64721 });
64722 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
64723 // This is a support class used internally by the Grid components
64724 Ext.grid.GridEditor = function(field, config){
64725     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
64726     field.monitorTab = false;
64727 };
64728
64729 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
64730     alignment: "tl-tl",
64731     autoSize: "width",
64732     hideEl : false,
64733     cls: "x-small-editor x-grid-editor",
64734     shim:false,
64735     shadow:false
64736 });/**
64737  * @class Ext.grid.PropertyRecord
64738  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
64739  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
64740  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
64741  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
64742  * can also be created explicitly as shwon below.  Example usage:
64743  * <pre><code>
64744 var rec = new Ext.grid.PropertyRecord({
64745     name: 'Birthday',
64746     value: new Date(Date.parse('05/26/1972'))
64747 });
64748 // Add record to an already populated grid
64749 grid.store.addSorted(rec);
64750 </code></pre>
64751  * @constructor
64752  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
64753  * will be read automatically by the grid to determine the type of editor to use when displaying it.
64754  */
64755 Ext.grid.PropertyRecord = Ext.data.Record.create([
64756     {name:'name',type:'string'}, 'value'
64757 ]);
64758
64759 /**
64760  * @class Ext.grid.PropertyStore
64761  * @extends Ext.util.Observable
64762  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
64763  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
64764  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
64765  * the grid's data should be accessed from the underlying store via the {@link #store} property.
64766  * @constructor
64767  * @param {Ext.grid.Grid} grid The grid this store will be bound to
64768  * @param {Object} source The source data config object
64769  */
64770 Ext.grid.PropertyStore = function(grid, source){
64771     this.grid = grid;
64772     this.store = new Ext.data.Store({
64773         recordType : Ext.grid.PropertyRecord
64774     });
64775     this.store.on('update', this.onUpdate,  this);
64776     if(source){
64777         this.setSource(source);
64778     }
64779     Ext.grid.PropertyStore.superclass.constructor.call(this);
64780 };
64781 Ext.extend(Ext.grid.PropertyStore, Ext.util.Observable, {
64782     // protected - should only be called by the grid.  Use grid.setSource instead.
64783     setSource : function(o){
64784         this.source = o;
64785         this.store.removeAll();
64786         var data = [];
64787         for(var k in o){
64788             if(this.isEditableValue(o[k])){
64789                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
64790             }
64791         }
64792         this.store.loadRecords({records: data}, {}, true);
64793     },
64794
64795     // private
64796     onUpdate : function(ds, record, type){
64797         if(type == Ext.data.Record.EDIT){
64798             var v = record.data.value;
64799             var oldValue = record.modified.value;
64800             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
64801                 this.source[record.id] = v;
64802                 record.commit();
64803                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
64804             }else{
64805                 record.reject();
64806             }
64807         }
64808     },
64809
64810     // private
64811     getProperty : function(row){
64812        return this.store.getAt(row);
64813     },
64814
64815     // private
64816     isEditableValue: function(val){
64817         if(Ext.isDate(val)){
64818             return true;
64819         }
64820         return !(Ext.isObject(val) || Ext.isFunction(val));
64821     },
64822
64823     // private
64824     setValue : function(prop, value){
64825         this.source[prop] = value;
64826         this.store.getById(prop).set('value', value);
64827     },
64828
64829     // protected - should only be called by the grid.  Use grid.getSource instead.
64830     getSource : function(){
64831         return this.source;
64832     }
64833 });
64834
64835 /**
64836  * @class Ext.grid.PropertyColumnModel
64837  * @extends Ext.grid.ColumnModel
64838  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
64839  * @constructor
64840  * @param {Ext.grid.Grid} grid The grid this store will be bound to
64841  * @param {Object} source The source data config object
64842  */
64843 Ext.grid.PropertyColumnModel = function(grid, store){
64844     var g = Ext.grid,
64845         f = Ext.form;
64846         
64847     this.grid = grid;
64848     g.PropertyColumnModel.superclass.constructor.call(this, [
64849         {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
64850         {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
64851     ]);
64852     this.store = store;
64853
64854     var bfield = new f.Field({
64855         autoCreate: {tag: 'select', children: [
64856             {tag: 'option', value: 'true', html: 'true'},
64857             {tag: 'option', value: 'false', html: 'false'}
64858         ]},
64859         getValue : function(){
64860             return this.el.value == 'true';
64861         }
64862     });
64863     this.editors = {
64864         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
64865         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
64866         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
64867         'boolean' : new g.GridEditor(bfield)
64868     };
64869     this.renderCellDelegate = this.renderCell.createDelegate(this);
64870     this.renderPropDelegate = this.renderProp.createDelegate(this);
64871 };
64872
64873 Ext.extend(Ext.grid.PropertyColumnModel, Ext.grid.ColumnModel, {
64874     // private - strings used for locale support
64875     nameText : 'Name',
64876     valueText : 'Value',
64877     dateFormat : 'm/j/Y',
64878
64879     // private
64880     renderDate : function(dateVal){
64881         return dateVal.dateFormat(this.dateFormat);
64882     },
64883
64884     // private
64885     renderBool : function(bVal){
64886         return bVal ? 'true' : 'false';
64887     },
64888
64889     // private
64890     isCellEditable : function(colIndex, rowIndex){
64891         return colIndex == 1;
64892     },
64893
64894     // private
64895     getRenderer : function(col){
64896         return col == 1 ?
64897             this.renderCellDelegate : this.renderPropDelegate;
64898     },
64899
64900     // private
64901     renderProp : function(v){
64902         return this.getPropertyName(v);
64903     },
64904
64905     // private
64906     renderCell : function(val){
64907         var rv = val;
64908         if(Ext.isDate(val)){
64909             rv = this.renderDate(val);
64910         }else if(typeof val == 'boolean'){
64911             rv = this.renderBool(val);
64912         }
64913         return Ext.util.Format.htmlEncode(rv);
64914     },
64915
64916     // private
64917     getPropertyName : function(name){
64918         var pn = this.grid.propertyNames;
64919         return pn && pn[name] ? pn[name] : name;
64920     },
64921
64922     // private
64923     getCellEditor : function(colIndex, rowIndex){
64924         var p = this.store.getProperty(rowIndex),
64925             n = p.data.name, 
64926             val = p.data.value;
64927         if(this.grid.customEditors[n]){
64928             return this.grid.customEditors[n];
64929         }
64930         if(Ext.isDate(val)){
64931             return this.editors.date;
64932         }else if(typeof val == 'number'){
64933             return this.editors.number;
64934         }else if(typeof val == 'boolean'){
64935             return this.editors['boolean'];
64936         }else{
64937             return this.editors.string;
64938         }
64939     },
64940
64941     // inherit docs
64942     destroy : function(){
64943         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
64944         for(var ed in this.editors){
64945             Ext.destroy(ed);
64946         }
64947     }
64948 });
64949
64950 /**
64951  * @class Ext.grid.PropertyGrid
64952  * @extends Ext.grid.EditorGridPanel
64953  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
64954  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
64955  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
64956  * <pre><code>
64957 var grid = new Ext.grid.PropertyGrid({
64958     title: 'Properties Grid',
64959     autoHeight: true,
64960     width: 300,
64961     renderTo: 'grid-ct',
64962     source: {
64963         "(name)": "My Object",
64964         "Created": new Date(Date.parse('10/15/2006')),
64965         "Available": false,
64966         "Version": .01,
64967         "Description": "A test object"
64968     }
64969 });
64970 </code></pre>
64971  * @constructor
64972  * @param {Object} config The grid config object
64973  */
64974 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
64975     /**
64976     * @cfg {Object} propertyNames An object containing property name/display name pairs.
64977     * If specified, the display name will be shown in the name column instead of the property name.
64978     */
64979     /**
64980     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
64981     */
64982     /**
64983     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
64984     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
64985     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
64986     * associated with a custom input control by specifying a custom editor.  The name of the editor
64987     * type should correspond with the name of the property that will use the editor.  Example usage:
64988     * <pre><code>
64989 var grid = new Ext.grid.PropertyGrid({
64990     ...
64991     customEditors: {
64992         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
64993     },
64994     source: {
64995         'Start Time': '10:00 AM'
64996     }
64997 });
64998 </code></pre>
64999     */
65000
65001     // private config overrides
65002     enableColumnMove:false,
65003     stripeRows:false,
65004     trackMouseOver: false,
65005     clicksToEdit:1,
65006     enableHdMenu : false,
65007     viewConfig : {
65008         forceFit:true
65009     },
65010
65011     // private
65012     initComponent : function(){
65013         this.customEditors = this.customEditors || {};
65014         this.lastEditRow = null;
65015         var store = new Ext.grid.PropertyStore(this);
65016         this.propStore = store;
65017         var cm = new Ext.grid.PropertyColumnModel(this, store);
65018         store.store.sort('name', 'ASC');
65019         this.addEvents(
65020             /**
65021              * @event beforepropertychange
65022              * Fires before a property value changes.  Handlers can return false to cancel the property change
65023              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
65024              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
65025              * as the {@link #source} config property).
65026              * @param {String} recordId The record's id in the data store
65027              * @param {Mixed} value The current edited property value
65028              * @param {Mixed} oldValue The original property value prior to editing
65029              */
65030             'beforepropertychange',
65031             /**
65032              * @event propertychange
65033              * Fires after a property value has changed.
65034              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
65035              * as the {@link #source} config property).
65036              * @param {String} recordId The record's id in the data store
65037              * @param {Mixed} value The current edited property value
65038              * @param {Mixed} oldValue The original property value prior to editing
65039              */
65040             'propertychange'
65041         );
65042         this.cm = cm;
65043         this.ds = store.store;
65044         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
65045
65046                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
65047             if(colIndex === 0){
65048                 this.startEditing.defer(200, this, [rowIndex, 1]);
65049                 return false;
65050             }
65051         }, this);
65052     },
65053
65054     // private
65055     onRender : function(){
65056         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
65057
65058         this.getGridEl().addClass('x-props-grid');
65059     },
65060
65061     // private
65062     afterRender: function(){
65063         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
65064         if(this.source){
65065             this.setSource(this.source);
65066         }
65067     },
65068
65069     /**
65070      * Sets the source data object containing the property data.  The data object can contain one or more name/value
65071      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
65072      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
65073      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
65074      * existing data.  See also the {@link #source} config value.  Example usage:
65075      * <pre><code>
65076 grid.setSource({
65077     "(name)": "My Object",
65078     "Created": new Date(Date.parse('10/15/2006')),  // date type
65079     "Available": false,  // boolean type
65080     "Version": .01,      // decimal type
65081     "Description": "A test object"
65082 });
65083 </code></pre>
65084      * @param {Object} source The data object
65085      */
65086     setSource : function(source){
65087         this.propStore.setSource(source);
65088     },
65089
65090     /**
65091      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
65092      * format of the data object.
65093      * @return {Object} The data object
65094      */
65095     getSource : function(){
65096         return this.propStore.getSource();
65097     }
65098 });
65099 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
65100 /**\r
65101  * @class Ext.grid.GroupingView\r
65102  * @extends Ext.grid.GridView\r
65103  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}\r
65104  * must be used to enable grouping.  Some grouping characteristics may also be configured at the\r
65105  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>\r
65106  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</li>\r
65107  * <li><code>{@link Ext.grid.Column#groupable groupable}</li>\r
65108  * <li><code>{@link Ext.grid.Column#groupName groupName}</li>\r
65109  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</li>\r
65110  * </ul></div>\r
65111  * <p>Sample usage:</p>\r
65112  * <pre><code>\r
65113 var grid = new Ext.grid.GridPanel({\r
65114     // A groupingStore is required for a GroupingView\r
65115     store: new {@link Ext.data.GroupingStore}({\r
65116         autoDestroy: true,\r
65117         reader: reader,\r
65118         data: xg.dummyData,\r
65119         sortInfo: {field: 'company', direction: 'ASC'},\r
65120         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,\r
65121         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,\r
65122         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'\r
65123     }),\r
65124     colModel: new {@link Ext.grid.ColumnModel}({\r
65125         columns:[\r
65126             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},\r
65127             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level\r
65128             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},\r
65129             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},\r
65130             {header: 'Industry', dataIndex: 'industry'},\r
65131             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}\r
65132         ],\r
65133         defaults: {\r
65134             sortable: true,\r
65135             menuDisabled: false,\r
65136             width: 20\r
65137         }\r
65138     }),\r
65139 \r
65140     view: new Ext.grid.GroupingView({\r
65141         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
65142         // custom grouping text template to display the number of items per group\r
65143         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
65144     }),\r
65145 \r
65146     frame:true,\r
65147     width: 700,\r
65148     height: 450,\r
65149     collapsible: true,\r
65150     animCollapse: false,\r
65151     title: 'Grouping Example',\r
65152     iconCls: 'icon-grid',\r
65153     renderTo: document.body\r
65154 });\r
65155  * </code></pre>\r
65156  * @constructor\r
65157  * @param {Object} config\r
65158  */\r
65159 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {\r
65160 \r
65161     /**\r
65162      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column\r
65163      * (defaults to 'Group By This Field').\r
65164      */\r
65165     groupByText : 'Group By This Field',\r
65166     /**\r
65167      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping\r
65168      * (defaults to 'Show in Groups').\r
65169      */\r
65170     showGroupsText : 'Show in Groups',\r
65171     /**\r
65172      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)\r
65173      */\r
65174     hideGroupedColumn : false,\r
65175     /**\r
65176      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value\r
65177      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>\r
65178      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the\r
65179      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).\r
65180      */\r
65181     showGroupName : true,\r
65182     /**\r
65183      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)\r
65184      */\r
65185     startCollapsed : false,\r
65186     /**\r
65187      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)\r
65188      */\r
65189     enableGrouping : true,\r
65190     /**\r
65191      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)\r
65192      */\r
65193     enableGroupingMenu : true,\r
65194     /**\r
65195      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)\r
65196      */\r
65197     enableNoGroups : true,\r
65198     /**\r
65199      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).\r
65200      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.\r
65201      */\r
65202     emptyGroupText : '(None)',\r
65203     /**\r
65204      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)\r
65205      */\r
65206     ignoreAdd : false,\r
65207     /**\r
65208      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).\r
65209      * This is used to format an object which contains the following properties:\r
65210      * <div class="mdetail-params"><ul>\r
65211      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.\r
65212      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>\r
65213      * is specified, it is the result of a call to that function.</p></li>\r
65214      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>\r
65215      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>\r
65216      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>\r
65217      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the\r
65218      * View Element which contains the group.</p></li>\r
65219      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
65220      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data\r
65221      * for the row which caused group change.</p></li>\r
65222      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>\r
65223      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>\r
65224      * </ul></div></p>\r
65225      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>\r
65226 var grid = new Ext.grid.GridPanel({\r
65227     ...\r
65228     view: new Ext.grid.GroupingView({\r
65229         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
65230     }),\r
65231 });\r
65232      * </code></pre>\r
65233      */\r
65234     groupTextTpl : '{text}',\r
65235     /**\r
65236      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for\r
65237      * each column.\r
65238      */\r
65239 \r
65240     // private\r
65241     gidSeed : 1000,\r
65242 \r
65243     // private\r
65244     initTemplates : function(){\r
65245         Ext.grid.GroupingView.superclass.initTemplates.call(this);\r
65246         this.state = {};\r
65247 \r
65248         var sm = this.grid.getSelectionModel();\r
65249         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',\r
65250                 this.onBeforeRowSelect, this);\r
65251 \r
65252         if(!this.startGroup){\r
65253             this.startGroup = new Ext.XTemplate(\r
65254                 '<div id="{groupId}" class="x-grid-group {cls}">',\r
65255                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',\r
65256                     '<div id="{groupId}-bd" class="x-grid-group-body">'\r
65257             );\r
65258         }\r
65259         this.startGroup.compile();\r
65260         this.endGroup = '</div></div>';\r
65261     },\r
65262 \r
65263     // private\r
65264     findGroup : function(el){\r
65265         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);\r
65266     },\r
65267 \r
65268     // private\r
65269     getGroups : function(){\r
65270         return this.hasRows() ? this.mainBody.dom.childNodes : [];\r
65271     },\r
65272 \r
65273     // private\r
65274     onAdd : function(){\r
65275         if(this.enableGrouping && !this.ignoreAdd){\r
65276             var ss = this.getScrollState();\r
65277             this.refresh();\r
65278             this.restoreScroll(ss);\r
65279         }else if(!this.enableGrouping){\r
65280             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);\r
65281         }\r
65282     },\r
65283 \r
65284     // private\r
65285     onRemove : function(ds, record, index, isUpdate){\r
65286         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);\r
65287         var g = document.getElementById(record._groupId);\r
65288         if(g && g.childNodes[1].childNodes.length < 1){\r
65289             Ext.removeNode(g);\r
65290         }\r
65291         this.applyEmptyText();\r
65292     },\r
65293 \r
65294     // private\r
65295     refreshRow : function(record){\r
65296         if(this.ds.getCount()==1){\r
65297             this.refresh();\r
65298         }else{\r
65299             this.isUpdating = true;\r
65300             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);\r
65301             this.isUpdating = false;\r
65302         }\r
65303     },\r
65304 \r
65305     // private\r
65306     beforeMenuShow : function(){\r
65307         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;\r
65308         if((item = items.get('groupBy'))){\r
65309             item.setDisabled(disabled);\r
65310         }\r
65311         if((item = items.get('showGroups'))){\r
65312             item.setDisabled(disabled);\r
65313                     item.setChecked(!!this.getGroupField(), true);\r
65314         }\r
65315     },\r
65316 \r
65317     // private\r
65318     renderUI : function(){\r
65319         Ext.grid.GroupingView.superclass.renderUI.call(this);\r
65320         this.mainBody.on('mousedown', this.interceptMouse, this);\r
65321 \r
65322         if(this.enableGroupingMenu && this.hmenu){\r
65323             this.hmenu.add('-',{\r
65324                 itemId:'groupBy',\r
65325                 text: this.groupByText,\r
65326                 handler: this.onGroupByClick,\r
65327                 scope: this,\r
65328                 iconCls:'x-group-by-icon'\r
65329             });\r
65330             if(this.enableNoGroups){\r
65331                 this.hmenu.add({\r
65332                     itemId:'showGroups',\r
65333                     text: this.showGroupsText,\r
65334                     checked: true,\r
65335                     checkHandler: this.onShowGroupsClick,\r
65336                     scope: this\r
65337                 });\r
65338             }\r
65339             this.hmenu.on('beforeshow', this.beforeMenuShow, this);\r
65340         }\r
65341     },\r
65342 \r
65343     // private\r
65344     onGroupByClick : function(){\r
65345         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));\r
65346         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups\r
65347     },\r
65348 \r
65349     // private\r
65350     onShowGroupsClick : function(mi, checked){\r
65351         if(checked){\r
65352             this.onGroupByClick();\r
65353         }else{\r
65354             this.grid.store.clearGrouping();\r
65355         }\r
65356     },\r
65357 \r
65358     /**\r
65359      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.\r
65360      * @param {String} groupId The groupId assigned to the group (see getGroupId)\r
65361      * @param {Boolean} expanded (optional)\r
65362      */\r
65363     toggleGroup : function(group, expanded){\r
65364         this.grid.stopEditing(true);\r
65365         group = Ext.getDom(group);\r
65366         var gel = Ext.fly(group);\r
65367         expanded = expanded !== undefined ?\r
65368                 expanded : gel.hasClass('x-grid-group-collapsed');\r
65369 \r
65370         this.state[gel.dom.id] = expanded;\r
65371         gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');\r
65372     },\r
65373 \r
65374     /**\r
65375      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.\r
65376      * @param {Boolean} expanded (optional)\r
65377      */\r
65378     toggleAllGroups : function(expanded){\r
65379         var groups = this.getGroups();\r
65380         for(var i = 0, len = groups.length; i < len; i++){\r
65381             this.toggleGroup(groups[i], expanded);\r
65382         }\r
65383     },\r
65384 \r
65385     /**\r
65386      * Expands all grouped rows.\r
65387      */\r
65388     expandAllGroups : function(){\r
65389         this.toggleAllGroups(true);\r
65390     },\r
65391 \r
65392     /**\r
65393      * Collapses all grouped rows.\r
65394      */\r
65395     collapseAllGroups : function(){\r
65396         this.toggleAllGroups(false);\r
65397     },\r
65398 \r
65399     // private\r
65400     interceptMouse : function(e){\r
65401         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);\r
65402         if(hd){\r
65403             e.stopEvent();\r
65404             this.toggleGroup(hd.parentNode);\r
65405         }\r
65406     },\r
65407 \r
65408     // private\r
65409     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){\r
65410         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);\r
65411         if(g === ''){\r
65412             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;\r
65413         }\r
65414         return g;\r
65415     },\r
65416 \r
65417     // private\r
65418     getGroupField : function(){\r
65419         return this.grid.store.getGroupState();\r
65420     },\r
65421     \r
65422     // private\r
65423     afterRender : function(){\r
65424         Ext.grid.GroupingView.superclass.afterRender.call(this);\r
65425         if(this.grid.deferRowRender){\r
65426             this.updateGroupWidths();\r
65427         }\r
65428     },\r
65429 \r
65430     // private\r
65431     renderRows : function(){\r
65432         var groupField = this.getGroupField();\r
65433         var eg = !!groupField;\r
65434         // if they turned off grouping and the last grouped field is hidden\r
65435         if(this.hideGroupedColumn) {\r
65436             var colIndex = this.cm.findColumnIndex(groupField);\r
65437             if(!eg && this.lastGroupField !== undefined) {\r
65438                 this.mainBody.update('');\r
65439                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);\r
65440                 delete this.lastGroupField;\r
65441             }else if (eg && this.lastGroupField === undefined) {\r
65442                 this.lastGroupField = groupField;\r
65443                 this.cm.setHidden(colIndex, true);\r
65444             }else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) {\r
65445                 this.mainBody.update('');\r
65446                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);\r
65447                 this.cm.setHidden(oldIndex, false);\r
65448                 this.lastGroupField = groupField;\r
65449                 this.cm.setHidden(colIndex, true);\r
65450             }\r
65451         }\r
65452         return Ext.grid.GroupingView.superclass.renderRows.apply(\r
65453                     this, arguments);\r
65454     },\r
65455 \r
65456     // private\r
65457     doRender : function(cs, rs, ds, startRow, colCount, stripe){\r
65458         if(rs.length < 1){\r
65459             return '';\r
65460         }\r
65461         var groupField = this.getGroupField(),\r
65462             colIndex = this.cm.findColumnIndex(groupField),\r
65463             g;\r
65464 \r
65465         this.enableGrouping = !!groupField;\r
65466 \r
65467         if(!this.enableGrouping || this.isUpdating){\r
65468             return Ext.grid.GroupingView.superclass.doRender.apply(\r
65469                     this, arguments);\r
65470         }\r
65471         var gstyle = 'width:'+this.getTotalWidth()+';';\r
65472 \r
65473         var gidPrefix = this.grid.getGridEl().id;\r
65474         var cfg = this.cm.config[colIndex];\r
65475         var groupRenderer = cfg.groupRenderer || cfg.renderer;\r
65476         var prefix = this.showGroupName ?\r
65477                      (cfg.groupName || cfg.header)+': ' : '';\r
65478 \r
65479         var groups = [], curGroup, i, len, gid;\r
65480         for(i = 0, len = rs.length; i < len; i++){\r
65481             var rowIndex = startRow + i,\r
65482                 r = rs[i],\r
65483                 gvalue = r.data[groupField];\r
65484                 \r
65485                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);\r
65486             if(!curGroup || curGroup.group != g){\r
65487                 gid = gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(g);\r
65488                 // if state is defined use it, however state is in terms of expanded\r
65489                                 // so negate it, otherwise use the default.\r
65490                                 var isCollapsed  = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;\r
65491                                 var gcls = isCollapsed ? 'x-grid-group-collapsed' : ''; \r
65492                 curGroup = {\r
65493                     group: g,\r
65494                     gvalue: gvalue,\r
65495                     text: prefix + g,\r
65496                     groupId: gid,\r
65497                     startRow: rowIndex,\r
65498                     rs: [r],\r
65499                     cls: gcls,\r
65500                     style: gstyle\r
65501                 };\r
65502                 groups.push(curGroup);\r
65503             }else{\r
65504                 curGroup.rs.push(r);\r
65505             }\r
65506             r._groupId = gid;\r
65507         }\r
65508 \r
65509         var buf = [];\r
65510         for(i = 0, len = groups.length; i < len; i++){\r
65511             g = groups[i];\r
65512             this.doGroupStart(buf, g, cs, ds, colCount);\r
65513             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(\r
65514                     this, cs, g.rs, ds, g.startRow, colCount, stripe);\r
65515 \r
65516             this.doGroupEnd(buf, g, cs, ds, colCount);\r
65517         }\r
65518         return buf.join('');\r
65519     },\r
65520 \r
65521     /**\r
65522      * Dynamically tries to determine the groupId of a specific value\r
65523      * @param {String} value\r
65524      * @return {String} The group id\r
65525      */\r
65526     getGroupId : function(value){\r
65527         var gidPrefix = this.grid.getGridEl().id;\r
65528         var groupField = this.getGroupField();\r
65529         var colIndex = this.cm.findColumnIndex(groupField);\r
65530         var cfg = this.cm.config[colIndex];\r
65531         var groupRenderer = cfg.groupRenderer || cfg.renderer;\r
65532         var gtext = this.getGroup(value, {data:{}}, groupRenderer, 0, colIndex, this.ds);\r
65533         return gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(value);\r
65534     },\r
65535 \r
65536     // private\r
65537     doGroupStart : function(buf, g, cs, ds, colCount){\r
65538         buf[buf.length] = this.startGroup.apply(g);\r
65539     },\r
65540 \r
65541     // private\r
65542     doGroupEnd : function(buf, g, cs, ds, colCount){\r
65543         buf[buf.length] = this.endGroup;\r
65544     },\r
65545 \r
65546     // private\r
65547     getRows : function(){\r
65548         if(!this.enableGrouping){\r
65549             return Ext.grid.GroupingView.superclass.getRows.call(this);\r
65550         }\r
65551         var r = [];\r
65552         var g, gs = this.getGroups();\r
65553         for(var i = 0, len = gs.length; i < len; i++){\r
65554             g = gs[i].childNodes[1].childNodes;\r
65555             for(var j = 0, jlen = g.length; j < jlen; j++){\r
65556                 r[r.length] = g[j];\r
65557             }\r
65558         }\r
65559         return r;\r
65560     },\r
65561 \r
65562     // private\r
65563     updateGroupWidths : function(){\r
65564         if(!this.enableGrouping || !this.hasRows()){\r
65565             return;\r
65566         }\r
65567         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.scrollOffset) +'px';\r
65568         var gs = this.getGroups();\r
65569         for(var i = 0, len = gs.length; i < len; i++){\r
65570             gs[i].firstChild.style.width = tw;\r
65571         }\r
65572     },\r
65573 \r
65574     // private\r
65575     onColumnWidthUpdated : function(col, w, tw){\r
65576         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);\r
65577         this.updateGroupWidths();\r
65578     },\r
65579 \r
65580     // private\r
65581     onAllColumnWidthsUpdated : function(ws, tw){\r
65582         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);\r
65583         this.updateGroupWidths();\r
65584     },\r
65585 \r
65586     // private\r
65587     onColumnHiddenUpdated : function(col, hidden, tw){\r
65588         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);\r
65589         this.updateGroupWidths();\r
65590     },\r
65591 \r
65592     // private\r
65593     onLayout : function(){\r
65594         this.updateGroupWidths();\r
65595     },\r
65596 \r
65597     // private\r
65598     onBeforeRowSelect : function(sm, rowIndex){\r
65599         if(!this.enableGrouping){\r
65600             return;\r
65601         }\r
65602         var row = this.getRow(rowIndex);\r
65603         if(row && !row.offsetParent){\r
65604             var g = this.findGroup(row);\r
65605             this.toggleGroup(g, true);\r
65606         }\r
65607     }\r
65608 });\r
65609 // private\r
65610 Ext.grid.GroupingView.GROUP_ID = 1000;