Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / ext-all-debug.js
1 /*!
2  * Ext JS Library 3.0.3
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.DomHelper\r
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating\r
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates\r
11  * from your DOM building code.</p>\r
12  *\r
13  * <p><b><u>DomHelper element specification object</u></b></p>\r
14  * <p>A specification object is used when creating elements. Attributes of this object\r
15  * are assumed to be element attributes, except for 4 special attributes:\r
16  * <div class="mdetail-params"><ul>\r
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>\r
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the\r
19  * same kind of element definition objects to be created and appended. These can be nested\r
20  * as deep as you want.</div></li>\r
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.\r
22  * This will end up being either the "class" attribute on a HTML fragment or className\r
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>\r
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>\r
25  * </ul></div></p>\r
26  *\r
27  * <p><b><u>Insertion methods</u></b></p>\r
28  * <p>Commonly used insertion methods:\r
29  * <div class="mdetail-params"><ul>\r
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>\r
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>\r
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>\r
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>\r
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>\r
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>\r
36  * </ul></div></p>\r
37  *\r
38  * <p><b><u>Example</u></b></p>\r
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing\r
40  * element with id <tt>'my-div'</tt>:<br>\r
41  <pre><code>\r
42 var dh = Ext.DomHelper; // create shorthand alias\r
43 // specification object\r
44 var spec = {\r
45     id: 'my-ul',\r
46     tag: 'ul',\r
47     cls: 'my-list',\r
48     // append children after creating\r
49     children: [     // may also specify 'cn' instead of 'children'\r
50         {tag: 'li', id: 'item0', html: 'List Item 0'},\r
51         {tag: 'li', id: 'item1', html: 'List Item 1'},\r
52         {tag: 'li', id: 'item2', html: 'List Item 2'}\r
53     ]\r
54 };\r
55 var list = dh.append(\r
56     'my-div', // the context element 'my-div' can either be the id or the actual node\r
57     spec      // the specification object\r
58 );\r
59  </code></pre></p>\r
60  * <p>Element creation specification parameters in this class may also be passed as an Array of\r
61  * specification objects. This can be used to insert multiple sibling nodes into an existing\r
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>\r
63 dh.append('my-ul', [\r
64     {tag: 'li', id: 'item3', html: 'List Item 3'},\r
65     {tag: 'li', id: 'item4', html: 'List Item 4'}\r
66 ]);\r
67  * </code></pre></p>\r
68  *\r
69  * <p><b><u>Templating</u></b></p>\r
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,\r
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to\r
72  * insert new elements. Revisiting the example above, we could utilize templating this time:\r
73  * <pre><code>\r
74 // create the node\r
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});\r
76 // get template\r
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});\r
78 \r
79 for(var i = 0; i < 5, i++){\r
80     tpl.append(list, [i]); // use template to append to the actual node\r
81 }\r
82  * </code></pre></p>\r
83  * <p>An example using a template:<pre><code>\r
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';\r
85 \r
86 var tpl = new Ext.DomHelper.createTemplate(html);\r
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);\r
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);\r
89  * </code></pre></p>\r
90  *\r
91  * <p>The same example using named parameters:<pre><code>\r
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';\r
93 \r
94 var tpl = new Ext.DomHelper.createTemplate(html);\r
95 tpl.append('blog-roll', {\r
96     id: 'link1',\r
97     url: 'http://www.jackslocum.com/',\r
98     text: "Jack&#39;s Site"\r
99 });\r
100 tpl.append('blog-roll', {\r
101     id: 'link2',\r
102     url: 'http://www.dustindiaz.com/',\r
103     text: "Dustin&#39;s Site"\r
104 });\r
105  * </code></pre></p>\r
106  *\r
107  * <p><b><u>Compiling Templates</u></b></p>\r
108  * <p>Templates are applied using regular expressions. The performance is great, but if\r
109  * you are adding a bunch of DOM elements using the same template, you can increase\r
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.\r
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and\r
112  * broken up at the different variable points and a dynamic function is created and eval'ed.\r
113  * The generated function performs string concatenation of these parts and the passed\r
114  * variables instead of using regular expressions.\r
115  * <pre><code>\r
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';\r
117 \r
118 var tpl = new Ext.DomHelper.createTemplate(html);\r
119 tpl.compile();\r
120 \r
121 //... use template like normal\r
122  * </code></pre></p>\r
123  *\r
124  * <p><b><u>Performance Boost</u></b></p>\r
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead\r
126  * of DOM can significantly boost performance.</p>\r
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,\r
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification\r
129  * results in the creation of a text node. Usage:</p>\r
130  * <pre><code>\r
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance\r
132  * </code></pre>\r
133  * @singleton\r
134  */\r
135 Ext.DomHelper = function(){\r
136     var tempTableEl = null,\r
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,\r
138         tableRe = /^table|tbody|tr|td$/i,\r
139         pub,\r
140         // kill repeat to save bytes\r
141         afterbegin = 'afterbegin',\r
142         afterend = 'afterend',\r
143         beforebegin = 'beforebegin',\r
144         beforeend = 'beforeend',\r
145         ts = '<table>',\r
146         te = '</table>',\r
147         tbs = ts+'<tbody>',\r
148         tbe = '</tbody>'+te,\r
149         trs = tbs + '<tr>',\r
150         tre = '</tr>'+tbe;\r
151 \r
152     // private\r
153     function doInsert(el, o, returnElement, pos, sibling, append){\r
154         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));\r
155         return returnElement ? Ext.get(newNode, true) : newNode;\r
156     }\r
157 \r
158     // build as innerHTML where available\r
159     function createHtml(o){\r
160         var b = '',\r
161             attr,\r
162             val,\r
163             key,\r
164             keyVal,\r
165             cn;\r
166 \r
167         if(Ext.isString(o)){\r
168             b = o;\r
169         } else if (Ext.isArray(o)) {\r
170             Ext.each(o, function(v) {\r
171                 b += createHtml(v);\r
172             });\r
173         } else {\r
174             b += '<' + (o.tag = o.tag || 'div');\r
175             Ext.iterate(o, function(attr, val){\r
176                 if(!/tag|children|cn|html$/i.test(attr)){\r
177                     if (Ext.isObject(val)) {\r
178                         b += ' ' + attr + '="';\r
179                         Ext.iterate(val, function(key, keyVal){\r
180                             b += key + ':' + keyVal + ';';\r
181                         });\r
182                         b += '"';\r
183                     }else{\r
184                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';\r
185                     }\r
186                 }\r
187             });\r
188             // Now either just close the tag or try to add children and close the tag.\r
189             if (emptyTags.test(o.tag)) {\r
190                 b += '/>';\r
191             } else {\r
192                 b += '>';\r
193                 if ((cn = o.children || o.cn)) {\r
194                     b += createHtml(cn);\r
195                 } else if(o.html){\r
196                     b += o.html;\r
197                 }\r
198                 b += '</' + o.tag + '>';\r
199             }\r
200         }\r
201         return b;\r
202     }\r
203 \r
204     function ieTable(depth, s, h, e){\r
205         tempTableEl.innerHTML = [s, h, e].join('');\r
206         var i = -1,\r
207             el = tempTableEl,\r
208             ns;\r
209         while(++i < depth){\r
210             el = el.firstChild;\r
211         }\r
212 //      If the result is multiple siblings, then encapsulate them into one fragment.\r
213         if(ns = el.nextSibling){\r
214             var df = document.createDocumentFragment();\r
215             while(el){\r
216                 ns = el.nextSibling;\r
217                 df.appendChild(el);\r
218                 el = ns;\r
219             }\r
220             el = df;\r
221         }\r
222         return el;\r
223     }\r
224 \r
225     /**\r
226      * @ignore\r
227      * Nasty code for IE's broken table implementation\r
228      */\r
229     function insertIntoTable(tag, where, el, html) {\r
230         var node,\r
231             before;\r
232 \r
233         tempTableEl = tempTableEl || document.createElement('div');\r
234 \r
235         if(tag == 'td' && (where == afterbegin || where == beforeend) ||\r
236            !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {\r
237             return;\r
238         }\r
239         before = where == beforebegin ? el :\r
240                  where == afterend ? el.nextSibling :\r
241                  where == afterbegin ? el.firstChild : null;\r
242 \r
243         if (where == beforebegin || where == afterend) {\r
244             el = el.parentNode;\r
245         }\r
246 \r
247         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {\r
248             node = ieTable(4, trs, html, tre);\r
249         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||\r
250                    (tag == 'tr' && (where == beforebegin || where == afterend))) {\r
251             node = ieTable(3, tbs, html, tbe);\r
252         } else {\r
253             node = ieTable(2, ts, html, te);\r
254         }\r
255         el.insertBefore(node, before);\r
256         return node;\r
257     }\r
258 \r
259 \r
260     pub = {\r
261         /**\r
262          * Returns the markup for the passed Element(s) config.\r
263          * @param {Object} o The DOM object spec (and children)\r
264          * @return {String}\r
265          */\r
266         markup : function(o){\r
267             return createHtml(o);\r
268         },\r
269         \r
270         /**\r
271          * Applies a style specification to an element.\r
272          * @param {String/HTMLElement} el The element to apply styles to\r
273          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or\r
274          * a function which returns such a specification.\r
275          */\r
276         applyStyles : function(el, styles){\r
277             if(styles){\r
278                 var i = 0,\r
279                     len,\r
280                     style;\r
281 \r
282                 el = Ext.fly(el);\r
283                 if(Ext.isFunction(styles)){\r
284                     styles = styles.call();\r
285                 }\r
286                 if(Ext.isString(styles)){\r
287                     styles = styles.trim().split(/\s*(?::|;)\s*/);\r
288                     for(len = styles.length; i < len;){\r
289                         el.setStyle(styles[i++], styles[i++]);\r
290                     }\r
291                 }else if (Ext.isObject(styles)){\r
292                     el.setStyle(styles);\r
293                 }\r
294             }\r
295         },\r
296 \r
297         /**\r
298          * Inserts an HTML fragment into the DOM.\r
299          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
300          * @param {HTMLElement} el The context element\r
301          * @param {String} html The HTML fragment\r
302          * @return {HTMLElement} The new node\r
303          */\r
304         insertHtml : function(where, el, html){\r
305             var hash = {},\r
306                 hashVal,\r
307                 setStart,\r
308                 range,\r
309                 frag,\r
310                 rangeEl,\r
311                 rs;\r
312 \r
313             where = where.toLowerCase();\r
314             // add these here because they are used in both branches of the condition.\r
315             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];\r
316             hash[afterend] = ['AfterEnd', 'nextSibling'];\r
317 \r
318             if (el.insertAdjacentHTML) {\r
319                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){\r
320                     return rs;\r
321                 }\r
322                 // add these two to the hash.\r
323                 hash[afterbegin] = ['AfterBegin', 'firstChild'];\r
324                 hash[beforeend] = ['BeforeEnd', 'lastChild'];\r
325                 if ((hashVal = hash[where])) {\r
326                     el.insertAdjacentHTML(hashVal[0], html);\r
327                     return el[hashVal[1]];\r
328                 }\r
329             } else {\r
330                 range = el.ownerDocument.createRange();\r
331                 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');\r
332                 if (hash[where]) {\r
333                     range[setStart](el);\r
334                     frag = range.createContextualFragment(html);\r
335                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);\r
336                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];\r
337                 } else {\r
338                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';\r
339                     if (el.firstChild) {\r
340                         range[setStart](el[rangeEl]);\r
341                         frag = range.createContextualFragment(html);\r
342                         if(where == afterbegin){\r
343                             el.insertBefore(frag, el.firstChild);\r
344                         }else{\r
345                             el.appendChild(frag);\r
346                         }\r
347                     } else {\r
348                         el.innerHTML = html;\r
349                     }\r
350                     return el[rangeEl];\r
351                 }\r
352             }\r
353             throw 'Illegal insertion point -> "' + where + '"';\r
354         },\r
355 \r
356         /**\r
357          * Creates new DOM element(s) and inserts them before el.\r
358          * @param {Mixed} el The context element\r
359          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
360          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
361          * @return {HTMLElement/Ext.Element} The new node\r
362          */\r
363         insertBefore : function(el, o, returnElement){\r
364             return doInsert(el, o, returnElement, beforebegin);\r
365         },\r
366 \r
367         /**\r
368          * Creates new DOM element(s) and inserts them after el.\r
369          * @param {Mixed} el The context element\r
370          * @param {Object} o The DOM object spec (and children)\r
371          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
372          * @return {HTMLElement/Ext.Element} The new node\r
373          */\r
374         insertAfter : function(el, o, returnElement){\r
375             return doInsert(el, o, returnElement, afterend, 'nextSibling');\r
376         },\r
377 \r
378         /**\r
379          * Creates new DOM element(s) and inserts them as the first child of el.\r
380          * @param {Mixed} el The context element\r
381          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
382          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
383          * @return {HTMLElement/Ext.Element} The new node\r
384          */\r
385         insertFirst : function(el, o, returnElement){\r
386             return doInsert(el, o, returnElement, afterbegin, 'firstChild');\r
387         },\r
388 \r
389         /**\r
390          * Creates new DOM element(s) and appends them to el.\r
391          * @param {Mixed} el The context element\r
392          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
393          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
394          * @return {HTMLElement/Ext.Element} The new node\r
395          */\r
396         append : function(el, o, returnElement){\r
397             return doInsert(el, o, returnElement, beforeend, '', true);\r
398         },\r
399 \r
400         /**\r
401          * Creates new DOM element(s) and overwrites the contents of el with them.\r
402          * @param {Mixed} el The context element\r
403          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
404          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
405          * @return {HTMLElement/Ext.Element} The new node\r
406          */\r
407         overwrite : function(el, o, returnElement){\r
408             el = Ext.getDom(el);\r
409             el.innerHTML = createHtml(o);\r
410             return returnElement ? Ext.get(el.firstChild) : el.firstChild;\r
411         },\r
412 \r
413         createHtml : createHtml\r
414     };\r
415     return pub;\r
416 }();/**\r
417  * @class Ext.DomHelper\r
418  */\r
419 Ext.apply(Ext.DomHelper,\r
420 function(){\r
421         var pub,\r
422                 afterbegin = 'afterbegin',\r
423         afterend = 'afterend',\r
424         beforebegin = 'beforebegin',\r
425         beforeend = 'beforeend';\r
426 \r
427         // private\r
428     function doInsert(el, o, returnElement, pos, sibling, append){\r
429         el = Ext.getDom(el);\r
430         var newNode;\r
431         if (pub.useDom) {\r
432             newNode = createDom(o, null);\r
433             if (append) {\r
434                     el.appendChild(newNode);\r
435             } else {\r
436                         (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);\r
437             }\r
438         } else {\r
439             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));\r
440         }\r
441         return returnElement ? Ext.get(newNode, true) : newNode;\r
442     }\r
443 \r
444         // build as dom\r
445     /** @ignore */\r
446     function createDom(o, parentNode){\r
447         var el,\r
448                 doc = document,\r
449                 useSet,\r
450                 attr,\r
451                 val,\r
452                 cn;\r
453 \r
454         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted\r
455             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment\r
456                 Ext.each(o, function(v) {\r
457                 createDom(v, el);\r
458             });\r
459         } else if (Ext.isString(o)) {         // Allow a string as a child spec.\r
460             el = doc.createTextNode(o);\r
461         } else {\r
462             el = doc.createElement( o.tag || 'div' );\r
463             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute\r
464             Ext.iterate(o, function(attr, val){\r
465                 if(!/tag|children|cn|html|style/.test(attr)){\r
466                         if(attr == 'cls'){\r
467                             el.className = val;\r
468                         }else{\r
469                         if(useSet){\r
470                             el.setAttribute(attr, val);\r
471                         }else{\r
472                             el[attr] = val;\r
473                         }\r
474                         }\r
475                 }\r
476             });\r
477             Ext.DomHelper.applyStyles(el, o.style);\r
478 \r
479             if ((cn = o.children || o.cn)) {\r
480                 createDom(cn, el);\r
481             } else if (o.html) {\r
482                 el.innerHTML = o.html;\r
483             }\r
484         }\r
485         if(parentNode){\r
486            parentNode.appendChild(el);\r
487         }\r
488         return el;\r
489     }\r
490 \r
491         pub = {\r
492                 /**\r
493              * Creates a new Ext.Template from the DOM object spec.\r
494              * @param {Object} o The DOM object spec (and children)\r
495              * @return {Ext.Template} The new template\r
496              */\r
497             createTemplate : function(o){\r
498                 var html = Ext.DomHelper.createHtml(o);\r
499                 return new Ext.Template(html);\r
500             },\r
501 \r
502                 /** True to force the use of DOM instead of html fragments @type Boolean */\r
503             useDom : false,\r
504 \r
505             /**\r
506              * Creates new DOM element(s) and inserts them before el.\r
507              * @param {Mixed} el The context element\r
508              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
509              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
510              * @return {HTMLElement/Ext.Element} The new node\r
511          * @hide (repeat)\r
512              */\r
513             insertBefore : function(el, o, returnElement){\r
514                 return doInsert(el, o, returnElement, beforebegin);\r
515             },\r
516 \r
517             /**\r
518              * Creates new DOM element(s) and inserts them after el.\r
519              * @param {Mixed} el The context element\r
520              * @param {Object} o The DOM object spec (and children)\r
521              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
522              * @return {HTMLElement/Ext.Element} The new node\r
523          * @hide (repeat)\r
524              */\r
525             insertAfter : function(el, o, returnElement){\r
526                 return doInsert(el, o, returnElement, afterend, 'nextSibling');\r
527             },\r
528 \r
529             /**\r
530              * Creates new DOM element(s) and inserts them as the first child of el.\r
531              * @param {Mixed} el The context element\r
532              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
533              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
534              * @return {HTMLElement/Ext.Element} The new node\r
535          * @hide (repeat)\r
536              */\r
537             insertFirst : function(el, o, returnElement){\r
538                 return doInsert(el, o, returnElement, afterbegin, 'firstChild');\r
539             },\r
540 \r
541             /**\r
542              * Creates new DOM element(s) and appends them to el.\r
543              * @param {Mixed} el The context element\r
544              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
545              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
546              * @return {HTMLElement/Ext.Element} The new node\r
547          * @hide (repeat)\r
548              */\r
549             append: function(el, o, returnElement){\r
550             return doInsert(el, o, returnElement, beforeend, '', true);\r
551         },\r
552 \r
553             /**\r
554              * Creates new DOM element(s) without inserting them to the document.\r
555              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
556              * @return {HTMLElement} The new uninserted node\r
557              */\r
558         createDom: createDom\r
559         };\r
560         return pub;\r
561 }());/**
562  * @class Ext.Template
563  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
564  * for greater performance.</p>
565  * <p>For example usage {@link #Template see the constructor}.</p>
566  * 
567  * @constructor
568  * An instance of this class may be created by passing to the constructor either
569  * a single argument, or multiple arguments:
570  * <div class="mdetail-params"><ul>
571  * <li><b>single argument</b> : String/Array
572  * <div class="sub-desc">
573  * The single argument may be either a String or an Array:<ul>
574  * <li><tt>String</tt> : </li><pre><code>
575 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
576 t.{@link #append}('some-element', ['foo']);
577  * </code></pre>
578  * <li><tt>Array</tt> : </li>
579  * An Array will be combined with <code>join('')</code>.
580 <pre><code>
581 var t = new Ext.Template([
582     '&lt;div name="{id}"&gt;',
583         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
584     '&lt;/div&gt;',
585 ]);
586 t.{@link #compile}();
587 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
588 </code></pre>
589  * </ul></div></li>
590  * <li><b>multiple arguments</b> : String, Object, Array, ...
591  * <div class="sub-desc">
592  * Multiple arguments will be combined with <code>join('')</code>.
593  * <pre><code>
594 var t = new Ext.Template(
595     '&lt;div name="{id}"&gt;',
596         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
597     '&lt;/div&gt;',
598     // a configuration object:
599     {
600         compiled: true,      // {@link #compile} immediately
601         disableFormats: true // See Notes below.
602     } 
603 );
604  * </code></pre>
605  * <p><b>Notes</b>:</p>
606  * <div class="mdetail-params"><ul>
607  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
608  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
609  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
610  * when no formatting is required.</li>
611  * </ul></div>
612  * </div></li>
613  * </ul></div>
614  * @param {Mixed} config
615  */
616 Ext.Template = function(html){
617     var me = this,
618         a = arguments,
619         buf = [];
620
621     if (Ext.isArray(html)) {
622         html = html.join("");
623     } else if (a.length > 1) {
624             Ext.each(a, function(v) {
625             if (Ext.isObject(v)) {
626                 Ext.apply(me, v);
627             } else {
628                 buf.push(v);
629             }
630         });
631         html = buf.join('');
632     }
633
634     /**@private*/
635     me.html = html;
636     /**
637      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
638      * immediately (see <code>{@link #compile}</code>).
639      * Defaults to <tt>false</tt>.
640      */
641     if (me.compiled) {
642         me.compile();
643     }
644 };
645 Ext.Template.prototype = {
646     /**
647      * @cfg {RegExp} re The regular expression used to match template variables.
648      * Defaults to:<pre><code>
649      * re : /\{([\w-]+)\}/g                                     // for Ext Core
650      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
651      * </code></pre>
652      */
653     re : /\{([\w-]+)\}/g,
654     /**
655      * See <code>{@link #re}</code>.
656      * @type RegExp
657      * @property re
658      */
659
660     /**
661      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
662      * @param {Object/Array} values
663      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
664      * or an object (i.e. <code>{foo: 'bar'}</code>).
665      * @return {String} The HTML fragment
666      */
667     applyTemplate : function(values){
668                 var me = this;
669
670         return me.compiled ?
671                         me.compiled(values) :
672                                 me.html.replace(me.re, function(m, name){
673                                 return values[name] !== undefined ? values[name] : "";
674                         });
675         },
676
677     /**
678      * Sets the HTML used as the template and optionally compiles it.
679      * @param {String} html
680      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
681      * @return {Ext.Template} this
682      */
683     set : function(html, compile){
684             var me = this;
685         me.html = html;
686         me.compiled = null;
687         return compile ? me.compile() : me;
688     },
689
690     /**
691      * Compiles the template into an internal function, eliminating the RegEx overhead.
692      * @return {Ext.Template} this
693      */
694     compile : function(){
695         var me = this,
696                 sep = Ext.isGecko ? "+" : ",";
697
698         function fn(m, name){                        
699                 name = "values['" + name + "']";
700                 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
701         }
702                 
703         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
704              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
705              (Ext.isGecko ?  "';};" : "'].join('');};"));
706         return me;
707     },
708
709     /**
710      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
711      * @param {Mixed} el The context element
712      * @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'})
713      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
714      * @return {HTMLElement/Ext.Element} The new node or Element
715      */
716     insertFirst: function(el, values, returnElement){
717         return this.doInsert('afterBegin', el, values, returnElement);
718     },
719
720     /**
721      * Applies the supplied values to the template and inserts the new node(s) before el.
722      * @param {Mixed} el The context element
723      * @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'})
724      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
725      * @return {HTMLElement/Ext.Element} The new node or Element
726      */
727     insertBefore: function(el, values, returnElement){
728         return this.doInsert('beforeBegin', el, values, returnElement);
729     },
730
731     /**
732      * Applies the supplied values to the template and inserts the new node(s) after el.
733      * @param {Mixed} el The context element
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'})
735      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
736      * @return {HTMLElement/Ext.Element} The new node or Element
737      */
738     insertAfter : function(el, values, returnElement){
739         return this.doInsert('afterEnd', el, values, returnElement);
740     },
741
742     /**
743      * Applies the supplied <code>values</code> to the template and appends
744      * the new node(s) to the specified <code>el</code>.
745      * <p>For example usage {@link #Template see the constructor}.</p>
746      * @param {Mixed} el The context element
747      * @param {Object/Array} values
748      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
749      * or an object (i.e. <code>{foo: 'bar'}</code>).
750      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
751      * @return {HTMLElement/Ext.Element} The new node or Element
752      */
753     append : function(el, values, returnElement){
754         return this.doInsert('beforeEnd', el, values, returnElement);
755     },
756
757     doInsert : function(where, el, values, returnEl){
758         el = Ext.getDom(el);
759         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
760         return returnEl ? Ext.get(newNode, true) : newNode;
761     },
762
763     /**
764      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
765      * @param {Mixed} el The context element
766      * @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'})
767      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
768      * @return {HTMLElement/Ext.Element} The new node or Element
769      */
770     overwrite : function(el, values, returnElement){
771         el = Ext.getDom(el);
772         el.innerHTML = this.applyTemplate(values);
773         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
774     }
775 };
776 /**
777  * Alias for {@link #applyTemplate}
778  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
779  * @param {Object/Array} values
780  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
781  * or an object (i.e. <code>{foo: 'bar'}</code>).
782  * @return {String} The HTML fragment
783  * @member Ext.Template
784  * @method apply
785  */
786 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
787
788 /**
789  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
790  * @param {String/HTMLElement} el A DOM element or its id
791  * @param {Object} config A configuration object
792  * @return {Ext.Template} The created template
793  * @static
794  */
795 Ext.Template.from = function(el, config){
796     el = Ext.getDom(el);
797     return new Ext.Template(el.value || el.innerHTML, config || '');
798 };/**\r
799  * @class Ext.Template\r
800  */\r
801 Ext.apply(Ext.Template.prototype, {\r
802     /**\r
803      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format\r
804      * functions in the template. If the template does not contain\r
805      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>\r
806      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.\r
807      * <pre><code>\r
808 var t = new Ext.Template(\r
809     '&lt;div name="{id}"&gt;',\r
810         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',\r
811     '&lt;/div&gt;',\r
812     {\r
813         compiled: true,      // {@link #compile} immediately\r
814         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting\r
815     }    \r
816 );\r
817      * </code></pre>\r
818      * For a list of available format functions, see {@link Ext.util.Format}.\r
819      */\r
820     disableFormats : false,                             \r
821     /**\r
822      * See <code>{@link #disableFormats}</code>.\r
823      * @type Boolean\r
824      * @property disableFormats\r
825      */\r
826 \r
827     /**\r
828      * The regular expression used to match template variables\r
829      * @type RegExp\r
830      * @property\r
831      * @hide repeat doc\r
832      */\r
833     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,\r
834 \r
835     /**\r
836      * Returns an HTML fragment of this template with the specified values applied.\r
837      * @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
838      * @return {String} The HTML fragment\r
839      * @hide repeat doc\r
840      */\r
841     applyTemplate : function(values){\r
842                 var me = this,\r
843                         useF = me.disableFormats !== true,\r
844                 fm = Ext.util.Format, \r
845                 tpl = me;           \r
846             \r
847         if(me.compiled){\r
848             return me.compiled(values);\r
849         }\r
850         function fn(m, name, format, args){\r
851             if (format && useF) {\r
852                 if (format.substr(0, 5) == "this.") {\r
853                     return tpl.call(format.substr(5), values[name], values);\r
854                 } else {\r
855                     if (args) {\r
856                         // quoted values are required for strings in compiled templates,\r
857                         // but for non compiled we need to strip them\r
858                         // quoted reversed for jsmin\r
859                         var re = /^\s*['"](.*)["']\s*$/;\r
860                         args = args.split(',');\r
861                         for(var i = 0, len = args.length; i < len; i++){\r
862                             args[i] = args[i].replace(re, "$1");\r
863                         }\r
864                         args = [values[name]].concat(args);\r
865                     } else {\r
866                         args = [values[name]];\r
867                     }\r
868                     return fm[format].apply(fm, args);\r
869                 }\r
870             } else {\r
871                 return values[name] !== undefined ? values[name] : "";\r
872             }\r
873         }\r
874         return me.html.replace(me.re, fn);\r
875     },\r
876                 \r
877     /**\r
878      * Compiles the template into an internal function, eliminating the RegEx overhead.\r
879      * @return {Ext.Template} this\r
880      * @hide repeat doc\r
881      */\r
882     compile : function(){\r
883         var me = this,\r
884                 fm = Ext.util.Format,\r
885                 useF = me.disableFormats !== true,\r
886                 sep = Ext.isGecko ? "+" : ",",\r
887                 body;\r
888         \r
889         function fn(m, name, format, args){\r
890             if(format && useF){\r
891                 args = args ? ',' + args : "";\r
892                 if(format.substr(0, 5) != "this."){\r
893                     format = "fm." + format + '(';\r
894                 }else{\r
895                     format = 'this.call("'+ format.substr(5) + '", ';\r
896                     args = ", values";\r
897                 }\r
898             }else{\r
899                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";\r
900             }\r
901             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";\r
902         }\r
903         \r
904         // branched to use + in gecko and [].join() in others\r
905         if(Ext.isGecko){\r
906             body = "this.compiled = function(values){ return '" +\r
907                    me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +\r
908                     "';};";\r
909         }else{\r
910             body = ["this.compiled = function(values){ return ['"];\r
911             body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));\r
912             body.push("'].join('');};");\r
913             body = body.join('');\r
914         }\r
915         eval(body);\r
916         return me;\r
917     },\r
918     \r
919     // private function used to call members\r
920     call : function(fnName, value, allValues){\r
921         return this[fnName](value, allValues);\r
922     }\r
923 });\r
924 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*\r
925  * This is code is also distributed under MIT license for use\r
926  * with jQuery and prototype JavaScript libraries.\r
927  */\r
928 /**\r
929  * @class Ext.DomQuery\r
930 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
931 <p>\r
932 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
933 \r
934 <p>\r
935 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
936 </p>\r
937 <h4>Element Selectors:</h4>\r
938 <ul class="list">\r
939     <li> <b>*</b> any element</li>\r
940     <li> <b>E</b> an element with the tag E</li>\r
941     <li> <b>E F</b> All descendent elements of E that have the tag F</li>\r
942     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>\r
943     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>\r
944     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>\r
945 </ul>\r
946 <h4>Attribute Selectors:</h4>\r
947 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>\r
948 <ul class="list">\r
949     <li> <b>E[foo]</b> has an attribute "foo"</li>\r
950     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>\r
951     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>\r
952     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>\r
953     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>\r
954     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>\r
955     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>\r
956 </ul>\r
957 <h4>Pseudo Classes:</h4>\r
958 <ul class="list">\r
959     <li> <b>E:first-child</b> E is the first child of its parent</li>\r
960     <li> <b>E:last-child</b> E is the last child of its parent</li>\r
961     <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
962     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>\r
963     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>\r
964     <li> <b>E:only-child</b> E is the only child of its parent</li>\r
965     <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
966     <li> <b>E:first</b> the first E in the resultset</li>\r
967     <li> <b>E:last</b> the last E in the resultset</li>\r
968     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>\r
969     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>\r
970     <li> <b>E:even</b> shortcut for :nth-child(even)</li>\r
971     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>\r
972     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>\r
973     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>\r
974     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>\r
975     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>\r
976     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>\r
977 </ul>\r
978 <h4>CSS Value Selectors:</h4>\r
979 <ul class="list">\r
980     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>\r
981     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>\r
982     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>\r
983     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>\r
984     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>\r
985     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>\r
986 </ul>\r
987  * @singleton\r
988  */\r
989 Ext.DomQuery = function(){\r
990     var cache = {}, \r
991         simpleCache = {}, \r
992         valueCache = {},\r
993         nonSpace = /\S/,\r
994         trimRe = /^\s+|\s+$/g,\r
995         tplRe = /\{(\d+)\}/g,\r
996         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,\r
997         tagTokenRe = /^(#)?([\w-\*]+)/,\r
998         nthRe = /(\d*)n\+?(\d*)/, \r
999         nthRe2 = /\D/,\r
1000         // This is for IE MSXML which does not support expandos.\r
1001             // IE runs the same speed using setAttribute, however FF slows way down\r
1002             // and Safari completely fails so they need to continue to use expandos.\r
1003             isIE = window.ActiveXObject ? true : false,\r
1004         isOpera = Ext.isOpera,\r
1005             key = 30803;\r
1006             \r
1007     // this eval is stop the compressor from\r
1008         // renaming the variable to something shorter\r
1009         eval("var batch = 30803;");     \r
1010 \r
1011     function child(p, index){\r
1012         var i = 0,\r
1013                 n = p.firstChild;\r
1014         while(n){\r
1015             if(n.nodeType == 1){\r
1016                if(++i == index){\r
1017                    return n;\r
1018                }\r
1019             }\r
1020             n = n.nextSibling;\r
1021         }\r
1022         return null;\r
1023     };\r
1024 \r
1025     function next(n){\r
1026         while((n = n.nextSibling) && n.nodeType != 1);\r
1027         return n;\r
1028     };\r
1029 \r
1030     function prev(n){\r
1031         while((n = n.previousSibling) && n.nodeType != 1);\r
1032         return n;\r
1033     };\r
1034 \r
1035     function children(d){\r
1036         var n = d.firstChild, ni = -1,\r
1037                 nx;\r
1038             while(n){\r
1039                 nx = n.nextSibling;\r
1040                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
1041                     d.removeChild(n);\r
1042                 }else{\r
1043                     n.nodeIndex = ++ni;\r
1044                 }\r
1045                 n = nx;\r
1046             }\r
1047             return this;\r
1048         };\r
1049 \r
1050     function byClassName(c, a, v){\r
1051         if(!v){\r
1052             return c;\r
1053         }\r
1054         var r = [], ri = -1, cn;\r
1055         for(var i = 0, ci; ci = c[i]; i++){\r
1056             if((' '+ci.className+' ').indexOf(v) != -1){\r
1057                 r[++ri] = ci;\r
1058             }\r
1059         }\r
1060         return r;\r
1061     };\r
1062 \r
1063     function attrValue(n, attr){\r
1064         if(!n.tagName && typeof n.length != "undefined"){\r
1065             n = n[0];\r
1066         }\r
1067         if(!n){\r
1068             return null;\r
1069         }\r
1070         if(attr == "for"){\r
1071             return n.htmlFor;\r
1072         }\r
1073         if(attr == "class" || attr == "className"){\r
1074             return n.className;\r
1075         }\r
1076         return n.getAttribute(attr) || n[attr];\r
1077 \r
1078     };\r
1079 \r
1080     function getNodes(ns, mode, tagName){\r
1081         var result = [], ri = -1, cs;\r
1082         if(!ns){\r
1083             return result;\r
1084         }\r
1085         tagName = tagName || "*";\r
1086         if(typeof ns.getElementsByTagName != "undefined"){\r
1087             ns = [ns];\r
1088         }\r
1089         if(!mode){\r
1090             for(var i = 0, ni; ni = ns[i]; i++){\r
1091                 cs = ni.getElementsByTagName(tagName);\r
1092                 for(var j = 0, ci; ci = cs[j]; j++){\r
1093                     result[++ri] = ci;\r
1094                 }\r
1095             }\r
1096         }else if(mode == "/" || mode == ">"){\r
1097             var utag = tagName.toUpperCase();\r
1098             for(var i = 0, ni, cn; ni = ns[i]; i++){\r
1099                 cn = isOpera ? ni.childNodes : (ni.children || ni.childNodes);\r
1100                 for(var j = 0, cj; cj = cn[j]; j++){\r
1101                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){\r
1102                         result[++ri] = cj;\r
1103                     }\r
1104                 }\r
1105             }\r
1106         }else if(mode == "+"){\r
1107             var utag = tagName.toUpperCase();\r
1108             for(var i = 0, n; n = ns[i]; i++){\r
1109                 while((n = n.nextSibling) && n.nodeType != 1);\r
1110                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){\r
1111                     result[++ri] = n;\r
1112                 }\r
1113             }\r
1114         }else if(mode == "~"){\r
1115             var utag = tagName.toUpperCase();\r
1116             for(var i = 0, n; n = ns[i]; i++){\r
1117                 while((n = n.nextSibling)){\r
1118                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){\r
1119                         result[++ri] = n;\r
1120                     }\r
1121                 }\r
1122             }\r
1123         }\r
1124         return result;\r
1125     };\r
1126 \r
1127     function concat(a, b){\r
1128         if(b.slice){\r
1129             return a.concat(b);\r
1130         }\r
1131         for(var i = 0, l = b.length; i < l; i++){\r
1132             a[a.length] = b[i];\r
1133         }\r
1134         return a;\r
1135     }\r
1136 \r
1137     function byTag(cs, tagName){\r
1138         if(cs.tagName || cs == document){\r
1139             cs = [cs];\r
1140         }\r
1141         if(!tagName){\r
1142             return cs;\r
1143         }\r
1144         var r = [], ri = -1;\r
1145         tagName = tagName.toLowerCase();\r
1146         for(var i = 0, ci; ci = cs[i]; i++){\r
1147             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){\r
1148                 r[++ri] = ci;\r
1149             }\r
1150         }\r
1151         return r;\r
1152     };\r
1153 \r
1154     function byId(cs, attr, id){\r
1155         if(cs.tagName || cs == document){\r
1156             cs = [cs];\r
1157         }\r
1158         if(!id){\r
1159             return cs;\r
1160         }\r
1161         var r = [], ri = -1;\r
1162         for(var i = 0,ci; ci = cs[i]; i++){\r
1163             if(ci && ci.id == id){\r
1164                 r[++ri] = ci;\r
1165                 return r;\r
1166             }\r
1167         }\r
1168         return r;\r
1169     };\r
1170 \r
1171     function byAttribute(cs, attr, value, op, custom){\r
1172         var r = [], \r
1173                 ri = -1, \r
1174                 st = custom=="{",\r
1175                 f = Ext.DomQuery.operators[op];\r
1176         for(var i = 0, ci; ci = cs[i]; i++){\r
1177             if(ci.nodeType != 1){\r
1178                 continue;\r
1179             }\r
1180             var a;\r
1181             if(st){\r
1182                 a = Ext.DomQuery.getStyle(ci, attr);\r
1183             }\r
1184             else if(attr == "class" || attr == "className"){\r
1185                 a = ci.className;\r
1186             }else if(attr == "for"){\r
1187                 a = ci.htmlFor;\r
1188             }else if(attr == "href"){\r
1189                 a = ci.getAttribute("href", 2);\r
1190             }else{\r
1191                 a = ci.getAttribute(attr);\r
1192             }\r
1193             if((f && f(a, value)) || (!f && a)){\r
1194                 r[++ri] = ci;\r
1195             }\r
1196         }\r
1197         return r;\r
1198     };\r
1199 \r
1200     function byPseudo(cs, name, value){\r
1201         return Ext.DomQuery.pseudos[name](cs, value);\r
1202     };\r
1203 \r
1204     function nodupIEXml(cs){\r
1205         var d = ++key, \r
1206                 r;\r
1207         cs[0].setAttribute("_nodup", d);\r
1208         r = [cs[0]];\r
1209         for(var i = 1, len = cs.length; i < len; i++){\r
1210             var c = cs[i];\r
1211             if(!c.getAttribute("_nodup") != d){\r
1212                 c.setAttribute("_nodup", d);\r
1213                 r[r.length] = c;\r
1214             }\r
1215         }\r
1216         for(var i = 0, len = cs.length; i < len; i++){\r
1217             cs[i].removeAttribute("_nodup");\r
1218         }\r
1219         return r;\r
1220     }\r
1221 \r
1222     function nodup(cs){\r
1223         if(!cs){\r
1224             return [];\r
1225         }\r
1226         var len = cs.length, c, i, r = cs, cj, ri = -1;\r
1227         if(!len || typeof cs.nodeType != "undefined" || len == 1){\r
1228             return cs;\r
1229         }\r
1230         if(isIE && typeof cs[0].selectSingleNode != "undefined"){\r
1231             return nodupIEXml(cs);\r
1232         }\r
1233         var d = ++key;\r
1234         cs[0]._nodup = d;\r
1235         for(i = 1; c = cs[i]; i++){\r
1236             if(c._nodup != d){\r
1237                 c._nodup = d;\r
1238             }else{\r
1239                 r = [];\r
1240                 for(var j = 0; j < i; j++){\r
1241                     r[++ri] = cs[j];\r
1242                 }\r
1243                 for(j = i+1; cj = cs[j]; j++){\r
1244                     if(cj._nodup != d){\r
1245                         cj._nodup = d;\r
1246                         r[++ri] = cj;\r
1247                     }\r
1248                 }\r
1249                 return r;\r
1250             }\r
1251         }\r
1252         return r;\r
1253     }\r
1254 \r
1255     function quickDiffIEXml(c1, c2){\r
1256         var d = ++key,\r
1257                 r = [];\r
1258         for(var i = 0, len = c1.length; i < len; i++){\r
1259             c1[i].setAttribute("_qdiff", d);\r
1260         }        \r
1261         for(var i = 0, len = c2.length; i < len; i++){\r
1262             if(c2[i].getAttribute("_qdiff") != d){\r
1263                 r[r.length] = c2[i];\r
1264             }\r
1265         }\r
1266         for(var i = 0, len = c1.length; i < len; i++){\r
1267            c1[i].removeAttribute("_qdiff");\r
1268         }\r
1269         return r;\r
1270     }\r
1271 \r
1272     function quickDiff(c1, c2){\r
1273         var len1 = c1.length,\r
1274                 d = ++key,\r
1275                 r = [];\r
1276         if(!len1){\r
1277             return c2;\r
1278         }\r
1279         if(isIE && c1[0].selectSingleNode){\r
1280             return quickDiffIEXml(c1, c2);\r
1281         }        \r
1282         for(var i = 0; i < len1; i++){\r
1283             c1[i]._qdiff = d;\r
1284         }        \r
1285         for(var i = 0, len = c2.length; i < len; i++){\r
1286             if(c2[i]._qdiff != d){\r
1287                 r[r.length] = c2[i];\r
1288             }\r
1289         }\r
1290         return r;\r
1291     }\r
1292 \r
1293     function quickId(ns, mode, root, id){\r
1294         if(ns == root){\r
1295            var d = root.ownerDocument || root;\r
1296            return d.getElementById(id);\r
1297         }\r
1298         ns = getNodes(ns, mode, "*");\r
1299         return byId(ns, null, id);\r
1300     }\r
1301 \r
1302     return {\r
1303         getStyle : function(el, name){\r
1304             return Ext.fly(el).getStyle(name);\r
1305         },\r
1306         /**\r
1307          * Compiles a selector/xpath query into a reusable function. The returned function\r
1308          * takes one parameter "root" (optional), which is the context node from where the query should start.\r
1309          * @param {String} selector The selector/xpath query\r
1310          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match\r
1311          * @return {Function}\r
1312          */\r
1313         compile : function(path, type){\r
1314             type = type || "select";\r
1315 \r
1316             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],\r
1317                 q = path, mode, lq,\r
1318                 tk = Ext.DomQuery.matchers,\r
1319                 tklen = tk.length,\r
1320                 mm,\r
1321                 // accept leading mode switch\r
1322                 lmode = q.match(modeRe);\r
1323             \r
1324             if(lmode && lmode[1]){\r
1325                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';\r
1326                 q = q.replace(lmode[1], "");\r
1327             }\r
1328             // strip leading slashes\r
1329             while(path.substr(0, 1)=="/"){\r
1330                 path = path.substr(1);\r
1331             }\r
1332 \r
1333             while(q && lq != q){\r
1334                 lq = q;\r
1335                 var tm = q.match(tagTokenRe);\r
1336                 if(type == "select"){\r
1337                     if(tm){\r
1338                         if(tm[1] == "#"){\r
1339                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';\r
1340                         }else{\r
1341                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';\r
1342                         }\r
1343                         q = q.replace(tm[0], "");\r
1344                     }else if(q.substr(0, 1) != '@'){\r
1345                         fn[fn.length] = 'n = getNodes(n, mode, "*");';\r
1346                     }\r
1347                 }else{\r
1348                     if(tm){\r
1349                         if(tm[1] == "#"){\r
1350                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';\r
1351                         }else{\r
1352                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';\r
1353                         }\r
1354                         q = q.replace(tm[0], "");\r
1355                     }\r
1356                 }\r
1357                 while(!(mm = q.match(modeRe))){\r
1358                     var matched = false;\r
1359                     for(var j = 0; j < tklen; j++){\r
1360                         var t = tk[j];\r
1361                         var m = q.match(t.re);\r
1362                         if(m){\r
1363                             fn[fn.length] = t.select.replace(tplRe, function(x, i){\r
1364                                                     return m[i];\r
1365                                                 });\r
1366                             q = q.replace(m[0], "");\r
1367                             matched = true;\r
1368                             break;\r
1369                         }\r
1370                     }\r
1371                     // prevent infinite loop on bad selector\r
1372                     if(!matched){\r
1373                         throw 'Error parsing selector, parsing failed at "' + q + '"';\r
1374                     }\r
1375                 }\r
1376                 if(mm[1]){\r
1377                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';\r
1378                     q = q.replace(mm[1], "");\r
1379                 }\r
1380             }\r
1381             fn[fn.length] = "return nodup(n);\n}";\r
1382             eval(fn.join(""));\r
1383             return f;\r
1384         },\r
1385 \r
1386         /**\r
1387          * Selects a group of elements.\r
1388          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)\r
1389          * @param {Node} root (optional) The start of the query (defaults to document).\r
1390          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1391          * no matches, and empty Array is returned.\r
1392          */\r
1393         select : function(path, root, type){\r
1394             if(!root || root == document){\r
1395                 root = document;\r
1396             }\r
1397             if(typeof root == "string"){\r
1398                 root = document.getElementById(root);\r
1399             }\r
1400             var paths = path.split(","),\r
1401                 results = [];\r
1402             for(var i = 0, len = paths.length; i < len; i++){\r
1403                 var p = paths[i].replace(trimRe, "");\r
1404                 if(!cache[p]){\r
1405                     cache[p] = Ext.DomQuery.compile(p);\r
1406                     if(!cache[p]){\r
1407                         throw p + " is not a valid selector";\r
1408                     }\r
1409                 }\r
1410                 var result = cache[p](root);\r
1411                 if(result && result != document){\r
1412                     results = results.concat(result);\r
1413                 }\r
1414             }\r
1415             if(paths.length > 1){\r
1416                 return nodup(results);\r
1417             }\r
1418             return results;\r
1419         },\r
1420 \r
1421         /**\r
1422          * Selects a single element.\r
1423          * @param {String} selector The selector/xpath query\r
1424          * @param {Node} root (optional) The start of the query (defaults to document).\r
1425          * @return {Element} The DOM element which matched the selector.\r
1426          */\r
1427         selectNode : function(path, root){\r
1428             return Ext.DomQuery.select(path, root)[0];\r
1429         },\r
1430 \r
1431         /**\r
1432          * Selects the value of a node, optionally replacing null with the defaultValue.\r
1433          * @param {String} selector The selector/xpath query\r
1434          * @param {Node} root (optional) The start of the query (defaults to document).\r
1435          * @param {String} defaultValue\r
1436          * @return {String}\r
1437          */\r
1438         selectValue : function(path, root, defaultValue){\r
1439             path = path.replace(trimRe, "");\r
1440             if(!valueCache[path]){\r
1441                 valueCache[path] = Ext.DomQuery.compile(path, "select");\r
1442             }\r
1443             var n = valueCache[path](root),\r
1444                 v;\r
1445             n = n[0] ? n[0] : n;\r
1446             v = (n && n.firstChild ? n.firstChild.nodeValue : null);\r
1447             return ((v === null||v === undefined||v==='') ? defaultValue : v);\r
1448         },\r
1449 \r
1450         /**\r
1451          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.\r
1452          * @param {String} selector The selector/xpath query\r
1453          * @param {Node} root (optional) The start of the query (defaults to document).\r
1454          * @param {Number} defaultValue\r
1455          * @return {Number}\r
1456          */\r
1457         selectNumber : function(path, root, defaultValue){\r
1458             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);\r
1459             return parseFloat(v);\r
1460         },\r
1461 \r
1462         /**\r
1463          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)\r
1464          * @param {String/HTMLElement/Array} el An element id, element or array of elements\r
1465          * @param {String} selector The simple selector to test\r
1466          * @return {Boolean}\r
1467          */\r
1468         is : function(el, ss){\r
1469             if(typeof el == "string"){\r
1470                 el = document.getElementById(el);\r
1471             }\r
1472             var isArray = Ext.isArray(el),\r
1473                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);\r
1474             return isArray ? (result.length == el.length) : (result.length > 0);\r
1475         },\r
1476 \r
1477         /**\r
1478          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)\r
1479          * @param {Array} el An array of elements to filter\r
1480          * @param {String} selector The simple selector to test\r
1481          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match\r
1482          * the selector instead of the ones that match\r
1483          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1484          * no matches, and empty Array is returned.\r
1485          */\r
1486         filter : function(els, ss, nonMatches){\r
1487             ss = ss.replace(trimRe, "");\r
1488             if(!simpleCache[ss]){\r
1489                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");\r
1490             }\r
1491             var result = simpleCache[ss](els);\r
1492             return nonMatches ? quickDiff(result, els) : result;\r
1493         },\r
1494 \r
1495         /**\r
1496          * Collection of matching regular expressions and code snippets.\r
1497          */\r
1498         matchers : [{\r
1499                 re: /^\.([\w-]+)/,\r
1500                 select: 'n = byClassName(n, null, " {1} ");'\r
1501             }, {\r
1502                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,\r
1503                 select: 'n = byPseudo(n, "{1}", "{2}");'\r
1504             },{\r
1505                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,\r
1506                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'\r
1507             }, {\r
1508                 re: /^#([\w-]+)/,\r
1509                 select: 'n = byId(n, null, "{1}");'\r
1510             },{\r
1511                 re: /^@([\w-]+)/,\r
1512                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'\r
1513             }\r
1514         ],\r
1515 \r
1516         /**\r
1517          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.\r
1518          * 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
1519          */\r
1520         operators : {\r
1521             "=" : function(a, v){\r
1522                 return a == v;\r
1523             },\r
1524             "!=" : function(a, v){\r
1525                 return a != v;\r
1526             },\r
1527             "^=" : function(a, v){\r
1528                 return a && a.substr(0, v.length) == v;\r
1529             },\r
1530             "$=" : function(a, v){\r
1531                 return a && a.substr(a.length-v.length) == v;\r
1532             },\r
1533             "*=" : function(a, v){\r
1534                 return a && a.indexOf(v) !== -1;\r
1535             },\r
1536             "%=" : function(a, v){\r
1537                 return (a % v) == 0;\r
1538             },\r
1539             "|=" : function(a, v){\r
1540                 return a && (a == v || a.substr(0, v.length+1) == v+'-');\r
1541             },\r
1542             "~=" : function(a, v){\r
1543                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;\r
1544             }\r
1545         },\r
1546 \r
1547         /**\r
1548          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed\r
1549          * two parameters:</p><div class="mdetail-params"><ul>\r
1550          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>\r
1551          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>\r
1552          * </ul></div>\r
1553          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>\r
1554          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,\r
1555          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>\r
1556          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>\r
1557          * <code><pre>\r
1558 Ext.DomQuery.pseudos.external = function(c, v){\r
1559     var r = [], ri = -1;\r
1560     for(var i = 0, ci; ci = c[i]; i++){\r
1561 //      Include in result set only if it's a link to an external resource\r
1562         if(ci.hostname != location.hostname){\r
1563             r[++ri] = ci;\r
1564         }\r
1565     }\r
1566     return r;\r
1567 };</pre></code>\r
1568          * Then external links could be gathered with the following statement:<code><pre>\r
1569 var externalLinks = Ext.select("a:external");\r
1570 </code></pre>\r
1571          */\r
1572         pseudos : {\r
1573             "first-child" : function(c){\r
1574                 var r = [], ri = -1, n;\r
1575                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1576                     while((n = n.previousSibling) && n.nodeType != 1);\r
1577                     if(!n){\r
1578                         r[++ri] = ci;\r
1579                     }\r
1580                 }\r
1581                 return r;\r
1582             },\r
1583 \r
1584             "last-child" : function(c){\r
1585                 var r = [], ri = -1, n;\r
1586                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1587                     while((n = n.nextSibling) && n.nodeType != 1);\r
1588                     if(!n){\r
1589                         r[++ri] = ci;\r
1590                     }\r
1591                 }\r
1592                 return r;\r
1593             },\r
1594 \r
1595             "nth-child" : function(c, a) {\r
1596                 var r = [], ri = -1,\r
1597                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),\r
1598                         f = (m[1] || 1) - 0, l = m[2] - 0;\r
1599                 for(var i = 0, n; n = c[i]; i++){\r
1600                     var pn = n.parentNode;\r
1601                     if (batch != pn._batch) {\r
1602                         var j = 0;\r
1603                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){\r
1604                             if(cn.nodeType == 1){\r
1605                                cn.nodeIndex = ++j;\r
1606                             }\r
1607                         }\r
1608                         pn._batch = batch;\r
1609                     }\r
1610                     if (f == 1) {\r
1611                         if (l == 0 || n.nodeIndex == l){\r
1612                             r[++ri] = n;\r
1613                         }\r
1614                     } else if ((n.nodeIndex + l) % f == 0){\r
1615                         r[++ri] = n;\r
1616                     }\r
1617                 }\r
1618 \r
1619                 return r;\r
1620             },\r
1621 \r
1622             "only-child" : function(c){\r
1623                 var r = [], ri = -1;;\r
1624                 for(var i = 0, ci; ci = c[i]; i++){\r
1625                     if(!prev(ci) && !next(ci)){\r
1626                         r[++ri] = ci;\r
1627                     }\r
1628                 }\r
1629                 return r;\r
1630             },\r
1631 \r
1632             "empty" : function(c){\r
1633                 var r = [], ri = -1;\r
1634                 for(var i = 0, ci; ci = c[i]; i++){\r
1635                     var cns = ci.childNodes, j = 0, cn, empty = true;\r
1636                     while(cn = cns[j]){\r
1637                         ++j;\r
1638                         if(cn.nodeType == 1 || cn.nodeType == 3){\r
1639                             empty = false;\r
1640                             break;\r
1641                         }\r
1642                     }\r
1643                     if(empty){\r
1644                         r[++ri] = ci;\r
1645                     }\r
1646                 }\r
1647                 return r;\r
1648             },\r
1649 \r
1650             "contains" : function(c, v){\r
1651                 var r = [], ri = -1;\r
1652                 for(var i = 0, ci; ci = c[i]; i++){\r
1653                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){\r
1654                         r[++ri] = ci;\r
1655                     }\r
1656                 }\r
1657                 return r;\r
1658             },\r
1659 \r
1660             "nodeValue" : function(c, v){\r
1661                 var r = [], ri = -1;\r
1662                 for(var i = 0, ci; ci = c[i]; i++){\r
1663                     if(ci.firstChild && ci.firstChild.nodeValue == v){\r
1664                         r[++ri] = ci;\r
1665                     }\r
1666                 }\r
1667                 return r;\r
1668             },\r
1669 \r
1670             "checked" : function(c){\r
1671                 var r = [], ri = -1;\r
1672                 for(var i = 0, ci; ci = c[i]; i++){\r
1673                     if(ci.checked == true){\r
1674                         r[++ri] = ci;\r
1675                     }\r
1676                 }\r
1677                 return r;\r
1678             },\r
1679 \r
1680             "not" : function(c, ss){\r
1681                 return Ext.DomQuery.filter(c, ss, true);\r
1682             },\r
1683 \r
1684             "any" : function(c, selectors){\r
1685                 var ss = selectors.split('|'),\r
1686                         r = [], ri = -1, s;\r
1687                 for(var i = 0, ci; ci = c[i]; i++){\r
1688                     for(var j = 0; s = ss[j]; j++){\r
1689                         if(Ext.DomQuery.is(ci, s)){\r
1690                             r[++ri] = ci;\r
1691                             break;\r
1692                         }\r
1693                     }\r
1694                 }\r
1695                 return r;\r
1696             },\r
1697 \r
1698             "odd" : function(c){\r
1699                 return this["nth-child"](c, "odd");\r
1700             },\r
1701 \r
1702             "even" : function(c){\r
1703                 return this["nth-child"](c, "even");\r
1704             },\r
1705 \r
1706             "nth" : function(c, a){\r
1707                 return c[a-1] || [];\r
1708             },\r
1709 \r
1710             "first" : function(c){\r
1711                 return c[0] || [];\r
1712             },\r
1713 \r
1714             "last" : function(c){\r
1715                 return c[c.length-1] || [];\r
1716             },\r
1717 \r
1718             "has" : function(c, ss){\r
1719                 var s = Ext.DomQuery.select,\r
1720                         r = [], ri = -1;\r
1721                 for(var i = 0, ci; ci = c[i]; i++){\r
1722                     if(s(ss, ci).length > 0){\r
1723                         r[++ri] = ci;\r
1724                     }\r
1725                 }\r
1726                 return r;\r
1727             },\r
1728 \r
1729             "next" : function(c, ss){\r
1730                 var is = Ext.DomQuery.is,\r
1731                         r = [], ri = -1;\r
1732                 for(var i = 0, ci; ci = c[i]; i++){\r
1733                     var n = next(ci);\r
1734                     if(n && is(n, ss)){\r
1735                         r[++ri] = ci;\r
1736                     }\r
1737                 }\r
1738                 return r;\r
1739             },\r
1740 \r
1741             "prev" : function(c, ss){\r
1742                 var is = Ext.DomQuery.is,\r
1743                         r = [], ri = -1;\r
1744                 for(var i = 0, ci; ci = c[i]; i++){\r
1745                     var n = prev(ci);\r
1746                     if(n && is(n, ss)){\r
1747                         r[++ri] = ci;\r
1748                     }\r
1749                 }\r
1750                 return r;\r
1751             }\r
1752         }\r
1753     };\r
1754 }();\r
1755 \r
1756 /**\r
1757  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}\r
1758  * @param {String} path The selector/xpath query\r
1759  * @param {Node} root (optional) The start of the query (defaults to document).\r
1760  * @return {Array}\r
1761  * @member Ext\r
1762  * @method query\r
1763  */\r
1764 Ext.query = Ext.DomQuery.select;\r
1765 (function(){
1766
1767 var EXTUTIL = Ext.util,
1768     TOARRAY = Ext.toArray,
1769     EACH = Ext.each,
1770     ISOBJECT = Ext.isObject,
1771     TRUE = true,
1772     FALSE = false;
1773 /**
1774  * @class Ext.util.Observable
1775  * Base class that provides a common interface for publishing events. Subclasses are expected to
1776  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1777  * with configured listeners defined.<br>
1778  * For example:
1779  * <pre><code>
1780 Employee = Ext.extend(Ext.util.Observable, {
1781     constructor: function(config){
1782         this.name = config.name;
1783         this.addEvents({
1784             "fired" : true,
1785             "quit" : true
1786         });
1787
1788         // Copy configured listeners into *this* object so that the base class&#39;s
1789         // constructor will add them.
1790         this.listeners = config.listeners;
1791
1792         // Call our superclass constructor to complete construction process.
1793         Employee.superclass.constructor.call(config)
1794     }
1795 });
1796 </code></pre>
1797  * This could then be used like this:<pre><code>
1798 var newEmployee = new Employee({
1799     name: employeeName,
1800     listeners: {
1801         quit: function() {
1802             // By default, "this" will be the object that fired the event.
1803             alert(this.name + " has quit!");
1804         }
1805     }
1806 });
1807 </code></pre>
1808  */
1809 EXTUTIL.Observable = function(){
1810     /**
1811      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1812      * object during initialization.  This should be a valid listeners config object as specified in the
1813      * {@link #addListener} example for attaching multiple handlers at once.</p>
1814      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1815      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1816      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1817      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1818      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1819      * has been rendered. A plugin can simplify this step:<pre><code>
1820 // Plugin is configured with a listeners config object.
1821 // The Component is appended to the argument list of all handler functions.
1822 Ext.DomObserver = Ext.extend(Object, {
1823     constructor: function(config) {
1824         this.listeners = config.listeners ? config.listeners : config;
1825     },
1826
1827     // Component passes itself into plugin&#39;s init method
1828     init: function(c) {
1829         var p, l = this.listeners;
1830         for (p in l) {
1831             if (Ext.isFunction(l[p])) {
1832                 l[p] = this.createHandler(l[p], c);
1833             } else {
1834                 l[p].fn = this.createHandler(l[p].fn, c);
1835             }
1836         }
1837
1838         // Add the listeners to the Element immediately following the render call
1839         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1840             var e = c.getEl();
1841             if (e) {
1842                 e.on(l);
1843             }
1844         });
1845     },
1846
1847     createHandler: function(fn, c) {
1848         return function(e) {
1849             fn.call(this, e, c);
1850         };
1851     }
1852 });
1853
1854 var combo = new Ext.form.ComboBox({
1855
1856     // Collapse combo when its element is clicked on
1857     plugins: [ new Ext.DomObserver({
1858         click: function(evt, comp) {
1859             comp.collapse();
1860         }
1861     })],
1862     store: myStore,
1863     typeAhead: true,
1864     mode: 'local',
1865     triggerAction: 'all'
1866 });
1867      * </code></pre></p>
1868      */
1869     var me = this, e = me.events;
1870     if(me.listeners){
1871         me.on(me.listeners);
1872         delete me.listeners;
1873     }
1874     me.events = e || {};
1875 };
1876
1877 EXTUTIL.Observable.prototype = {
1878     // private
1879     filterOptRe : /^(?:scope|delay|buffer|single)$/,
1880
1881     /**
1882      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
1883      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
1884      * by calling {@link #enableBubble}.</p>
1885      * @param {String} eventName The name of the event to fire.
1886      * @param {Object...} args Variable number of parameters are passed to handlers.
1887      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
1888      */
1889     fireEvent : function(){
1890         var a = TOARRAY(arguments),
1891             ename = a[0].toLowerCase(),
1892             me = this,
1893             ret = TRUE,
1894             ce = me.events[ename],
1895             q,
1896             c;
1897         if (me.eventsSuspended === TRUE) {
1898             if (q = me.eventQueue) {
1899                 q.push(a);
1900             }
1901         }
1902         else if(ISOBJECT(ce) && ce.bubble){
1903             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
1904                 return FALSE;
1905             }
1906             c = me.getBubbleTarget && me.getBubbleTarget();
1907             if(c && c.enableBubble) {
1908                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
1909                     c.enableBubble(ename);
1910                 }
1911                 return c.fireEvent.apply(c, a);
1912             }
1913         }
1914         else {
1915             if (ISOBJECT(ce)) {
1916                 a.shift();
1917                 ret = ce.fire.apply(ce, a);
1918             }
1919         }
1920         return ret;
1921     },
1922
1923     /**
1924      * Appends an event handler to this object.
1925      * @param {String}   eventName The name of the event to listen for.
1926      * @param {Function} handler The method the event invokes.
1927      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1928      * <b>If omitted, defaults to the object which fired the event.</b>
1929      * @param {Object}   options (optional) An object containing handler configuration.
1930      * properties. This may contain any of the following properties:<ul>
1931      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1932      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
1933      * <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>
1934      * <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>
1935      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1936      * by the specified number of milliseconds. If the event fires again within that time, the original
1937      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1938      * <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>
1939      * if the event was bubbled up from a child Observable.</div></li>
1940      * </ul><br>
1941      * <p>
1942      * <b>Combining Options</b><br>
1943      * Using the options argument, it is possible to combine different types of listeners:<br>
1944      * <br>
1945      * A delayed, one-time listener.
1946      * <pre><code>
1947 myDataView.on('click', this.onClick, this, {
1948 single: true,
1949 delay: 100
1950 });</code></pre>
1951      * <p>
1952      * <b>Attaching multiple handlers in 1 call</b><br>
1953      * The method also allows for a single argument to be passed which is a config object containing properties
1954      * which specify multiple handlers.
1955      * <p>
1956      * <pre><code>
1957 myGridPanel.on({
1958 'click' : {
1959     fn: this.onClick,
1960     scope: this,
1961     delay: 100
1962 },
1963 'mouseover' : {
1964     fn: this.onMouseOver,
1965     scope: this
1966 },
1967 'mouseout' : {
1968     fn: this.onMouseOut,
1969     scope: this
1970 }
1971 });</code></pre>
1972  * <p>
1973  * Or a shorthand syntax:<br>
1974  * <pre><code>
1975 myGridPanel.on({
1976 'click' : this.onClick,
1977 'mouseover' : this.onMouseOver,
1978 'mouseout' : this.onMouseOut,
1979  scope: this
1980 });</code></pre>
1981      */
1982     addListener : function(eventName, fn, scope, o){
1983         var me = this,
1984             e,
1985             oe,
1986             isF,
1987         ce;
1988         if (ISOBJECT(eventName)) {
1989             o = eventName;
1990             for (e in o){
1991                 oe = o[e];
1992                 if (!me.filterOptRe.test(e)) {
1993                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
1994                 }
1995             }
1996         } else {
1997             eventName = eventName.toLowerCase();
1998             ce = me.events[eventName] || TRUE;
1999             if (Ext.isBoolean(ce)) {
2000                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2001             }
2002             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2003         }
2004     },
2005
2006     /**
2007      * Removes an event handler.
2008      * @param {String}   eventName The type of event the handler was associated with.
2009      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2010      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2011      */
2012     removeListener : function(eventName, fn, scope){
2013         var ce = this.events[eventName.toLowerCase()];
2014         if (ISOBJECT(ce)) {
2015             ce.removeListener(fn, scope);
2016         }
2017     },
2018
2019     /**
2020      * Removes all listeners for this object
2021      */
2022     purgeListeners : function(){
2023         var events = this.events,
2024             evt,
2025             key;
2026         for(key in events){
2027             evt = events[key];
2028             if(ISOBJECT(evt)){
2029                 evt.clearListeners();
2030             }
2031         }
2032     },
2033
2034     /**
2035      * Adds the specified events to the list of events which this Observable may fire.
2036      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2037      * or the first event name string if multiple event names are being passed as separate parameters.
2038      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2039      * Usage:<pre><code>
2040 this.addEvents('storeloaded', 'storecleared');
2041 </code></pre>
2042      */
2043     addEvents : function(o){
2044         var me = this;
2045         me.events = me.events || {};
2046         if (Ext.isString(o)) {
2047             EACH(arguments, function(a) {
2048                 me.events[a] = me.events[a] || TRUE;
2049             });
2050         } else {
2051             Ext.applyIf(me.events, o);
2052         }
2053     },
2054
2055     /**
2056      * Checks to see if this object has any listeners for a specified event
2057      * @param {String} eventName The name of the event to check for
2058      * @return {Boolean} True if the event is being listened for, else false
2059      */
2060     hasListener : function(eventName){
2061         var e = this.events[eventName];
2062         return ISOBJECT(e) && e.listeners.length > 0;
2063     },
2064
2065     /**
2066      * Suspend the firing of all events. (see {@link #resumeEvents})
2067      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2068      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2069      */
2070     suspendEvents : function(queueSuspended){
2071         this.eventsSuspended = TRUE;
2072         if(queueSuspended && !this.eventQueue){
2073             this.eventQueue = [];
2074         }
2075     },
2076
2077     /**
2078      * Resume firing events. (see {@link #suspendEvents})
2079      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2080      * events fired during event suspension will be sent to any listeners now.
2081      */
2082     resumeEvents : function(){
2083         var me = this,
2084             queued = me.eventQueue || [];
2085         me.eventsSuspended = FALSE;
2086         delete me.eventQueue;
2087         EACH(queued, function(e) {
2088             me.fireEvent.apply(me, e);
2089         });
2090     }
2091 };
2092
2093 var OBSERVABLE = EXTUTIL.Observable.prototype;
2094 /**
2095  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2096  * @param {String}   eventName     The type of event to listen for
2097  * @param {Function} handler       The method the event invokes
2098  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2099  * <b>If omitted, defaults to the object which fired the event.</b>
2100  * @param {Object}   options       (optional) An object containing handler configuration.
2101  * @method
2102  */
2103 OBSERVABLE.on = OBSERVABLE.addListener;
2104 /**
2105  * Removes an event handler (shorthand for {@link #removeListener}.)
2106  * @param {String}   eventName     The type of event the handler was associated with.
2107  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2108  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2109  * @method
2110  */
2111 OBSERVABLE.un = OBSERVABLE.removeListener;
2112
2113 /**
2114  * Removes <b>all</b> added captures from the Observable.
2115  * @param {Observable} o The Observable to release
2116  * @static
2117  */
2118 EXTUTIL.Observable.releaseCapture = function(o){
2119     o.fireEvent = OBSERVABLE.fireEvent;
2120 };
2121
2122 function createTargeted(h, o, scope){
2123     return function(){
2124         if(o.target == arguments[0]){
2125             h.apply(scope, TOARRAY(arguments));
2126         }
2127     };
2128 };
2129
2130 function createBuffered(h, o, scope){
2131     var task = new EXTUTIL.DelayedTask();
2132     return function(){
2133         task.delay(o.buffer, h, scope, TOARRAY(arguments));
2134     };
2135 }
2136
2137 function createSingle(h, e, fn, scope){
2138     return function(){
2139         e.removeListener(fn, scope);
2140         return h.apply(scope, arguments);
2141     };
2142 }
2143
2144 function createDelayed(h, o, scope){
2145     return function(){
2146         var args = TOARRAY(arguments);
2147         (function(){
2148             h.apply(scope, args);
2149         }).defer(o.delay || 10);
2150     };
2151 };
2152
2153 EXTUTIL.Event = function(obj, name){
2154     this.name = name;
2155     this.obj = obj;
2156     this.listeners = [];
2157 };
2158
2159 EXTUTIL.Event.prototype = {
2160     addListener : function(fn, scope, options){
2161         var me = this,
2162             l;
2163         scope = scope || me.obj;
2164         if(!me.isListening(fn, scope)){
2165             l = me.createListener(fn, scope, options);
2166             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2167                 me.listeners = me.listeners.slice(0);
2168             }
2169             me.listeners.push(l);
2170         }
2171     },
2172
2173     createListener: function(fn, scope, o){
2174         o = o || {}, scope = scope || this.obj;
2175         var l = {
2176             fn: fn,
2177             scope: scope,
2178             options: o
2179         }, h = fn;
2180         if(o.target){
2181             h = createTargeted(h, o, scope);
2182         }
2183         if(o.delay){
2184             h = createDelayed(h, o, scope);
2185         }
2186         if(o.single){
2187             h = createSingle(h, this, fn, scope);
2188         }
2189         if(o.buffer){
2190             h = createBuffered(h, o, scope);
2191         }
2192         l.fireFn = h;
2193         return l;
2194     },
2195
2196     findListener : function(fn, scope){
2197         var s, ret = -1;
2198         EACH(this.listeners, function(l, i) {
2199             s = l.scope;
2200             if(l.fn == fn && (s == scope || s == this.obj)){
2201                 ret = i;
2202                 return FALSE;
2203             }
2204         },
2205         this);
2206         return ret;
2207     },
2208
2209     isListening : function(fn, scope){
2210         return this.findListener(fn, scope) != -1;
2211     },
2212
2213     removeListener : function(fn, scope){
2214         var index,
2215             me = this,
2216             ret = FALSE;
2217         if((index = me.findListener(fn, scope)) != -1){
2218             if (me.firing) {
2219                 me.listeners = me.listeners.slice(0);
2220             }
2221             me.listeners.splice(index, 1);
2222             ret = TRUE;
2223         }
2224         return ret;
2225     },
2226
2227     clearListeners : function(){
2228         this.listeners = [];
2229     },
2230
2231     fire : function(){
2232         var me = this,
2233             args = TOARRAY(arguments),
2234             ret = TRUE;
2235
2236         EACH(me.listeners, function(l) {
2237             me.firing = TRUE;
2238             if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2239                 return ret = me.firing = FALSE;
2240             }
2241         });
2242         me.firing = FALSE;
2243         return ret;
2244     }
2245 };
2246 })();/**\r
2247  * @class Ext.util.Observable\r
2248  */\r
2249 Ext.apply(Ext.util.Observable.prototype, function(){    \r
2250     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)\r
2251     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2252     // private\r
2253     function getMethodEvent(method){\r
2254         var e = (this.methodEvents = this.methodEvents ||\r
2255         {})[method], returnValue, v, cancel, obj = this;\r
2256         \r
2257         if (!e) {\r
2258             this.methodEvents[method] = e = {};\r
2259             e.originalFn = this[method];\r
2260             e.methodName = method;\r
2261             e.before = [];\r
2262             e.after = [];\r
2263             \r
2264             var makeCall = function(fn, scope, args){\r
2265                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {\r
2266                     if (Ext.isObject(v)) {\r
2267                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;\r
2268                         cancel = !!v.cancel;\r
2269                     }\r
2270                     else \r
2271                         if (v === false) {\r
2272                             cancel = true;\r
2273                         }\r
2274                         else {\r
2275                             returnValue = v;\r
2276                         }\r
2277                 }\r
2278             };\r
2279             \r
2280             this[method] = function(){\r
2281                 var args = Ext.toArray(arguments);\r
2282                 returnValue = v = undefined;\r
2283                 cancel = false;\r
2284                 \r
2285                 Ext.each(e.before, function(b){\r
2286                     makeCall(b.fn, b.scope, args);\r
2287                     if (cancel) {\r
2288                         return returnValue;\r
2289                     }\r
2290                 });\r
2291                 \r
2292                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {\r
2293                     returnValue = v;\r
2294                 }\r
2295                 Ext.each(e.after, function(a){\r
2296                     makeCall(a.fn, a.scope, args);\r
2297                     if (cancel) {\r
2298                         return returnValue;\r
2299                     }\r
2300                 });\r
2301                 return returnValue;\r
2302             };\r
2303         }\r
2304         return e;\r
2305     }\r
2306     \r
2307     return {\r
2308         // these are considered experimental\r
2309         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2310         // adds an "interceptor" called before the original method\r
2311         beforeMethod: function(method, fn, scope){\r
2312             getMethodEvent.call(this, method).before.push({\r
2313                 fn: fn,\r
2314                 scope: scope\r
2315             });\r
2316         },\r
2317         \r
2318         // adds a "sequence" called after the original method\r
2319         afterMethod: function(method, fn, scope){\r
2320             getMethodEvent.call(this, method).after.push({\r
2321                 fn: fn,\r
2322                 scope: scope\r
2323             });\r
2324         },\r
2325         \r
2326         removeMethodListener: function(method, fn, scope){\r
2327             var e = getMethodEvent.call(this, method), found = false;\r
2328             Ext.each(e.before, function(b, i, arr){\r
2329                 if (b.fn == fn && b.scope == scope) {\r
2330                     arr.splice(i, 1);\r
2331                     found = true;\r
2332                     return false;\r
2333                 }\r
2334             });\r
2335             if (!found) {\r
2336                 Ext.each(e.after, function(a, i, arr){\r
2337                     if (a.fn == fn && a.scope == scope) {\r
2338                         arr.splice(i, 1);\r
2339                         return false;\r
2340                     }\r
2341                 });\r
2342             }\r
2343         },\r
2344         \r
2345         /**\r
2346          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.\r
2347          * @param {Object} o The Observable whose events this object is to relay.\r
2348          * @param {Array} events Array of event names to relay.\r
2349          */\r
2350         relayEvents: function(o, events){\r
2351             var me = this;\r
2352             function createHandler(ename){\r
2353                 return function(){\r
2354                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));\r
2355                 };\r
2356             }\r
2357             Ext.each(events, function(ename){\r
2358                 me.events[ename] = me.events[ename] || true;\r
2359                 o.on(ename, createHandler(ename), me);\r
2360             });\r
2361         },\r
2362         \r
2363         /**\r
2364          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling\r
2365          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>\r
2366          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default\r
2367          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to\r
2368          * access the required target more quickly.</p>\r
2369          * <p>Example:</p><pre><code>\r
2370 Ext.override(Ext.form.Field, {\r
2371 //  Add functionality to Field's initComponent to enable the change event to bubble\r
2372     initComponent: Ext.form.Field.prototype.initComponent.createSequence(function() {\r
2373         this.enableBubble('change');\r
2374     }),\r
2375 \r
2376 //  We know that we want Field's events to bubble directly to the FormPanel.\r
2377     getBubbleTarget: function() {\r
2378         if (!this.formPanel) {\r
2379             this.formPanel = this.findParentByType('form');\r
2380         }\r
2381         return this.formPanel;\r
2382     }\r
2383 });\r
2384 \r
2385 var myForm = new Ext.formPanel({\r
2386     title: 'User Details',\r
2387     items: [{\r
2388         ...\r
2389     }],\r
2390     listeners: {\r
2391         change: function() {\r
2392 //          Title goes red if form has been modified.\r
2393             myForm.header.setStyle("color", "red");\r
2394         }\r
2395     }\r
2396 });\r
2397 </code></pre>\r
2398          * @param {Object} events The event name to bubble, or an Array of event names.\r
2399          */\r
2400         enableBubble: function(events){\r
2401             var me = this;\r
2402             if(!Ext.isEmpty(events)){\r
2403                 events = Ext.isArray(events) ? events : Ext.toArray(arguments);\r
2404                 Ext.each(events, function(ename){\r
2405                     ename = ename.toLowerCase();\r
2406                     var ce = me.events[ename] || true;\r
2407                     if (Ext.isBoolean(ce)) {\r
2408                         ce = new Ext.util.Event(me, ename);\r
2409                         me.events[ename] = ce;\r
2410                     }\r
2411                     ce.bubble = true;\r
2412                 });\r
2413             }\r
2414         }\r
2415     };\r
2416 }());\r
2417 \r
2418 \r
2419 /**\r
2420  * Starts capture on the specified Observable. All events will be passed\r
2421  * to the supplied function with the event name + standard signature of the event\r
2422  * <b>before</b> the event is fired. If the supplied function returns false,\r
2423  * the event will not fire.\r
2424  * @param {Observable} o The Observable to capture\r
2425  * @param {Function} fn The function to call\r
2426  * @param {Object} scope (optional) The scope (this object) for the fn\r
2427  * @static\r
2428  */\r
2429 Ext.util.Observable.capture = function(o, fn, scope){\r
2430     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);\r
2431 };\r
2432 \r
2433 \r
2434 /**\r
2435  * Sets observability on the passed class constructor.<p>\r
2436  * <p>This makes any event fired on any instance of the passed class also fire a single event through\r
2437  * the <i>class</i> allowing for central handling of events on many instances at once.</p>\r
2438  * <p>Usage:</p><pre><code>\r
2439 Ext.util.Observable.observeClass(Ext.data.Connection);\r
2440 Ext.data.Connection.on('beforerequest', function(con, options) {\r
2441     console.log("Ajax request made to " + options.url);\r
2442 });</code></pre>\r
2443  * @param {Function} c The class constructor to make observable.\r
2444  * @static\r
2445  */\r
2446 Ext.util.Observable.observeClass = function(c){\r
2447     Ext.apply(c, new Ext.util.Observable());\r
2448     c.prototype.fireEvent = function(){\r
2449         return (c.fireEvent.apply(c, arguments) !== false) &&\r
2450         (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false);\r
2451     };\r
2452 };/**
2453  * @class Ext.EventManager
2454  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2455  * several useful events directly.
2456  * See {@link Ext.EventObject} for more details on normalized event objects.
2457  * @singleton
2458  */
2459 Ext.EventManager = function(){
2460     var docReadyEvent, 
2461         docReadyProcId, 
2462         docReadyState = false,        
2463         E = Ext.lib.Event,
2464         D = Ext.lib.Dom,
2465         DOC = document,
2466         WINDOW = window,
2467         IEDEFERED = "ie-deferred-loader",
2468         DOMCONTENTLOADED = "DOMContentLoaded",
2469         elHash = {},
2470         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
2471
2472     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2473     function addListener(el, ename, fn, wrap, scope){        
2474         var id = Ext.id(el),
2475             es = elHash[id] = elHash[id] || {};         
2476        
2477         (es[ename] = es[ename] || []).push([fn, wrap, scope]);
2478         E.on(el, ename, wrap);
2479
2480         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2481         // without breaking ExtJS.
2482         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
2483             var args = ["DOMMouseScroll", wrap, false];
2484             el.addEventListener.apply(el, args);
2485             E.on(window, 'unload', function(){
2486                 el.removeEventListener.apply(el, args);                
2487             });
2488         }
2489         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
2490             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2491         }
2492     };
2493     
2494     function fireDocReady(){
2495         if(!docReadyState){            
2496             Ext.isReady = docReadyState = true;
2497             if(docReadyProcId){
2498                 clearInterval(docReadyProcId);
2499             }
2500             if(Ext.isGecko || Ext.isOpera) {
2501                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2502             }
2503             if(Ext.isIE){
2504                 var defer = DOC.getElementById(IEDEFERED);
2505                 if(defer){
2506                     defer.onreadystatechange = null;
2507                     defer.parentNode.removeChild(defer);
2508                 }
2509             }
2510             if(docReadyEvent){
2511                 docReadyEvent.fire();
2512                 docReadyEvent.clearListeners();
2513             }
2514         }
2515     };
2516
2517     function initDocReady(){
2518         var COMPLETE = "complete";
2519             
2520         docReadyEvent = new Ext.util.Event();
2521         if (Ext.isGecko || Ext.isOpera) {
2522             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2523         } else if (Ext.isIE){
2524             DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");            
2525             DOC.getElementById(IEDEFERED).onreadystatechange = function(){
2526                 if(this.readyState == COMPLETE){
2527                     fireDocReady();
2528                 }
2529             };
2530         } else if (Ext.isWebKit){
2531             docReadyProcId = setInterval(function(){                
2532                 if(DOC.readyState == COMPLETE) {
2533                     fireDocReady();
2534                  }
2535             }, 10);
2536         }
2537         // no matter what, make sure it fires on load
2538         E.on(WINDOW, "load", fireDocReady);
2539     };
2540
2541     function createTargeted(h, o){
2542         return function(){
2543             var args = Ext.toArray(arguments);
2544             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2545                 h.apply(this, args);
2546             }
2547         };
2548     };    
2549     
2550     function createBuffered(h, o){
2551         var task = new Ext.util.DelayedTask(h);
2552         return function(e){
2553             // create new event object impl so new events don't wipe out properties            
2554             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2555         };
2556     };
2557
2558     function createSingle(h, el, ename, fn, scope){
2559         return function(e){
2560             Ext.EventManager.removeListener(el, ename, fn, scope);
2561             h(e);
2562         };
2563     };
2564
2565     function createDelayed(h, o){
2566         return function(e){
2567             // create new event object impl so new events don't wipe out properties   
2568             e = new Ext.EventObjectImpl(e);
2569             setTimeout(function(){
2570                 h(e);
2571             }, o.delay || 10);
2572         };
2573     };
2574
2575     function listen(element, ename, opt, fn, scope){
2576         var o = !Ext.isObject(opt) ? {} : opt,
2577             el = Ext.getDom(element);
2578             
2579         fn = fn || o.fn; 
2580         scope = scope || o.scope;
2581         
2582         if(!el){
2583             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2584         }
2585         function h(e){
2586             // prevent errors while unload occurring
2587             if(!Ext){// !window[xname]){  ==> can't we do this? 
2588                 return;
2589             }
2590             e = Ext.EventObject.setEvent(e);
2591             var t;
2592             if (o.delegate) {
2593                 if(!(t = e.getTarget(o.delegate, el))){
2594                     return;
2595                 }
2596             } else {
2597                 t = e.target;
2598             }            
2599             if (o.stopEvent) {
2600                 e.stopEvent();
2601             }
2602             if (o.preventDefault) {
2603                e.preventDefault();
2604             }
2605             if (o.stopPropagation) {
2606                 e.stopPropagation();
2607             }
2608             if (o.normalized) {
2609                 e = e.browserEvent;
2610             }
2611             
2612             fn.call(scope || el, e, t, o);
2613         };
2614         if(o.target){
2615             h = createTargeted(h, o);
2616         }
2617         if(o.delay){
2618             h = createDelayed(h, o);
2619         }
2620         if(o.single){
2621             h = createSingle(h, el, ename, fn, scope);
2622         }
2623         if(o.buffer){
2624             h = createBuffered(h, o);
2625         }
2626
2627         addListener(el, ename, fn, h, scope);
2628         return h;
2629     };
2630
2631     var pub = {
2632         /**
2633          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2634          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2635          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2636          * @param {String} eventName The name of the event to listen for.
2637          * @param {Function} handler The handler function the event invokes. This function is passed
2638          * the following parameters:<ul>
2639          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2640          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2641          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2642          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2643          * </ul>
2644          * @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>.
2645          * @param {Object} options (optional) An object containing handler configuration properties.
2646          * This may contain any of the following properties:<ul>
2647          * <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>
2648          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2649          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2650          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2651          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2652          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2653          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2654          * <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>
2655          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2656          * by the specified number of milliseconds. If the event fires again within that time, the original
2657          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2658          * <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>
2659          * </ul><br>
2660          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2661          */
2662         addListener : function(element, eventName, fn, scope, options){                                       
2663             if(Ext.isObject(eventName)){                
2664                 var o = eventName, e, val;
2665                 for(e in o){
2666                     val = o[e];
2667                     if(!propRe.test(e)){                                             
2668                         if(Ext.isFunction(val)){
2669                             // shared options
2670                             listen(element, e, o, val, o.scope);
2671                         }else{
2672                             // individual options
2673                             listen(element, e, val);
2674                         }
2675                     }
2676                 }
2677             } else {
2678                 listen(element, eventName, options, fn, scope);
2679             }
2680         },
2681         
2682         /**
2683          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
2684          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2685          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
2686          * @param {String} eventName The name of the event.
2687          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2688          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2689          * then this must refer to the same object.
2690          */
2691         removeListener : function(element, eventName, fn, scope){            
2692             var el = Ext.getDom(element),
2693                 id = Ext.id(el),
2694                 wrap;      
2695             
2696             Ext.each((elHash[id] || {})[eventName], function (v,i,a) {
2697                 if (Ext.isArray(v) && v[0] == fn && (!scope || v[2] == scope)) {                                    
2698                     E.un(el, eventName, wrap = v[1]);
2699                     a.splice(i,1);
2700                     return false;                    
2701                 }
2702             });    
2703
2704             // jQuery workaround that should be removed from Ext Core
2705             if(eventName == "mousewheel" && el.addEventListener && wrap){
2706                 el.removeEventListener("DOMMouseScroll", wrap, false);
2707             }
2708                         
2709             if(eventName == "mousedown" && el == DOC && wrap){ // fix stopped mousedowns on the document
2710                 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
2711             }
2712         },
2713         
2714         /**
2715          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
2716          * directly on an Element in favor of calling this version.
2717          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
2718          */
2719         removeAll : function(el){
2720             var id = Ext.id(el = Ext.getDom(el)), 
2721                 es = elHash[id],                 
2722                 ename;
2723            
2724             for(ename in es){
2725                 if(es.hasOwnProperty(ename)){                        
2726                     Ext.each(es[ename], function(v) {
2727                         E.un(el, ename, v.wrap);                    
2728                     });
2729                 }            
2730             }
2731             elHash[id] = null;       
2732         },
2733
2734         /**
2735          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
2736          * accessed shorthanded as Ext.onReady().
2737          * @param {Function} fn The method the event invokes.
2738          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
2739          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
2740          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
2741          */
2742         onDocumentReady : function(fn, scope, options){
2743             if(docReadyState){ // if it already fired
2744                 docReadyEvent.addListener(fn, scope, options);
2745                 docReadyEvent.fire();
2746                 docReadyEvent.clearListeners();               
2747             } else {
2748                 if(!docReadyEvent) initDocReady();
2749                 options = options || {};
2750                 options.delay = options.delay || 1;                
2751                 docReadyEvent.addListener(fn, scope, options);
2752             }
2753         },
2754         
2755         elHash : elHash   
2756     };
2757      /**
2758      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
2759      * @param {String/HTMLElement} el The html element or id to assign the event handler to
2760      * @param {String} eventName The name of the event to listen for.
2761      * @param {Function} handler The handler function the event invokes.
2762      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
2763      * @param {Object} options (optional) An object containing standard {@link #addListener} options
2764      * @member Ext.EventManager
2765      * @method on
2766      */
2767     pub.on = pub.addListener;
2768     /**
2769      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
2770      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
2771      * @param {String} eventName The name of the event.
2772      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
2773      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2774      * then this must refer to the same object.
2775      * @member Ext.EventManager
2776      * @method un
2777      */
2778     pub.un = pub.removeListener;
2779
2780     pub.stoppedMouseDownEvent = new Ext.util.Event();
2781     return pub;
2782 }();
2783 /**
2784   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
2785   * @param {Function} fn The method the event invokes.
2786   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
2787   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
2788   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
2789   * @member Ext
2790   * @method onReady
2791  */
2792 Ext.onReady = Ext.EventManager.onDocumentReady;
2793
2794
2795 //Initialize doc classes
2796 (function(){
2797     
2798     var initExtCss = function(){
2799         // find the body element
2800         var bd = document.body || document.getElementsByTagName('body')[0];
2801         if(!bd){ return false; }
2802         var cls = [' ',
2803                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
2804                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
2805                 : Ext.isOpera ? "ext-opera"
2806                 : Ext.isWebKit ? "ext-webkit" : ""];
2807
2808         if(Ext.isSafari){
2809             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
2810         }else if(Ext.isChrome){
2811             cls.push("ext-chrome");
2812         }
2813
2814         if(Ext.isMac){
2815             cls.push("ext-mac");
2816         }
2817         if(Ext.isLinux){
2818             cls.push("ext-linux");
2819         }
2820
2821         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
2822             var p = bd.parentNode;
2823             if(p){
2824                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
2825             }
2826         }
2827         bd.className += cls.join(' ');
2828         return true;
2829     }
2830
2831     if(!initExtCss()){
2832         Ext.onReady(initExtCss);
2833     }
2834 })();
2835
2836
2837 /**
2838  * @class Ext.EventObject
2839  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject 
2840  * wraps the browser's native event-object normalizing cross-browser differences,
2841  * such as which mouse button is clicked, keys pressed, mechanisms to stop
2842  * event-propagation along with a method to prevent default actions from taking place.
2843  * <p>For example:</p>
2844  * <pre><code>
2845 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
2846     e.preventDefault();
2847     var target = e.getTarget(); // same as t (the target HTMLElement)
2848     ...
2849 }
2850 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
2851 myDiv.on(         // 'on' is shorthand for addListener
2852     "click",      // perform an action on click of myDiv
2853     handleClick   // reference to the action handler
2854 );  
2855 // other methods to do the same:
2856 Ext.EventManager.on("myDiv", 'click', handleClick);
2857 Ext.EventManager.addListener("myDiv", 'click', handleClick);
2858  </code></pre>
2859  * @singleton
2860  */
2861 Ext.EventObject = function(){
2862     var E = Ext.lib.Event,
2863         // safari keypress events for special keys return bad keycodes
2864         safariKeys = {
2865             3 : 13, // enter
2866             63234 : 37, // left
2867             63235 : 39, // right
2868             63232 : 38, // up
2869             63233 : 40, // down
2870             63276 : 33, // page up
2871             63277 : 34, // page down
2872             63272 : 46, // delete
2873             63273 : 36, // home
2874             63275 : 35  // end
2875         },
2876         // normalize button clicks
2877         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
2878                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
2879
2880     Ext.EventObjectImpl = function(e){
2881         if(e){
2882             this.setEvent(e.browserEvent || e);
2883         }
2884     };
2885
2886     Ext.EventObjectImpl.prototype = {
2887            /** @private */
2888         setEvent : function(e){
2889             var me = this;
2890             if(e == me || (e && e.browserEvent)){ // already wrapped
2891                 return e;
2892             }
2893             me.browserEvent = e;
2894             if(e){
2895                 // normalize buttons
2896                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
2897                 if(e.type == 'click' && me.button == -1){
2898                     me.button = 0;
2899                 }
2900                 me.type = e.type;
2901                 me.shiftKey = e.shiftKey;
2902                 // mac metaKey behaves like ctrlKey
2903                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
2904                 me.altKey = e.altKey;
2905                 // in getKey these will be normalized for the mac
2906                 me.keyCode = e.keyCode;
2907                 me.charCode = e.charCode;
2908                 // cache the target for the delayed and or buffered events
2909                 me.target = E.getTarget(e);
2910                 // same for XY
2911                 me.xy = E.getXY(e);
2912             }else{
2913                 me.button = -1;
2914                 me.shiftKey = false;
2915                 me.ctrlKey = false;
2916                 me.altKey = false;
2917                 me.keyCode = 0;
2918                 me.charCode = 0;
2919                 me.target = null;
2920                 me.xy = [0, 0];
2921             }
2922             return me;
2923         },
2924
2925         /**
2926          * Stop the event (preventDefault and stopPropagation)
2927          */
2928         stopEvent : function(){
2929             var me = this;
2930             if(me.browserEvent){
2931                 if(me.browserEvent.type == 'mousedown'){
2932                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
2933                 }
2934                 E.stopEvent(me.browserEvent);
2935             }
2936         },
2937
2938         /**
2939          * Prevents the browsers default handling of the event.
2940          */
2941         preventDefault : function(){
2942             if(this.browserEvent){
2943                 E.preventDefault(this.browserEvent);
2944             }
2945         },        
2946
2947         /**
2948          * Cancels bubbling of the event.
2949          */
2950         stopPropagation : function(){
2951             var me = this;
2952             if(me.browserEvent){
2953                 if(me.browserEvent.type == 'mousedown'){
2954                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
2955                 }
2956                 E.stopPropagation(me.browserEvent);
2957             }
2958         },
2959
2960         /**
2961          * Gets the character code for the event.
2962          * @return {Number}
2963          */
2964         getCharCode : function(){
2965             return this.charCode || this.keyCode;
2966         },
2967
2968         /**
2969          * Returns a normalized keyCode for the event.
2970          * @return {Number} The key code
2971          */
2972         getKey : function(){
2973             return this.normalizeKey(this.keyCode || this.charCode)
2974         },
2975         
2976         // private
2977         normalizeKey: function(k){
2978             return Ext.isSafari ? (safariKeys[k] || k) : k; 
2979         },
2980
2981         /**
2982          * Gets the x coordinate of the event.
2983          * @return {Number}
2984          */
2985         getPageX : function(){
2986             return this.xy[0];
2987         },
2988
2989         /**
2990          * Gets the y coordinate of the event.
2991          * @return {Number}
2992          */
2993         getPageY : function(){
2994             return this.xy[1];
2995         },
2996
2997         /**
2998          * Gets the page coordinates of the event.
2999          * @return {Array} The xy values like [x, y]
3000          */
3001         getXY : function(){
3002             return this.xy;
3003         },
3004
3005         /**
3006          * Gets the target for the event.
3007          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3008          * @param {Number/Mixed} maxDepth (optional) The max depth to
3009                 search as a number or element (defaults to 10 || document.body)
3010          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3011          * @return {HTMLelement}
3012          */
3013         getTarget : function(selector, maxDepth, returnEl){
3014             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3015         },
3016
3017         /**
3018          * Gets the related target.
3019          * @return {HTMLElement}
3020          */
3021         getRelatedTarget : function(){
3022             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3023         },
3024
3025         /**
3026          * Normalizes mouse wheel delta across browsers
3027          * @return {Number} The delta
3028          */
3029         getWheelDelta : function(){
3030             var e = this.browserEvent;
3031             var delta = 0;
3032             if(e.wheelDelta){ /* IE/Opera. */
3033                 delta = e.wheelDelta/120;
3034             }else if(e.detail){ /* Mozilla case. */
3035                 delta = -e.detail/3;
3036             }
3037             return delta;
3038         },
3039         
3040         /**
3041         * 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.
3042         * Example usage:<pre><code>
3043         // Handle click on any child of an element
3044         Ext.getBody().on('click', function(e){
3045             if(e.within('some-el')){
3046                 alert('Clicked on a child of some-el!');
3047             }
3048         });
3049         
3050         // Handle click directly on an element, ignoring clicks on child nodes
3051         Ext.getBody().on('click', function(e,t){
3052             if((t.id == 'some-el') && !e.within(t, true)){
3053                 alert('Clicked directly on some-el!');
3054             }
3055         });
3056         </code></pre>
3057          * @param {Mixed} el The id, DOM element or Ext.Element to check
3058          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3059          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3060          * @return {Boolean}
3061          */
3062         within : function(el, related, allowEl){
3063             if(el){
3064                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3065                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3066             }
3067             return false;
3068         }
3069      };
3070
3071     return new Ext.EventObjectImpl();
3072 }();/**\r
3073  * @class Ext.EventManager\r
3074  */\r
3075 Ext.apply(Ext.EventManager, function(){\r
3076         var resizeEvent, \r
3077         resizeTask, \r
3078         textEvent, \r
3079         textSize,\r
3080         D = Ext.lib.Dom,\r
3081         E = Ext.lib.Event,\r
3082         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,\r
3083         curWidth = 0,\r
3084         curHeight = 0,\r
3085         // note 1: IE fires ONLY the keydown event on specialkey autorepeat\r
3086         // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat\r
3087         // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)\r
3088         useKeydown = Ext.isWebKit ? \r
3089                     Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :\r
3090                     !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);\r
3091         \r
3092         return { \r
3093                 // private\r
3094             doResizeEvent: function(){\r
3095             var h = D.getViewHeight(),\r
3096                 w = D.getViewWidth();\r
3097             \r
3098             //whacky problem in IE where the resize event will fire even though the w/h are the same.\r
3099             if(curHeight != h || curWidth != w){\r
3100                 resizeEvent.fire(curWidth = w, curHeight = h);\r
3101             }\r
3102             },\r
3103             \r
3104             /**\r
3105              * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.\r
3106              * @param {Function} fn        The method the event invokes\r
3107              * @param {Object}   scope    An object that becomes the scope of the handler\r
3108              * @param {boolean}  options\r
3109              */\r
3110             onWindowResize : function(fn, scope, options){\r
3111                 if(!resizeEvent){\r
3112                     resizeEvent = new Ext.util.Event();\r
3113                     resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);\r
3114                     E.on(window, "resize", this.fireWindowResize, this);\r
3115                 }\r
3116                 resizeEvent.addListener(fn, scope, options);\r
3117             },\r
3118         \r
3119             // exposed only to allow manual firing\r
3120             fireWindowResize : function(){\r
3121                 if(resizeEvent){\r
3122                     if((Ext.isIE||Ext.isAir) && resizeTask){\r
3123                         resizeTask.delay(50);\r
3124                     }else{\r
3125                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
3126                     }\r
3127                 }\r
3128             },\r
3129         \r
3130             /**\r
3131              * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.\r
3132              * @param {Function} fn        The method the event invokes\r
3133              * @param {Object}   scope    An object that becomes the scope of the handler\r
3134              * @param {boolean}  options\r
3135              */\r
3136             onTextResize : function(fn, scope, options){\r
3137                 if(!textEvent){\r
3138                     textEvent = new Ext.util.Event();\r
3139                     var textEl = new Ext.Element(document.createElement('div'));\r
3140                     textEl.dom.className = 'x-text-resize';\r
3141                     textEl.dom.innerHTML = 'X';\r
3142                     textEl.appendTo(document.body);\r
3143                     textSize = textEl.dom.offsetHeight;\r
3144                     setInterval(function(){\r
3145                         if(textEl.dom.offsetHeight != textSize){\r
3146                             textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);\r
3147                         }\r
3148                     }, this.textResizeInterval);\r
3149                 }\r
3150                 textEvent.addListener(fn, scope, options);\r
3151             },\r
3152         \r
3153             /**\r
3154              * Removes the passed window resize listener.\r
3155              * @param {Function} fn        The method the event invokes\r
3156              * @param {Object}   scope    The scope of handler\r
3157              */\r
3158             removeResizeListener : function(fn, scope){\r
3159                 if(resizeEvent){\r
3160                     resizeEvent.removeListener(fn, scope);\r
3161                 }\r
3162             },\r
3163         \r
3164             // private\r
3165             fireResize : function(){\r
3166                 if(resizeEvent){\r
3167                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
3168                 }\r
3169             },\r
3170             \r
3171              /**\r
3172              * The frequency, in milliseconds, to check for text resize events (defaults to 50)\r
3173              */\r
3174             textResizeInterval : 50,\r
3175             \r
3176             /**\r
3177          * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)\r
3178          */\r
3179         ieDeferSrc : false,\r
3180         \r
3181         // protected for use inside the framework\r
3182         // detects whether we should use keydown or keypress based on the browser.\r
3183         useKeydown: useKeydown\r
3184     };\r
3185 }());\r
3186 \r
3187 Ext.EventManager.on = Ext.EventManager.addListener;\r
3188 \r
3189 \r
3190 Ext.apply(Ext.EventObjectImpl.prototype, {\r
3191     /** Key constant @type Number */\r
3192     BACKSPACE: 8,\r
3193     /** Key constant @type Number */\r
3194     TAB: 9,\r
3195     /** Key constant @type Number */\r
3196     NUM_CENTER: 12,\r
3197     /** Key constant @type Number */\r
3198     ENTER: 13,\r
3199     /** Key constant @type Number */\r
3200     RETURN: 13,\r
3201     /** Key constant @type Number */\r
3202     SHIFT: 16,\r
3203     /** Key constant @type Number */\r
3204     CTRL: 17,\r
3205     CONTROL : 17, // legacy\r
3206     /** Key constant @type Number */\r
3207     ALT: 18,\r
3208     /** Key constant @type Number */\r
3209     PAUSE: 19,\r
3210     /** Key constant @type Number */\r
3211     CAPS_LOCK: 20,\r
3212     /** Key constant @type Number */\r
3213     ESC: 27,\r
3214     /** Key constant @type Number */\r
3215     SPACE: 32,\r
3216     /** Key constant @type Number */\r
3217     PAGE_UP: 33,\r
3218     PAGEUP : 33, // legacy\r
3219     /** Key constant @type Number */\r
3220     PAGE_DOWN: 34,\r
3221     PAGEDOWN : 34, // legacy\r
3222     /** Key constant @type Number */\r
3223     END: 35,\r
3224     /** Key constant @type Number */\r
3225     HOME: 36,\r
3226     /** Key constant @type Number */\r
3227     LEFT: 37,\r
3228     /** Key constant @type Number */\r
3229     UP: 38,\r
3230     /** Key constant @type Number */\r
3231     RIGHT: 39,\r
3232     /** Key constant @type Number */\r
3233     DOWN: 40,\r
3234     /** Key constant @type Number */\r
3235     PRINT_SCREEN: 44,\r
3236     /** Key constant @type Number */\r
3237     INSERT: 45,\r
3238     /** Key constant @type Number */\r
3239     DELETE: 46,\r
3240     /** Key constant @type Number */\r
3241     ZERO: 48,\r
3242     /** Key constant @type Number */\r
3243     ONE: 49,\r
3244     /** Key constant @type Number */\r
3245     TWO: 50,\r
3246     /** Key constant @type Number */\r
3247     THREE: 51,\r
3248     /** Key constant @type Number */\r
3249     FOUR: 52,\r
3250     /** Key constant @type Number */\r
3251     FIVE: 53,\r
3252     /** Key constant @type Number */\r
3253     SIX: 54,\r
3254     /** Key constant @type Number */\r
3255     SEVEN: 55,\r
3256     /** Key constant @type Number */\r
3257     EIGHT: 56,\r
3258     /** Key constant @type Number */\r
3259     NINE: 57,\r
3260     /** Key constant @type Number */\r
3261     A: 65,\r
3262     /** Key constant @type Number */\r
3263     B: 66,\r
3264     /** Key constant @type Number */\r
3265     C: 67,\r
3266     /** Key constant @type Number */\r
3267     D: 68,\r
3268     /** Key constant @type Number */\r
3269     E: 69,\r
3270     /** Key constant @type Number */\r
3271     F: 70,\r
3272     /** Key constant @type Number */\r
3273     G: 71,\r
3274     /** Key constant @type Number */\r
3275     H: 72,\r
3276     /** Key constant @type Number */\r
3277     I: 73,\r
3278     /** Key constant @type Number */\r
3279     J: 74,\r
3280     /** Key constant @type Number */\r
3281     K: 75,\r
3282     /** Key constant @type Number */\r
3283     L: 76,\r
3284     /** Key constant @type Number */\r
3285     M: 77,\r
3286     /** Key constant @type Number */\r
3287     N: 78,\r
3288     /** Key constant @type Number */\r
3289     O: 79,\r
3290     /** Key constant @type Number */\r
3291     P: 80,\r
3292     /** Key constant @type Number */\r
3293     Q: 81,\r
3294     /** Key constant @type Number */\r
3295     R: 82,\r
3296     /** Key constant @type Number */\r
3297     S: 83,\r
3298     /** Key constant @type Number */\r
3299     T: 84,\r
3300     /** Key constant @type Number */\r
3301     U: 85,\r
3302     /** Key constant @type Number */\r
3303     V: 86,\r
3304     /** Key constant @type Number */\r
3305     W: 87,\r
3306     /** Key constant @type Number */\r
3307     X: 88,\r
3308     /** Key constant @type Number */\r
3309     Y: 89,\r
3310     /** Key constant @type Number */\r
3311     Z: 90,\r
3312     /** Key constant @type Number */\r
3313     CONTEXT_MENU: 93,\r
3314     /** Key constant @type Number */\r
3315     NUM_ZERO: 96,\r
3316     /** Key constant @type Number */\r
3317     NUM_ONE: 97,\r
3318     /** Key constant @type Number */\r
3319     NUM_TWO: 98,\r
3320     /** Key constant @type Number */\r
3321     NUM_THREE: 99,\r
3322     /** Key constant @type Number */\r
3323     NUM_FOUR: 100,\r
3324     /** Key constant @type Number */\r
3325     NUM_FIVE: 101,\r
3326     /** Key constant @type Number */\r
3327     NUM_SIX: 102,\r
3328     /** Key constant @type Number */\r
3329     NUM_SEVEN: 103,\r
3330     /** Key constant @type Number */\r
3331     NUM_EIGHT: 104,\r
3332     /** Key constant @type Number */\r
3333     NUM_NINE: 105,\r
3334     /** Key constant @type Number */\r
3335     NUM_MULTIPLY: 106,\r
3336     /** Key constant @type Number */\r
3337     NUM_PLUS: 107,\r
3338     /** Key constant @type Number */\r
3339     NUM_MINUS: 109,\r
3340     /** Key constant @type Number */\r
3341     NUM_PERIOD: 110,\r
3342     /** Key constant @type Number */\r
3343     NUM_DIVISION: 111,\r
3344     /** Key constant @type Number */\r
3345     F1: 112,\r
3346     /** Key constant @type Number */\r
3347     F2: 113,\r
3348     /** Key constant @type Number */\r
3349     F3: 114,\r
3350     /** Key constant @type Number */\r
3351     F4: 115,\r
3352     /** Key constant @type Number */\r
3353     F5: 116,\r
3354     /** Key constant @type Number */\r
3355     F6: 117,\r
3356     /** Key constant @type Number */\r
3357     F7: 118,\r
3358     /** Key constant @type Number */\r
3359     F8: 119,\r
3360     /** Key constant @type Number */\r
3361     F9: 120,\r
3362     /** Key constant @type Number */\r
3363     F10: 121,\r
3364     /** Key constant @type Number */\r
3365     F11: 122,\r
3366     /** Key constant @type Number */\r
3367     F12: 123,   \r
3368     \r
3369     /** @private */\r
3370     isNavKeyPress : function(){\r
3371         var me = this,\r
3372                 k = this.normalizeKey(me.keyCode);              \r
3373         return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down\r
3374                 k == me.RETURN ||\r
3375                 k == me.TAB ||\r
3376                 k == me.ESC;\r
3377     },\r
3378 \r
3379     isSpecialKey : function(){\r
3380         var k = this.normalizeKey(this.keyCode);\r
3381         return (this.type == 'keypress' && this.ctrlKey) ||\r
3382                 this.isNavKeyPress() ||\r
3383         (k == this.BACKSPACE) || // Backspace\r
3384                 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock\r
3385                 (k >= 44 && k <= 45);   // Print Screen, Insert\r
3386     },\r
3387         \r
3388         getPoint : function(){\r
3389             return new Ext.lib.Point(this.xy[0], this.xy[1]);\r
3390         },\r
3391 \r
3392     /**\r
3393      * Returns true if the control, meta, shift or alt key was pressed during this event.\r
3394      * @return {Boolean}\r
3395      */\r
3396     hasModifier : function(){\r
3397         return ((this.ctrlKey || this.altKey) || this.shiftKey);\r
3398     }\r
3399 });/**\r
3400  * @class Ext.Element\r
3401  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>\r
3402  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>\r
3403  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To\r
3404  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older\r
3405  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>\r
3406  * Usage:<br>\r
3407 <pre><code>\r
3408 // by id\r
3409 var el = Ext.get("my-div");\r
3410 \r
3411 // by DOM element reference\r
3412 var el = Ext.get(myDivElement);\r
3413 </code></pre>\r
3414  * <b>Animations</b><br />\r
3415  * <p>When an element is manipulated, by default there is no animation.</p>\r
3416  * <pre><code>\r
3417 var el = Ext.get("my-div");\r
3418 \r
3419 // no animation\r
3420 el.setWidth(100);\r
3421  * </code></pre>\r
3422  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This\r
3423  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>\r
3424  * <pre><code>\r
3425 // default animation\r
3426 el.setWidth(100, true);\r
3427  * </code></pre>\r
3428  * \r
3429  * <p>To configure the effects, an object literal with animation options to use as the Element animation\r
3430  * configuration object can also be specified. Note that the supported Element animation configuration\r
3431  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported\r
3432  * Element animation configuration options are:</p>\r
3433 <pre>\r
3434 Option    Default   Description\r
3435 --------- --------  ---------------------------------------------\r
3436 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds\r
3437 {@link Ext.Fx#easing easing}    easeOut   The easing method\r
3438 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes\r
3439 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function\r
3440 </pre>\r
3441  * \r
3442  * <pre><code>\r
3443 // Element animation options object\r
3444 var opt = {\r
3445     {@link Ext.Fx#duration duration}: 1,\r
3446     {@link Ext.Fx#easing easing}: 'elasticIn',\r
3447     {@link Ext.Fx#callback callback}: this.foo,\r
3448     {@link Ext.Fx#scope scope}: this\r
3449 };\r
3450 // animation with some options set\r
3451 el.setWidth(100, opt);\r
3452  * </code></pre>\r
3453  * <p>The Element animation object being used for the animation will be set on the options\r
3454  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>\r
3455  * <pre><code>\r
3456 // using the "anim" property to get the Anim object\r
3457 if(opt.anim.isAnimated()){\r
3458     opt.anim.stop();\r
3459 }\r
3460  * </code></pre>\r
3461  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>\r
3462  * <p><b> Composite (Collections of) Elements</b></p>\r
3463  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>\r
3464  * @constructor Create a new Element directly.\r
3465  * @param {String/HTMLElement} element\r
3466  * @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
3467  */\r
3468 (function(){\r
3469 var DOC = document;\r
3470 \r
3471 Ext.Element = function(element, forceNew){\r
3472     var dom = typeof element == "string" ?\r
3473               DOC.getElementById(element) : element,\r
3474         id;\r
3475 \r
3476     if(!dom) return null;\r
3477 \r
3478     id = dom.id;\r
3479 \r
3480     if(!forceNew && id && Ext.Element.cache[id]){ // element object already exists\r
3481         return Ext.Element.cache[id];\r
3482     }\r
3483 \r
3484     /**\r
3485      * The DOM element\r
3486      * @type HTMLElement\r
3487      */\r
3488     this.dom = dom;\r
3489 \r
3490     /**\r
3491      * The DOM element ID\r
3492      * @type String\r
3493      */\r
3494     this.id = id || Ext.id(dom);\r
3495 };\r
3496 \r
3497 var D = Ext.lib.Dom,\r
3498     DH = Ext.DomHelper,\r
3499     E = Ext.lib.Event,\r
3500     A = Ext.lib.Anim,\r
3501     El = Ext.Element;\r
3502 \r
3503 El.prototype = {\r
3504     /**\r
3505      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)\r
3506      * @param {Object} o The object with the attributes\r
3507      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.\r
3508      * @return {Ext.Element} this\r
3509      */\r
3510     set : function(o, useSet){\r
3511         var el = this.dom,\r
3512             attr,\r
3513             val;        \r
3514        \r
3515         for(attr in o){\r
3516             val = o[attr];\r
3517             if (attr != "style" && !Ext.isFunction(val)) {\r
3518                 if (attr == "cls" ) {\r
3519                     el.className = val;\r
3520                 } else if (o.hasOwnProperty(attr)) {\r
3521                     if (useSet || !!el.setAttribute) el.setAttribute(attr, val);\r
3522                     else el[attr] = val;\r
3523                 }\r
3524             }\r
3525         }\r
3526         if(o.style){\r
3527             DH.applyStyles(el, o.style);\r
3528         }\r
3529         return this;\r
3530     },\r
3531     \r
3532 //  Mouse events\r
3533     /**\r
3534      * @event click\r
3535      * Fires when a mouse click is detected within the element.\r
3536      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3537      * @param {HtmlElement} t The target of the event.\r
3538      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3539      */\r
3540     /**\r
3541      * @event contextmenu\r
3542      * Fires when a right click is detected within the element.\r
3543      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3544      * @param {HtmlElement} t The target of the event.\r
3545      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3546      */\r
3547     /**\r
3548      * @event dblclick\r
3549      * Fires when a mouse double click is detected within the element.\r
3550      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3551      * @param {HtmlElement} t The target of the event.\r
3552      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3553      */\r
3554     /**\r
3555      * @event mousedown\r
3556      * Fires when a mousedown is detected within the element.\r
3557      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3558      * @param {HtmlElement} t The target of the event.\r
3559      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3560      */\r
3561     /**\r
3562      * @event mouseup\r
3563      * Fires when a mouseup is detected within the element.\r
3564      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3565      * @param {HtmlElement} t The target of the event.\r
3566      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3567      */\r
3568     /**\r
3569      * @event mouseover\r
3570      * Fires when a mouseover is detected within the element.\r
3571      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3572      * @param {HtmlElement} t The target of the event.\r
3573      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3574      */\r
3575     /**\r
3576      * @event mousemove\r
3577      * Fires when a mousemove is detected with the element.\r
3578      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3579      * @param {HtmlElement} t The target of the event.\r
3580      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3581      */\r
3582     /**\r
3583      * @event mouseout\r
3584      * Fires when a mouseout is detected with the element.\r
3585      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3586      * @param {HtmlElement} t The target of the event.\r
3587      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3588      */\r
3589     /**\r
3590      * @event mouseenter\r
3591      * Fires when the mouse enters the element.\r
3592      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3593      * @param {HtmlElement} t The target of the event.\r
3594      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3595      */\r
3596     /**\r
3597      * @event mouseleave\r
3598      * Fires when the mouse leaves the element.\r
3599      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3600      * @param {HtmlElement} t The target of the event.\r
3601      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3602      */\r
3603     \r
3604 //  Keyboard events\r
3605     /**\r
3606      * @event keypress\r
3607      * Fires when a keypress is detected within the element.\r
3608      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3609      * @param {HtmlElement} t The target of the event.\r
3610      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3611      */\r
3612     /**\r
3613      * @event keydown\r
3614      * Fires when a keydown is detected within the element.\r
3615      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3616      * @param {HtmlElement} t The target of the event.\r
3617      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3618      */\r
3619     /**\r
3620      * @event keyup\r
3621      * Fires when a keyup is detected within the element.\r
3622      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3623      * @param {HtmlElement} t The target of the event.\r
3624      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3625      */\r
3626 \r
3627 \r
3628 //  HTML frame/object events\r
3629     /**\r
3630      * @event load\r
3631      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.\r
3632      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3633      * @param {HtmlElement} t The target of the event.\r
3634      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3635      */\r
3636     /**\r
3637      * @event unload\r
3638      * 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
3639      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3640      * @param {HtmlElement} t The target of the event.\r
3641      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3642      */\r
3643     /**\r
3644      * @event abort\r
3645      * Fires when an object/image is stopped from loading before completely loaded.\r
3646      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3647      * @param {HtmlElement} t The target of the event.\r
3648      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3649      */\r
3650     /**\r
3651      * @event error\r
3652      * Fires when an object/image/frame cannot be loaded properly.\r
3653      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3654      * @param {HtmlElement} t The target of the event.\r
3655      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3656      */\r
3657     /**\r
3658      * @event resize\r
3659      * Fires when a document view is resized.\r
3660      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3661      * @param {HtmlElement} t The target of the event.\r
3662      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3663      */\r
3664     /**\r
3665      * @event scroll\r
3666      * Fires when a document view is scrolled.\r
3667      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3668      * @param {HtmlElement} t The target of the event.\r
3669      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3670      */\r
3671 \r
3672 //  Form events\r
3673     /**\r
3674      * @event select\r
3675      * Fires when a user selects some text in a text field, including input and textarea.\r
3676      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3677      * @param {HtmlElement} t The target of the event.\r
3678      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3679      */\r
3680     /**\r
3681      * @event change\r
3682      * Fires when a control loses the input focus and its value has been modified since gaining focus.\r
3683      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3684      * @param {HtmlElement} t The target of the event.\r
3685      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3686      */\r
3687     /**\r
3688      * @event submit\r
3689      * Fires when a form is submitted.\r
3690      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3691      * @param {HtmlElement} t The target of the event.\r
3692      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3693      */\r
3694     /**\r
3695      * @event reset\r
3696      * Fires when a form is reset.\r
3697      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3698      * @param {HtmlElement} t The target of the event.\r
3699      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3700      */\r
3701     /**\r
3702      * @event focus\r
3703      * Fires when an element receives focus either via the pointing device or by tab navigation.\r
3704      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3705      * @param {HtmlElement} t The target of the event.\r
3706      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3707      */\r
3708     /**\r
3709      * @event blur\r
3710      * Fires when an element loses focus either via the pointing device or by tabbing navigation.\r
3711      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3712      * @param {HtmlElement} t The target of the event.\r
3713      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3714      */\r
3715 \r
3716 //  User Interface events\r
3717     /**\r
3718      * @event DOMFocusIn\r
3719      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.\r
3720      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3721      * @param {HtmlElement} t The target of the event.\r
3722      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3723      */\r
3724     /**\r
3725      * @event DOMFocusOut\r
3726      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.\r
3727      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3728      * @param {HtmlElement} t The target of the event.\r
3729      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3730      */\r
3731     /**\r
3732      * @event DOMActivate\r
3733      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.\r
3734      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3735      * @param {HtmlElement} t The target of the event.\r
3736      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3737      */\r
3738 \r
3739 //  DOM Mutation events\r
3740     /**\r
3741      * @event DOMSubtreeModified\r
3742      * Where supported. Fires when the subtree is modified.\r
3743      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3744      * @param {HtmlElement} t The target of the event.\r
3745      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3746      */\r
3747     /**\r
3748      * @event DOMNodeInserted\r
3749      * Where supported. Fires when a node has been added as a child of another node.\r
3750      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3751      * @param {HtmlElement} t The target of the event.\r
3752      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3753      */\r
3754     /**\r
3755      * @event DOMNodeRemoved\r
3756      * Where supported. Fires when a descendant node of the element is removed.\r
3757      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3758      * @param {HtmlElement} t The target of the event.\r
3759      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3760      */\r
3761     /**\r
3762      * @event DOMNodeRemovedFromDocument\r
3763      * Where supported. Fires when a node is being removed from a document.\r
3764      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3765      * @param {HtmlElement} t The target of the event.\r
3766      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3767      */\r
3768     /**\r
3769      * @event DOMNodeInsertedIntoDocument\r
3770      * Where supported. Fires when a node is being inserted into a document.\r
3771      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3772      * @param {HtmlElement} t The target of the event.\r
3773      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3774      */\r
3775     /**\r
3776      * @event DOMAttrModified\r
3777      * Where supported. Fires when an attribute has been modified.\r
3778      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3779      * @param {HtmlElement} t The target of the event.\r
3780      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3781      */\r
3782     /**\r
3783      * @event DOMCharacterDataModified\r
3784      * Where supported. Fires when the character data has been modified.\r
3785      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.\r
3786      * @param {HtmlElement} t The target of the event.\r
3787      * @param {Object} o The options configuration passed to the {@link #addListener} call.\r
3788      */\r
3789 \r
3790     /**\r
3791      * The default unit to append to CSS values where a unit isn't provided (defaults to px).\r
3792      * @type String\r
3793      */\r
3794     defaultUnit : "px",\r
3795 \r
3796     /**\r
3797      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)\r
3798      * @param {String} selector The simple selector to test\r
3799      * @return {Boolean} True if this element matches the selector, else false\r
3800      */\r
3801     is : function(simpleSelector){\r
3802         return Ext.DomQuery.is(this.dom, simpleSelector);\r
3803     },\r
3804 \r
3805     /**\r
3806      * Tries to focus the element. Any exceptions are caught and ignored.\r
3807      * @param {Number} defer (optional) Milliseconds to defer the focus\r
3808      * @return {Ext.Element} this\r
3809      */\r
3810     focus : function(defer, /* private */ dom) {\r
3811         var me = this,\r
3812             dom = dom || me.dom;\r
3813         try{\r
3814             if(Number(defer)){\r
3815                 me.focus.defer(defer, null, [null, dom]);\r
3816             }else{\r
3817                 dom.focus();\r
3818             }\r
3819         }catch(e){}\r
3820         return me;\r
3821     },\r
3822 \r
3823     /**\r
3824      * Tries to blur the element. Any exceptions are caught and ignored.\r
3825      * @return {Ext.Element} this\r
3826      */\r
3827     blur : function() {\r
3828         try{\r
3829             this.dom.blur();\r
3830         }catch(e){}\r
3831         return this;\r
3832     },\r
3833 \r
3834     /**\r
3835      * Returns the value of the "value" attribute\r
3836      * @param {Boolean} asNumber true to parse the value as a number\r
3837      * @return {String/Number}\r
3838      */\r
3839     getValue : function(asNumber){\r
3840         var val = this.dom.value;\r
3841         return asNumber ? parseInt(val, 10) : val;\r
3842     },\r
3843 \r
3844     /**\r
3845      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.\r
3846      * @param {String} eventName The name of event to handle.\r
3847      * @param {Function} fn The handler function the event invokes. This function is passed\r
3848      * the following parameters:<ul>\r
3849      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
3850      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.\r
3851      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
3852      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
3853      * </ul>\r
3854      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.\r
3855      * <b>If omitted, defaults to this Element.</b>.\r
3856      * @param {Object} options (optional) An object containing handler configuration properties.\r
3857      * This may contain any of the following properties:<ul>\r
3858      * <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
3859      * <b>If omitted, defaults to this Element.</b></div></li>\r
3860      * <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
3861      * <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
3862      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>\r
3863      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>\r
3864      * <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
3865      * <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
3866      * <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
3867      * <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
3868      * <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
3869      * by the specified number of milliseconds. If the event fires again within that time, the original\r
3870      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>\r
3871      * </ul><br>\r
3872      * <p>\r
3873      * <b>Combining Options</b><br>\r
3874      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose\r
3875      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different\r
3876      * types of listeners:<br>\r
3877      * <br>\r
3878      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the\r
3879      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">\r
3880      * Code:<pre><code>\r
3881 el.on('click', this.onClick, this, {\r
3882     single: true,\r
3883     delay: 100,\r
3884     stopEvent : true,\r
3885     forumId: 4\r
3886 });</code></pre></p>\r
3887      * <p>\r
3888      * <b>Attaching multiple handlers in 1 call</b><br>\r
3889      * The method also allows for a single argument to be passed which is a config object containing properties\r
3890      * which specify multiple handlers.</p>\r
3891      * <p>\r
3892      * Code:<pre><code>\r
3893 el.on({\r
3894     'click' : {\r
3895         fn: this.onClick,\r
3896         scope: this,\r
3897         delay: 100\r
3898     },\r
3899     'mouseover' : {\r
3900         fn: this.onMouseOver,\r
3901         scope: this\r
3902     },\r
3903     'mouseout' : {\r
3904         fn: this.onMouseOut,\r
3905         scope: this\r
3906     }\r
3907 });</code></pre>\r
3908      * <p>\r
3909      * Or a shorthand syntax:<br>\r
3910      * Code:<pre><code></p>\r
3911 el.on({\r
3912     'click' : this.onClick,\r
3913     'mouseover' : this.onMouseOver,\r
3914     'mouseout' : this.onMouseOut,\r
3915     scope: this\r
3916 });\r
3917      * </code></pre></p>\r
3918      * <p><b>delegate</b></p>\r
3919      * <p>This is a configuration option that you can pass along when registering a handler for\r
3920      * an event to assist with event delegation. Event delegation is a technique that is used to\r
3921      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event\r
3922      * for a container element as opposed to each element within a container. By setting this\r
3923      * configuration option to a simple selector, the target element will be filtered to look for\r
3924      * a descendant of the target.\r
3925      * For example:<pre><code>\r
3926 // using this markup:\r
3927 &lt;div id='elId'>\r
3928     &lt;p id='p1'>paragraph one&lt;/p>\r
3929     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>\r
3930     &lt;p id='p3'>paragraph three&lt;/p>\r
3931 &lt;/div>\r
3932 // utilize event delegation to registering just one handler on the container element: \r
3933 el = Ext.get('elId');\r
3934 el.on(\r
3935     'click',\r
3936     function(e,t) {\r
3937         // handle click\r
3938         console.info(t.id); // 'p2'\r
3939     },\r
3940     this,\r
3941     {\r
3942         // filter the target element to be a descendant with the class 'clickable'\r
3943         delegate: '.clickable' \r
3944     }\r
3945 );\r
3946      * </code></pre></p>\r
3947      * @return {Ext.Element} this\r
3948      */\r
3949     addListener : function(eventName, fn, scope, options){\r
3950         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);\r
3951         return this;\r
3952     },\r
3953 \r
3954     /**\r
3955      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.\r
3956      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the\r
3957      * listener, the same scope must be specified here.\r
3958      * Example:\r
3959      * <pre><code>\r
3960 el.removeListener('click', this.handlerFn);\r
3961 // or\r
3962 el.un('click', this.handlerFn);\r
3963 </code></pre>\r
3964      * @param {String} eventName The name of the event from which to remove the handler.\r
3965      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>\r
3966      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
3967      * then this must refer to the same object.\r
3968      * @return {Ext.Element} this\r
3969      */\r
3970     removeListener : function(eventName, fn, scope){\r
3971         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);\r
3972         return this;\r
3973     },\r
3974 \r
3975     /**\r
3976      * Removes all previous added listeners from this element\r
3977      * @return {Ext.Element} this\r
3978      */\r
3979     removeAllListeners : function(){\r
3980         Ext.EventManager.removeAll(this.dom);\r
3981         return this;\r
3982     },\r
3983 \r
3984     /**\r
3985      * @private Test if size has a unit, otherwise appends the default\r
3986      */\r
3987     addUnits : function(size){\r
3988         if(size === "" || size == "auto" || size === undefined){\r
3989             size = size || '';\r
3990         } else if(!isNaN(size) || !unitPattern.test(size)){\r
3991             size = size + (this.defaultUnit || 'px');\r
3992         }\r
3993         return size;\r
3994     },\r
3995 \r
3996     /**\r
3997      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element\r
3998      * 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
3999      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>\r
4000      * @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
4001      * exactly how to request the HTML.\r
4002      * @return {Ext.Element} this\r
4003      */\r
4004     load : function(url, params, cb){\r
4005         Ext.Ajax.request(Ext.apply({\r
4006             params: params,\r
4007             url: url.url || url,\r
4008             callback: cb,\r
4009             el: this.dom,\r
4010             indicatorText: url.indicatorText || ''\r
4011         }, Ext.isObject(url) ? url : {}));\r
4012         return this;\r
4013     },\r
4014 \r
4015     /**\r
4016      * Tests various css rules/browsers to determine if this element uses a border box\r
4017      * @return {Boolean}\r
4018      */\r
4019     isBorderBox : function(){\r
4020         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;\r
4021     },\r
4022 \r
4023     /**\r
4024      * Removes this element from the DOM and deletes it from the cache\r
4025      */\r
4026     remove : function(){\r
4027         var me = this,\r
4028             dom = me.dom;\r
4029         \r
4030         me.removeAllListeners();\r
4031         if (dom) {\r
4032             delete me.dom;\r
4033             delete El.cache[dom.id];\r
4034             delete El.dataCache[dom.id];\r
4035             Ext.removeNode(dom);\r
4036         }\r
4037     },\r
4038 \r
4039     /**\r
4040      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.\r
4041      * @param {Function} overFn The function to call when the mouse enters the Element.\r
4042      * @param {Function} outFn The function to call when the mouse leaves the Element.\r
4043      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.\r
4044      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.\r
4045      * @return {Ext.Element} this\r
4046      */\r
4047     hover : function(overFn, outFn, scope, options){\r
4048         var me = this;\r
4049         me.on('mouseenter', overFn, scope || me.dom, options);\r
4050         me.on('mouseleave', outFn, scope || me.dom, options);\r
4051         return me;\r
4052     },\r
4053 \r
4054     /**\r
4055      * Returns true if this element is an ancestor of the passed element\r
4056      * @param {HTMLElement/String} el The element to check\r
4057      * @return {Boolean} True if this element is an ancestor of el, else false\r
4058      */\r
4059     contains : function(el){\r
4060         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);\r
4061     },\r
4062 \r
4063     /**\r
4064      * Returns the value of a namespaced attribute from the element's underlying DOM node.\r
4065      * @param {String} namespace The namespace in which to look for the attribute\r
4066      * @param {String} name The attribute name\r
4067      * @return {String} The attribute value\r
4068      * @deprecated\r
4069      */\r
4070     getAttributeNS : function(ns, name){\r
4071         return this.getAttribute(name, ns); \r
4072     },\r
4073     \r
4074     /**\r
4075      * Returns the value of an attribute from the element's underlying DOM node.\r
4076      * @param {String} name The attribute name\r
4077      * @param {String} namespace (optional) The namespace in which to look for the attribute\r
4078      * @return {String} The attribute value\r
4079      */\r
4080     getAttribute : Ext.isIE ? function(name, ns){\r
4081         var d = this.dom,\r
4082             type = typeof d[ns + ":" + name];\r
4083 \r
4084         if(['undefined', 'unknown'].indexOf(type) == -1){\r
4085             return d[ns + ":" + name];\r
4086         }\r
4087         return d[name];\r
4088     } : function(name, ns){\r
4089         var d = this.dom;\r
4090         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];\r
4091     },\r
4092     \r
4093     /**\r
4094     * Update the innerHTML of this element\r
4095     * @param {String} html The new HTML\r
4096     * @return {Ext.Element} this\r
4097      */\r
4098     update : function(html) {\r
4099         if (this.dom) {\r
4100             this.dom.innerHTML = html;\r
4101         }\r
4102         return this;\r
4103     }\r
4104 };\r
4105 \r
4106 var ep = El.prototype;\r
4107 \r
4108 El.addMethods = function(o){\r
4109    Ext.apply(ep, o);\r
4110 };\r
4111 \r
4112 /**\r
4113  * Appends an event handler (shorthand for {@link #addListener}).\r
4114  * @param {String} eventName The name of event to handle.\r
4115  * @param {Function} fn The handler function the event invokes.\r
4116  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.\r
4117  * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
4118  * @member Ext.Element\r
4119  * @method on\r
4120  */\r
4121 ep.on = ep.addListener;\r
4122 \r
4123 /**\r
4124  * Removes an event handler from this element (see {@link #removeListener} for additional notes).\r
4125  * @param {String} eventName The name of the event from which to remove the handler.\r
4126  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>\r
4127  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
4128  * then this must refer to the same object.\r
4129  * @return {Ext.Element} this\r
4130  * @member Ext.Element\r
4131  * @method un\r
4132  */\r
4133 ep.un = ep.removeListener;\r
4134 \r
4135 /**\r
4136  * true to automatically adjust width and height settings for box-model issues (default to true)\r
4137  */\r
4138 ep.autoBoxAdjust = true;\r
4139 \r
4140 // private\r
4141 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,\r
4142     docEl;\r
4143 \r
4144 /**\r
4145  * @private\r
4146  */\r
4147 El.cache = {};\r
4148 El.dataCache = {};\r
4149 \r
4150 /**\r
4151  * Retrieves Ext.Element objects.\r
4152  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method\r
4153  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by\r
4154  * its ID, use {@link Ext.ComponentMgr#get}.</p>\r
4155  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an\r
4156  * object was recreated with the same id via AJAX or DOM.</p>\r
4157  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
4158  * @return {Element} The Element object (or null if no matching element was found)\r
4159  * @static\r
4160  * @member Ext.Element\r
4161  * @method get\r
4162  */\r
4163 El.get = function(el){\r
4164     var ex,\r
4165         elm,\r
4166         id;\r
4167     if(!el){ return null; }\r
4168     if (typeof el == "string") { // element id\r
4169         if (!(elm = DOC.getElementById(el))) {\r
4170             return null;\r
4171         }\r
4172         if (ex = El.cache[el]) {\r
4173             ex.dom = elm;\r
4174         } else {\r
4175             ex = El.cache[el] = new El(elm);\r
4176         }\r
4177         return ex;\r
4178     } else if (el.tagName) { // dom element\r
4179         if(!(id = el.id)){\r
4180             id = Ext.id(el);\r
4181         }\r
4182         if(ex = El.cache[id]){\r
4183             ex.dom = el;\r
4184         }else{\r
4185             ex = El.cache[id] = new El(el);\r
4186         }\r
4187         return ex;\r
4188     } else if (el instanceof El) {\r
4189         if(el != docEl){\r
4190             el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,\r
4191                                                           // catch case where it hasn't been appended\r
4192             El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it\r
4193         }\r
4194         return el;\r
4195     } else if(el.isComposite) {\r
4196         return el;\r
4197     } else if(Ext.isArray(el)) {\r
4198         return El.select(el);\r
4199     } else if(el == DOC) {\r
4200         // create a bogus element object representing the document object\r
4201         if(!docEl){\r
4202             var f = function(){};\r
4203             f.prototype = El.prototype;\r
4204             docEl = new f();\r
4205             docEl.dom = DOC;\r
4206         }\r
4207         return docEl;\r
4208     }\r
4209     return null;\r
4210 };\r
4211 \r
4212 // private method for getting and setting element data\r
4213 El.data = function(el, key, value){\r
4214     var c = El.dataCache[el.id];\r
4215     if(!c){\r
4216         c = El.dataCache[el.id] = {};\r
4217     }\r
4218     if(arguments.length == 2){\r
4219         return c[key];    \r
4220     }else{\r
4221         return (c[key] = value);\r
4222     }\r
4223 };\r
4224 \r
4225 // private\r
4226 // Garbage collection - uncache elements/purge listeners on orphaned elements\r
4227 // so we don't hold a reference and cause the browser to retain them\r
4228 function garbageCollect(){\r
4229     if(!Ext.enableGarbageCollector){\r
4230         clearInterval(El.collectorThread);\r
4231     } else {\r
4232         var eid,\r
4233             el,\r
4234             d;\r
4235 \r
4236         for(eid in El.cache){\r
4237             el = El.cache[eid];\r
4238             d = el.dom;\r
4239             // -------------------------------------------------------\r
4240             // Determining what is garbage:\r
4241             // -------------------------------------------------------\r
4242             // !d\r
4243             // dom node is null, definitely garbage\r
4244             // -------------------------------------------------------\r
4245             // !d.parentNode\r
4246             // no parentNode == direct orphan, definitely garbage\r
4247             // -------------------------------------------------------\r
4248             // !d.offsetParent && !document.getElementById(eid)\r
4249             // display none elements have no offsetParent so we will\r
4250             // also try to look it up by it's id. However, check\r
4251             // offsetParent first so we don't do unneeded lookups.\r
4252             // This enables collection of elements that are not orphans\r
4253             // directly, but somewhere up the line they have an orphan\r
4254             // parent.\r
4255             // -------------------------------------------------------\r
4256             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){\r
4257                 delete El.cache[eid];\r
4258                 if(d && Ext.enableListenerCollection){\r
4259                     Ext.EventManager.removeAll(d);\r
4260                 }\r
4261             }\r
4262         }\r
4263     }\r
4264 }\r
4265 El.collectorThreadId = setInterval(garbageCollect, 30000);\r
4266 \r
4267 var flyFn = function(){};\r
4268 flyFn.prototype = El.prototype;\r
4269 \r
4270 // dom is optional\r
4271 El.Flyweight = function(dom){\r
4272     this.dom = dom;\r
4273 };\r
4274 \r
4275 El.Flyweight.prototype = new flyFn();\r
4276 El.Flyweight.prototype.isFlyweight = true;\r
4277 El._flyweights = {};\r
4278 \r
4279 /**\r
4280  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
4281  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>\r
4282  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by\r
4283  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}\r
4284  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>\r
4285  * @param {String/HTMLElement} el The dom node or id\r
4286  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts\r
4287  * (e.g. internally Ext uses "_global")\r
4288  * @return {Element} The shared Element object (or null if no matching element was found)\r
4289  * @member Ext.Element\r
4290  * @method fly\r
4291  */\r
4292 El.fly = function(el, named){\r
4293     var ret = null;\r
4294     named = named || '_global';\r
4295 \r
4296     if (el = Ext.getDom(el)) {\r
4297         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;\r
4298         ret = El._flyweights[named];\r
4299     }\r
4300     return ret;\r
4301 };\r
4302 \r
4303 /**\r
4304  * Retrieves Ext.Element objects.\r
4305  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method\r
4306  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by\r
4307  * its ID, use {@link Ext.ComponentMgr#get}.</p>\r
4308  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an\r
4309  * object was recreated with the same id via AJAX or DOM.</p>\r
4310  * Shorthand of {@link Ext.Element#get}\r
4311  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.\r
4312  * @return {Element} The Element object (or null if no matching element was found)\r
4313  * @member Ext\r
4314  * @method get\r
4315  */\r
4316 Ext.get = El.get;\r
4317 \r
4318 /**\r
4319  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -\r
4320  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>\r
4321  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by\r
4322  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}\r
4323  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>\r
4324  * @param {String/HTMLElement} el The dom node or id\r
4325  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts\r
4326  * (e.g. internally Ext uses "_global")\r
4327  * @return {Element} The shared Element object (or null if no matching element was found)\r
4328  * @member Ext\r
4329  * @method fly\r
4330  */\r
4331 Ext.fly = El.fly;\r
4332 \r
4333 // speedy lookup for elements never to box adjust\r
4334 var noBoxAdjust = Ext.isStrict ? {\r
4335     select:1\r
4336 } : {\r
4337     input:1, select:1, textarea:1\r
4338 };\r
4339 if(Ext.isIE || Ext.isGecko){\r
4340     noBoxAdjust['button'] = 1;\r
4341 }\r
4342 \r
4343 \r
4344 Ext.EventManager.on(window, 'unload', function(){\r
4345     delete El.cache;\r
4346     delete El.dataCache;\r
4347     delete El._flyweights;\r
4348 });\r
4349 })();\r
4350 /**\r
4351  * @class Ext.Element\r
4352  */\r
4353 Ext.Element.addMethods({    \r
4354     /**\r
4355      * Stops the specified event(s) from bubbling and optionally prevents the default action\r
4356      * @param {String/Array} eventName an event / array of events to stop from bubbling\r
4357      * @param {Boolean} preventDefault (optional) true to prevent the default action too\r
4358      * @return {Ext.Element} this\r
4359      */\r
4360     swallowEvent : function(eventName, preventDefault){\r
4361             var me = this;\r
4362         function fn(e){\r
4363             e.stopPropagation();\r
4364             if(preventDefault){\r
4365                 e.preventDefault();\r
4366             }\r
4367         }\r
4368         if(Ext.isArray(eventName)){            \r
4369                 Ext.each(eventName, function(e) {\r
4370                  me.on(e, fn);\r
4371             });\r
4372             return me;\r
4373         }\r
4374         me.on(eventName, fn);\r
4375         return me;\r
4376     },\r
4377     \r
4378     /**\r
4379      * Create an event handler on this element such that when the event fires and is handled by this element,\r
4380      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).\r
4381      * @param {String} eventName The type of event to relay\r
4382      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context\r
4383      * for firing the relayed event\r
4384      */\r
4385     relayEvent : function(eventName, observable){\r
4386         this.on(eventName, function(e){\r
4387             observable.fireEvent(eventName, e);\r
4388         });\r
4389     },\r
4390     \r
4391         /**\r
4392      * Removes worthless text nodes\r
4393      * @param {Boolean} forceReclean (optional) By default the element\r
4394      * keeps track if it has been cleaned already so\r
4395      * you can call this over and over. However, if you update the element and\r
4396      * need to force a reclean, you can pass true.\r
4397      */\r
4398     clean : function(forceReclean){\r
4399         var me = this, \r
4400             dom = me.dom,\r
4401                 n = dom.firstChild, \r
4402                 ni = -1;\r
4403                 \r
4404             if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){\r
4405             return me;\r
4406         }      \r
4407                 \r
4408             while(n){\r
4409                 var nx = n.nextSibling;\r
4410             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){\r
4411                 dom.removeChild(n);\r
4412             }else{\r
4413                 n.nodeIndex = ++ni;\r
4414             }\r
4415                 n = nx;\r
4416             }\r
4417         Ext.Element.data(dom, 'isCleaned', true);\r
4418             return me;\r
4419         },\r
4420     \r
4421     /**\r
4422      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object\r
4423      * parameter as {@link Ext.Updater#update}\r
4424      * @return {Ext.Element} this\r
4425      */\r
4426     load : function(){\r
4427         var um = this.getUpdater();\r
4428         um.update.apply(um, arguments);\r
4429         return this;\r
4430     },\r
4431 \r
4432     /**\r
4433     * Gets this element's {@link Ext.Updater Updater}\r
4434     * @return {Ext.Updater} The Updater\r
4435     */\r
4436     getUpdater : function(){\r
4437         return this.updateManager || (this.updateManager = new Ext.Updater(this));\r
4438     },\r
4439     \r
4440         /**\r
4441     * Update the innerHTML of this element, optionally searching for and processing scripts\r
4442     * @param {String} html The new HTML\r
4443     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)\r
4444     * @param {Function} callback (optional) For async script loading you can be notified when the update completes\r
4445     * @return {Ext.Element} this\r
4446      */\r
4447     update : function(html, loadScripts, callback){\r
4448         html = html || "";\r
4449             \r
4450         if(loadScripts !== true){\r
4451             this.dom.innerHTML = html;\r
4452             if(Ext.isFunction(callback)){\r
4453                 callback();\r
4454             }\r
4455             return this;\r
4456         }\r
4457         \r
4458         var id = Ext.id(),\r
4459                 dom = this.dom;\r
4460 \r
4461         html += '<span id="' + id + '"></span>';\r
4462 \r
4463         Ext.lib.Event.onAvailable(id, function(){\r
4464             var DOC = document,\r
4465                 hd = DOC.getElementsByTagName("head")[0],\r
4466                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
4467                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,\r
4468                 typeRe = /\stype=([\'\"])(.*?)\1/i,\r
4469                 match,\r
4470                 attrs,\r
4471                 srcMatch,\r
4472                 typeMatch,\r
4473                 el,\r
4474                 s;\r
4475 \r
4476             while((match = re.exec(html))){\r
4477                 attrs = match[1];\r
4478                 srcMatch = attrs ? attrs.match(srcRe) : false;\r
4479                 if(srcMatch && srcMatch[2]){\r
4480                    s = DOC.createElement("script");\r
4481                    s.src = srcMatch[2];\r
4482                    typeMatch = attrs.match(typeRe);\r
4483                    if(typeMatch && typeMatch[2]){\r
4484                        s.type = typeMatch[2];\r
4485                    }\r
4486                    hd.appendChild(s);\r
4487                 }else if(match[2] && match[2].length > 0){\r
4488                     if(window.execScript) {\r
4489                        window.execScript(match[2]);\r
4490                     } else {\r
4491                        window.eval(match[2]);\r
4492                     }\r
4493                 }\r
4494             }\r
4495             el = DOC.getElementById(id);\r
4496             if(el){Ext.removeNode(el);}\r
4497             if(Ext.isFunction(callback)){\r
4498                 callback();\r
4499             }\r
4500         });\r
4501         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");\r
4502         return this;\r
4503     },\r
4504     \r
4505     /**\r
4506      * Creates a proxy element of this element\r
4507      * @param {String/Object} config The class name of the proxy element or a DomHelper config object\r
4508      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)\r
4509      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)\r
4510      * @return {Ext.Element} The new proxy element\r
4511      */\r
4512     createProxy : function(config, renderTo, matchBox){\r
4513         config = Ext.isObject(config) ? config : {tag : "div", cls: config};\r
4514 \r
4515         var me = this,\r
4516                 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :\r
4517                                                    Ext.DomHelper.insertBefore(me.dom, config, true);        \r
4518         \r
4519         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded\r
4520            proxy.setBox(me.getBox());\r
4521         }\r
4522         return proxy;\r
4523     }\r
4524 });\r
4525 \r
4526 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;\r
4527 \r
4528 // private\r
4529 Ext.Element.uncache = function(el){\r
4530     for(var i = 0, a = arguments, len = a.length; i < len; i++) {\r
4531         if(a[i]){\r
4532             delete Ext.Element.cache[a[i].id || a[i]];\r
4533         }\r
4534     }\r
4535 };/**\r
4536  * @class Ext.Element\r
4537  */\r
4538 Ext.Element.addMethods({\r
4539     /**\r
4540      * Gets the x,y coordinates specified by the anchor position on the element.\r
4541      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}\r
4542      * for details on supported anchor positions.\r
4543      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead\r
4544      * of page coordinates\r
4545      * @param {Object} size (optional) An object containing the size to use for calculating anchor position\r
4546      * {width: (target width), height: (target height)} (defaults to the element's current size)\r
4547      * @return {Array} [x, y] An array containing the element's x and y coordinates\r
4548      */\r
4549     getAnchorXY : function(anchor, local, s){\r
4550         //Passing a different size is useful for pre-calculating anchors,\r
4551         //especially for anchored animations that change the el size.\r
4552                 anchor = (anchor || "tl").toLowerCase();\r
4553         s = s || {};\r
4554         \r
4555         var me = this,        \r
4556                 vp = me.dom == document.body || me.dom == document,\r
4557                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),\r
4558                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              \r
4559                 xy,             \r
4560                 r = Math.round,\r
4561                 o = me.getXY(),\r
4562                 scroll = me.getScroll(),\r
4563                 extraX = vp ? scroll.left : !local ? o[0] : 0,\r
4564                 extraY = vp ? scroll.top : !local ? o[1] : 0,\r
4565                 hash = {\r
4566                         c  : [r(w * 0.5), r(h * 0.5)],\r
4567                         t  : [r(w * 0.5), 0],\r
4568                         l  : [0, r(h * 0.5)],\r
4569                         r  : [w, r(h * 0.5)],\r
4570                         b  : [r(w * 0.5), h],\r
4571                         tl : [0, 0],    \r
4572                         bl : [0, h],\r
4573                         br : [w, h],\r
4574                         tr : [w, 0]\r
4575                 };\r
4576         \r
4577         xy = hash[anchor];      \r
4578         return [xy[0] + extraX, xy[1] + extraY]; \r
4579     },\r
4580 \r
4581     /**\r
4582      * Anchors an element to another element and realigns it when the window is resized.\r
4583      * @param {Mixed} element The element to align to.\r
4584      * @param {String} position The position to align to.\r
4585      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4586      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object\r
4587      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter\r
4588      * is a number, it is used as the buffer delay (defaults to 50ms).\r
4589      * @param {Function} callback The function to call after the animation finishes\r
4590      * @return {Ext.Element} this\r
4591      */\r
4592     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        \r
4593             var me = this,\r
4594             dom = me.dom;\r
4595             \r
4596             function action(){\r
4597             Ext.fly(dom).alignTo(el, alignment, offsets, animate);\r
4598             Ext.callback(callback, Ext.fly(dom));\r
4599         }\r
4600         \r
4601         Ext.EventManager.onWindowResize(action, me);\r
4602         \r
4603         if(!Ext.isEmpty(monitorScroll)){\r
4604             Ext.EventManager.on(window, 'scroll', action, me,\r
4605                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});\r
4606         }\r
4607         action.call(me); // align immediately\r
4608         return me;\r
4609     },\r
4610 \r
4611     /**\r
4612      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the\r
4613      * supported position values.\r
4614      * @param {Mixed} element The element to align to.\r
4615      * @param {String} position The position to align to.\r
4616      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4617      * @return {Array} [x, y]\r
4618      */\r
4619     getAlignToXY : function(el, p, o){      \r
4620         el = Ext.get(el);\r
4621         \r
4622         if(!el || !el.dom){\r
4623             throw "Element.alignToXY with an element that doesn't exist";\r
4624         }\r
4625         \r
4626         o = o || [0,0];\r
4627         p = (p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       \r
4628                 \r
4629         var me = this,\r
4630                 d = me.dom,\r
4631                 a1,\r
4632                 a2,\r
4633                 x,\r
4634                 y,\r
4635                 //constrain the aligned el to viewport if necessary\r
4636                 w,\r
4637                 h,\r
4638                 r,\r
4639                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie\r
4640                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie\r
4641                 p1y,\r
4642                 p1x,            \r
4643                 p2y,\r
4644                 p2x,\r
4645                 swapY,\r
4646                 swapX,\r
4647                 doc = document,\r
4648                 docElement = doc.documentElement,\r
4649                 docBody = doc.body,\r
4650                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,\r
4651                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,\r
4652                 c = false, //constrain to viewport\r
4653                 p1 = "", \r
4654                 p2 = "",\r
4655                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
4656         \r
4657         if(!m){\r
4658            throw "Element.alignTo with an invalid alignment " + p;\r
4659         }\r
4660         \r
4661         p1 = m[1]; \r
4662         p2 = m[2]; \r
4663         c = !!m[3];\r
4664 \r
4665         //Subtract the aligned el's internal xy from the target's offset xy\r
4666         //plus custom offset to get the aligned el's new offset xy\r
4667         a1 = me.getAnchorXY(p1, true);\r
4668         a2 = el.getAnchorXY(p2, false);\r
4669 \r
4670         x = a2[0] - a1[0] + o[0];\r
4671         y = a2[1] - a1[1] + o[1];\r
4672 \r
4673         if(c){    \r
4674                w = me.getWidth();\r
4675            h = me.getHeight();\r
4676            r = el.getRegion();       \r
4677            //If we are at a viewport boundary and the aligned el is anchored on a target border that is\r
4678            //perpendicular to the vp border, allow the aligned el to slide on that border,\r
4679            //otherwise swap the aligned el to the opposite border of the target.\r
4680            p1y = p1.charAt(0);\r
4681            p1x = p1.charAt(p1.length-1);\r
4682            p2y = p2.charAt(0);\r
4683            p2x = p2.charAt(p2.length-1);\r
4684            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));\r
4685            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          \r
4686            \r
4687 \r
4688            if (x + w > dw + scrollX) {\r
4689                 x = swapX ? r.left-w : dw+scrollX-w;\r
4690            }\r
4691            if (x < scrollX) {\r
4692                x = swapX ? r.right : scrollX;\r
4693            }\r
4694            if (y + h > dh + scrollY) {\r
4695                 y = swapY ? r.top-h : dh+scrollY-h;\r
4696             }\r
4697            if (y < scrollY){\r
4698                y = swapY ? r.bottom : scrollY;\r
4699            }\r
4700         }\r
4701         return [x,y];\r
4702     },\r
4703 \r
4704     /**\r
4705      * Aligns this element with another element relative to the specified anchor points. If the other element is the\r
4706      * document it aligns it to the viewport.\r
4707      * The position parameter is optional, and can be specified in any one of the following formats:\r
4708      * <ul>\r
4709      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>\r
4710      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.\r
4711      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been\r
4712      *       deprecated in favor of the newer two anchor syntax below</i>.</li>\r
4713      *   <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
4714      *       element's anchor point, and the second value is used as the target's anchor point.</li>\r
4715      * </ul>\r
4716      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of\r
4717      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to\r
4718      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than\r
4719      * that specified in order to enforce the viewport constraints.\r
4720      * Following are all of the supported anchor positions:\r
4721 <pre>\r
4722 Value  Description\r
4723 -----  -----------------------------\r
4724 tl     The top left corner (default)\r
4725 t      The center of the top edge\r
4726 tr     The top right corner\r
4727 l      The center of the left edge\r
4728 c      In the center of the element\r
4729 r      The center of the right edge\r
4730 bl     The bottom left corner\r
4731 b      The center of the bottom edge\r
4732 br     The bottom right corner\r
4733 </pre>\r
4734 Example Usage:\r
4735 <pre><code>\r
4736 // align el to other-el using the default positioning ("tl-bl", non-constrained)\r
4737 el.alignTo("other-el");\r
4738 \r
4739 // align the top left corner of el with the top right corner of other-el (constrained to viewport)\r
4740 el.alignTo("other-el", "tr?");\r
4741 \r
4742 // align the bottom right corner of el with the center left edge of other-el\r
4743 el.alignTo("other-el", "br-l?");\r
4744 \r
4745 // align the center of el with the bottom left corner of other-el and\r
4746 // adjust the x position by -6 pixels (and the y position by 0)\r
4747 el.alignTo("other-el", "c-bl", [-6, 0]);\r
4748 </code></pre>\r
4749      * @param {Mixed} element The element to align to.\r
4750      * @param {String} position The position to align to.\r
4751      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
4752      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
4753      * @return {Ext.Element} this\r
4754      */\r
4755     alignTo : function(element, position, offsets, animate){\r
4756             var me = this;\r
4757         return me.setXY(me.getAlignToXY(element, position, offsets),\r
4758                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);\r
4759     },\r
4760     \r
4761     // private ==>  used outside of core\r
4762     adjustForConstraints : function(xy, parent, offsets){\r
4763         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;\r
4764     },\r
4765 \r
4766     // private ==>  used outside of core\r
4767     getConstrainToXY : function(el, local, offsets, proposedXY){   \r
4768             var os = {top:0, left:0, bottom:0, right: 0};\r
4769 \r
4770         return function(el, local, offsets, proposedXY){\r
4771             el = Ext.get(el);\r
4772             offsets = offsets ? Ext.applyIf(offsets, os) : os;\r
4773 \r
4774             var vw, vh, vx = 0, vy = 0;\r
4775             if(el.dom == document.body || el.dom == document){\r
4776                 vw =Ext.lib.Dom.getViewWidth();\r
4777                 vh = Ext.lib.Dom.getViewHeight();\r
4778             }else{\r
4779                 vw = el.dom.clientWidth;\r
4780                 vh = el.dom.clientHeight;\r
4781                 if(!local){\r
4782                     var vxy = el.getXY();\r
4783                     vx = vxy[0];\r
4784                     vy = vxy[1];\r
4785                 }\r
4786             }\r
4787 \r
4788             var s = el.getScroll();\r
4789 \r
4790             vx += offsets.left + s.left;\r
4791             vy += offsets.top + s.top;\r
4792 \r
4793             vw -= offsets.right;\r
4794             vh -= offsets.bottom;\r
4795 \r
4796             var vr = vx+vw;\r
4797             var vb = vy+vh;\r
4798 \r
4799             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);\r
4800             var x = xy[0], y = xy[1];\r
4801             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;\r
4802 \r
4803             // only move it if it needs it\r
4804             var moved = false;\r
4805 \r
4806             // first validate right/bottom\r
4807             if((x + w) > vr){\r
4808                 x = vr - w;\r
4809                 moved = true;\r
4810             }\r
4811             if((y + h) > vb){\r
4812                 y = vb - h;\r
4813                 moved = true;\r
4814             }\r
4815             // then make sure top/left isn't negative\r
4816             if(x < vx){\r
4817                 x = vx;\r
4818                 moved = true;\r
4819             }\r
4820             if(y < vy){\r
4821                 y = vy;\r
4822                 moved = true;\r
4823             }\r
4824             return moved ? [x, y] : false;\r
4825         };\r
4826     }(),\r
4827             \r
4828             \r
4829                 \r
4830 //         el = Ext.get(el);\r
4831 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});\r
4832 \r
4833 //         var  me = this,\r
4834 //              doc = document,\r
4835 //              s = el.getScroll(),\r
4836 //              vxy = el.getXY(),\r
4837 //              vx = offsets.left + s.left, \r
4838 //              vy = offsets.top + s.top,               \r
4839 //              vw = -offsets.right, \r
4840 //              vh = -offsets.bottom, \r
4841 //              vr,\r
4842 //              vb,\r
4843 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),\r
4844 //              x = xy[0],\r
4845 //              y = xy[1],\r
4846 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,\r
4847 //              moved = false; // only move it if it needs it\r
4848 //       \r
4849 //              \r
4850 //         if(el.dom == doc.body || el.dom == doc){\r
4851 //             vw += Ext.lib.Dom.getViewWidth();\r
4852 //             vh += Ext.lib.Dom.getViewHeight();\r
4853 //         }else{\r
4854 //             vw += el.dom.clientWidth;\r
4855 //             vh += el.dom.clientHeight;\r
4856 //             if(!local){                    \r
4857 //                 vx += vxy[0];\r
4858 //                 vy += vxy[1];\r
4859 //             }\r
4860 //         }\r
4861 \r
4862 //         // first validate right/bottom\r
4863 //         if(x + w > vx + vw){\r
4864 //             x = vx + vw - w;\r
4865 //             moved = true;\r
4866 //         }\r
4867 //         if(y + h > vy + vh){\r
4868 //             y = vy + vh - h;\r
4869 //             moved = true;\r
4870 //         }\r
4871 //         // then make sure top/left isn't negative\r
4872 //         if(x < vx){\r
4873 //             x = vx;\r
4874 //             moved = true;\r
4875 //         }\r
4876 //         if(y < vy){\r
4877 //             y = vy;\r
4878 //             moved = true;\r
4879 //         }\r
4880 //         return moved ? [x, y] : false;\r
4881 //    },\r
4882     \r
4883     /**\r
4884     * Calculates the x, y to center this element on the screen\r
4885     * @return {Array} The x, y values [x, y]\r
4886     */\r
4887     getCenterXY : function(){\r
4888         return this.getAlignToXY(document, 'c-c');\r
4889     },\r
4890 \r
4891     /**\r
4892     * Centers the Element in either the viewport, or another Element.\r
4893     * @param {Mixed} centerIn (optional) The element in which to center the element.\r
4894     */\r
4895     center : function(centerIn){\r
4896         return this.alignTo(centerIn || document, 'c-c');        \r
4897     }    \r
4898 });\r
4899 /**\r
4900  * @class Ext.Element\r
4901  */\r
4902 Ext.Element.addMethods(function(){\r
4903         var PARENTNODE = 'parentNode',\r
4904                 NEXTSIBLING = 'nextSibling',\r
4905                 PREVIOUSSIBLING = 'previousSibling',\r
4906                 DQ = Ext.DomQuery,\r
4907                 GET = Ext.get;\r
4908         \r
4909         return {\r
4910                 /**\r
4911              * 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
4912              * @param {String} selector The simple selector to test\r
4913              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)\r
4914              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
4915              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
4916              */\r
4917             findParent : function(simpleSelector, maxDepth, returnEl){\r
4918                 var p = this.dom,\r
4919                         b = document.body, \r
4920                         depth = 0,                      \r
4921                         stopEl;         \r
4922             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {\r
4923                 return null;\r
4924             }\r
4925                 maxDepth = maxDepth || 50;\r
4926                 if (isNaN(maxDepth)) {\r
4927                     stopEl = Ext.getDom(maxDepth);\r
4928                     maxDepth = Number.MAX_VALUE;\r
4929                 }\r
4930                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){\r
4931                     if(DQ.is(p, simpleSelector)){\r
4932                         return returnEl ? GET(p) : p;\r
4933                     }\r
4934                     depth++;\r
4935                     p = p.parentNode;\r
4936                 }\r
4937                 return null;\r
4938             },\r
4939         \r
4940             /**\r
4941              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
4942              * @param {String} selector The simple selector to test\r
4943              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
4944                     search as a number or element (defaults to 10 || document.body)\r
4945              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
4946              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
4947              */\r
4948             findParentNode : function(simpleSelector, maxDepth, returnEl){\r
4949                 var p = Ext.fly(this.dom.parentNode, '_internal');\r
4950                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;\r
4951             },\r
4952         \r
4953             /**\r
4954              * 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
4955              * This is a shortcut for findParentNode() that always returns an Ext.Element.\r
4956              * @param {String} selector The simple selector to test\r
4957              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
4958                     search as a number or element (defaults to 10 || document.body)\r
4959              * @return {Ext.Element} The matching DOM node (or null if no match was found)\r
4960              */\r
4961             up : function(simpleSelector, maxDepth){\r
4962                 return this.findParentNode(simpleSelector, maxDepth, true);\r
4963             },\r
4964         \r
4965             /**\r
4966              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
4967              * @param {String} selector The CSS selector\r
4968              * @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
4969              * @return {CompositeElement/CompositeElementLite} The composite element\r
4970              */\r
4971             select : function(selector, unique){\r
4972                 return Ext.Element.select(selector, unique, this.dom);\r
4973             },\r
4974         \r
4975             /**\r
4976              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).\r
4977              * @param {String} selector The CSS selector\r
4978              * @return {Array} An array of the matched nodes\r
4979              */\r
4980             query : function(selector, unique){\r
4981                 return DQ.select(selector, this.dom);\r
4982             },\r
4983         \r
4984             /**\r
4985              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).\r
4986              * @param {String} selector The CSS selector\r
4987              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
4988              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
4989              */\r
4990             child : function(selector, returnDom){\r
4991                 var n = DQ.selectNode(selector, this.dom);\r
4992                 return returnDom ? n : GET(n);\r
4993             },\r
4994         \r
4995             /**\r
4996              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).\r
4997              * @param {String} selector The CSS selector\r
4998              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
4999              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
5000              */\r
5001             down : function(selector, returnDom){\r
5002                 var n = DQ.selectNode(" > " + selector, this.dom);\r
5003                 return returnDom ? n : GET(n);\r
5004             },\r
5005         \r
5006                  /**\r
5007              * Gets the parent node for this element, optionally chaining up trying to match a selector\r
5008              * @param {String} selector (optional) Find a parent node that matches the passed simple selector\r
5009              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5010              * @return {Ext.Element/HTMLElement} The parent node or null\r
5011                  */\r
5012             parent : function(selector, returnDom){\r
5013                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);\r
5014             },\r
5015         \r
5016              /**\r
5017              * Gets the next sibling, skipping text nodes\r
5018              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5019              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5020              * @return {Ext.Element/HTMLElement} The next sibling or null\r
5021                  */\r
5022             next : function(selector, returnDom){\r
5023                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);\r
5024             },\r
5025         \r
5026             /**\r
5027              * Gets the previous sibling, skipping text nodes\r
5028              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5029              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5030              * @return {Ext.Element/HTMLElement} The previous sibling or null\r
5031                  */\r
5032             prev : function(selector, returnDom){\r
5033                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);\r
5034             },\r
5035         \r
5036         \r
5037             /**\r
5038              * Gets the first child, skipping text nodes\r
5039              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5040              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5041              * @return {Ext.Element/HTMLElement} The first child or null\r
5042                  */\r
5043             first : function(selector, returnDom){\r
5044                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);\r
5045             },\r
5046         \r
5047             /**\r
5048              * Gets the last child, skipping text nodes\r
5049              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5050              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5051              * @return {Ext.Element/HTMLElement} The last child or null\r
5052                  */\r
5053             last : function(selector, returnDom){\r
5054                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);\r
5055             },\r
5056             \r
5057             matchNode : function(dir, start, selector, returnDom){\r
5058                 var n = this.dom[start];\r
5059                 while(n){\r
5060                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){\r
5061                         return !returnDom ? GET(n) : n;\r
5062                     }\r
5063                     n = n[dir];\r
5064                 }\r
5065                 return null;\r
5066             }   \r
5067     }\r
5068 }());/**\r
5069  * @class Ext.Element\r
5070  */\r
5071 Ext.Element.addMethods(\r
5072 function() {\r
5073         var GETDOM = Ext.getDom,\r
5074                 GET = Ext.get,\r
5075                 DH = Ext.DomHelper;\r
5076         \r
5077         return {\r
5078             /**\r
5079              * Appends the passed element(s) to this element\r
5080              * @param {String/HTMLElement/Array/Element/CompositeElement} el\r
5081              * @return {Ext.Element} this\r
5082              */\r
5083             appendChild: function(el){        \r
5084                 return GET(el).appendTo(this);        \r
5085             },\r
5086         \r
5087             /**\r
5088              * Appends this element to the passed element\r
5089              * @param {Mixed} el The new parent element\r
5090              * @return {Ext.Element} this\r
5091              */\r
5092             appendTo: function(el){        \r
5093                 GETDOM(el).appendChild(this.dom);        \r
5094                 return this;\r
5095             },\r
5096         \r
5097             /**\r
5098              * Inserts this element before the passed element in the DOM\r
5099              * @param {Mixed} el The element before which this element will be inserted\r
5100              * @return {Ext.Element} this\r
5101              */\r
5102             insertBefore: function(el){                   \r
5103                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);\r
5104                 return this;\r
5105             },\r
5106         \r
5107             /**\r
5108              * Inserts this element after the passed element in the DOM\r
5109              * @param {Mixed} el The element to insert after\r
5110              * @return {Ext.Element} this\r
5111              */\r
5112             insertAfter: function(el){\r
5113                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);\r
5114                 return this;\r
5115             },\r
5116         \r
5117             /**\r
5118              * Inserts (or creates) an element (or DomHelper config) as the first child of this element\r
5119              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert\r
5120              * @return {Ext.Element} The new child\r
5121              */\r
5122             insertFirst: function(el, returnDom){\r
5123             el = el || {};\r
5124             if(el.nodeType || el.dom || typeof el == 'string'){ // element\r
5125                 el = GETDOM(el);\r
5126                 this.dom.insertBefore(el, this.dom.firstChild);\r
5127                 return !returnDom ? GET(el) : el;\r
5128             }else{ // dh config\r
5129                 return this.createChild(el, this.dom.firstChild, returnDom);\r
5130             }\r
5131         },\r
5132         \r
5133             /**\r
5134              * Replaces the passed element with this element\r
5135              * @param {Mixed} el The element to replace\r
5136              * @return {Ext.Element} this\r
5137              */\r
5138             replace: function(el){\r
5139                 el = GET(el);\r
5140                 this.insertBefore(el);\r
5141                 el.remove();\r
5142                 return this;\r
5143             },\r
5144         \r
5145             /**\r
5146              * Replaces this element with the passed element\r
5147              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create\r
5148              * @return {Ext.Element} this\r
5149              */\r
5150             replaceWith: function(el){\r
5151                     var me = this,\r
5152                         Element = Ext.Element;\r
5153             if(el.nodeType || el.dom || typeof el == 'string'){\r
5154                 el = GETDOM(el);\r
5155                 me.dom.parentNode.insertBefore(el, me.dom);\r
5156             }else{\r
5157                 el = DH.insertBefore(me.dom, el);\r
5158             }\r
5159                 \r
5160                 delete Element.cache[me.id];\r
5161                 Ext.removeNode(me.dom);      \r
5162                 me.id = Ext.id(me.dom = el);\r
5163                 return Element.cache[me.id] = me;        \r
5164             },\r
5165             \r
5166                 /**\r
5167                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.\r
5168                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be\r
5169                  * automatically generated with the specified attributes.\r
5170                  * @param {HTMLElement} insertBefore (optional) a child element of this element\r
5171                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element\r
5172                  * @return {Ext.Element} The new child element\r
5173                  */\r
5174                 createChild: function(config, insertBefore, returnDom){\r
5175                     config = config || {tag:'div'};\r
5176                     return insertBefore ? \r
5177                            DH.insertBefore(insertBefore, config, returnDom !== true) :  \r
5178                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);\r
5179                 },\r
5180                 \r
5181                 /**\r
5182                  * Creates and wraps this element with another element\r
5183                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div\r
5184                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5185                  * @return {HTMLElement/Element} The newly created wrapper element\r
5186                  */\r
5187                 wrap: function(config, returnDom){        \r
5188                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);\r
5189                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);\r
5190                     return newEl;\r
5191                 },\r
5192                 \r
5193                 /**\r
5194                  * Inserts an html fragment into this element\r
5195                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
5196                  * @param {String} html The HTML fragment\r
5197                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)\r
5198                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)\r
5199                  */\r
5200                 insertHtml : function(where, html, returnEl){\r
5201                     var el = DH.insertHtml(where, this.dom, html);\r
5202                     return returnEl ? Ext.get(el) : el;\r
5203                 }\r
5204         }\r
5205 }());/**\r
5206  * @class Ext.Element\r
5207  */\r
5208 Ext.apply(Ext.Element.prototype, function() {\r
5209         var GETDOM = Ext.getDom,\r
5210                 GET = Ext.get,\r
5211                 DH = Ext.DomHelper;\r
5212         \r
5213         return {        \r
5214                 /**\r
5215              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element\r
5216              * @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
5217              * @param {String} where (optional) 'before' or 'after' defaults to before\r
5218              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5219              * @return {Ext.Element} the inserted Element\r
5220              */\r
5221             insertSibling: function(el, where, returnDom){\r
5222                 var me = this,\r
5223                         rt;\r
5224                         \r
5225                 if(Ext.isArray(el)){            \r
5226                     Ext.each(el, function(e) {\r
5227                             rt = me.insertSibling(e, where, returnDom);\r
5228                     });\r
5229                     return rt;\r
5230                 }\r
5231                         \r
5232                 where = (where || 'before').toLowerCase();\r
5233                 el = el || {};\r
5234                 \r
5235             if(el.nodeType || el.dom){\r
5236                 rt = me.dom.parentNode.insertBefore(GETDOM(el), where == 'before' ? me.dom : me.dom.nextSibling);\r
5237                 if (!returnDom) {\r
5238                     rt = GET(rt);\r
5239                 }\r
5240             }else{\r
5241                 if (where == 'after' && !me.dom.nextSibling) {\r
5242                     rt = DH.append(me.dom.parentNode, el, !returnDom);\r
5243                 } else {                    \r
5244                     rt = DH[where == 'after' ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);\r
5245                 }\r
5246             }\r
5247                 return rt;\r
5248             }\r
5249     };\r
5250 }());/**
5251  * @class Ext.Element
5252  */
5253 Ext.Element.addMethods(function(){  
5254     // local style camelizing for speed
5255     var propCache = {},
5256         camelRe = /(-[a-z])/gi,
5257         classReCache = {},
5258         view = document.defaultView,
5259         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5260         opacityRe = /alpha\(opacity=(.*)\)/i,
5261         trimRe = /^\s+|\s+$/g,
5262         EL = Ext.Element,   
5263         PADDING = "padding",
5264         MARGIN = "margin",
5265         BORDER = "border",
5266         LEFT = "-left",
5267         RIGHT = "-right",
5268         TOP = "-top",
5269         BOTTOM = "-bottom",
5270         WIDTH = "-width",    
5271         MATH = Math,
5272         HIDDEN = 'hidden',
5273         ISCLIPPED = 'isClipped',
5274         OVERFLOW = 'overflow',
5275         OVERFLOWX = 'overflow-x',
5276         OVERFLOWY = 'overflow-y',
5277         ORIGINALCLIP = 'originalClip',
5278         // special markup used throughout Ext when box wrapping elements    
5279         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5280         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5281         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5282         data = Ext.Element.data;
5283         
5284     
5285     // private  
5286     function camelFn(m, a) {
5287         return a.charAt(1).toUpperCase();
5288     }
5289     
5290     function chkCache(prop) {
5291         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5292     }
5293             
5294     return {
5295         // private  ==> used by Fx  
5296         adjustWidth : function(width) {
5297             var me = this;
5298             var isNum = Ext.isNumber(width);
5299             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5300                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5301             }
5302             return (isNum && width < 0) ? 0 : width;
5303         },
5304         
5305         // private   ==> used by Fx 
5306         adjustHeight : function(height) {
5307             var me = this;
5308             var isNum = Ext.isNumber(height);
5309             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5310                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));               
5311             }
5312             return (isNum && height < 0) ? 0 : height;
5313         },
5314     
5315     
5316         /**
5317          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5318          * @param {String/Array} className The CSS class to add, or an array of classes
5319          * @return {Ext.Element} this
5320          */
5321         addClass : function(className){
5322             var me = this;
5323             Ext.each(className, function(v) {
5324                 me.dom.className += (!me.hasClass(v) && v ? " " + v : "");  
5325             });
5326             return me;
5327         },
5328     
5329         /**
5330          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5331          * @param {String/Array} className The CSS class to add, or an array of classes
5332          * @return {Ext.Element} this
5333          */
5334         radioClass : function(className){
5335             Ext.each(this.dom.parentNode.childNodes, function(v) {
5336                 if(v.nodeType == 1) {
5337                     Ext.fly(v, '_internal').removeClass(className);          
5338                 }
5339             });
5340             return this.addClass(className);
5341         },
5342     
5343         /**
5344          * Removes one or more CSS classes from the element.
5345          * @param {String/Array} className The CSS class to remove, or an array of classes
5346          * @return {Ext.Element} this
5347          */
5348         removeClass : function(className){
5349             var me = this;
5350             if (me.dom && me.dom.className) {
5351                 Ext.each(className, function(v) {               
5352                     me.dom.className = me.dom.className.replace(
5353                         classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), 
5354                         " ");               
5355                 });    
5356             }
5357             return me;
5358         },
5359     
5360         /**
5361          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5362          * @param {String} className The CSS class to toggle
5363          * @return {Ext.Element} this
5364          */
5365         toggleClass : function(className){
5366             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5367         },
5368     
5369         /**
5370          * Checks if the specified CSS class exists on this element's DOM node.
5371          * @param {String} className The CSS class to check for
5372          * @return {Boolean} True if the class exists, else false
5373          */
5374         hasClass : function(className){
5375             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5376         },
5377     
5378         /**
5379          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5380          * @param {String} oldClassName The CSS class to replace
5381          * @param {String} newClassName The replacement CSS class
5382          * @return {Ext.Element} this
5383          */
5384         replaceClass : function(oldClassName, newClassName){
5385             return this.removeClass(oldClassName).addClass(newClassName);
5386         },
5387         
5388         isStyle : function(style, val) {
5389             return this.getStyle(style) == val;  
5390         },
5391     
5392         /**
5393          * Normalizes currentStyle and computedStyle.
5394          * @param {String} property The style property whose value is returned.
5395          * @return {String} The current value of the style property for this element.
5396          */
5397         getStyle : function(){         
5398             return view && view.getComputedStyle ?
5399                 function(prop){
5400                     var el = this.dom,
5401                         v,                  
5402                         cs,
5403                         out;
5404                     if(el == document) return null;
5405                     prop = chkCache(prop);
5406                     out = (v = el.style[prop]) ? v : 
5407                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5408                     
5409                     // Webkit returns rgb values for transparent.
5410                     if(Ext.isWebKit && out == 'rgba(0, 0, 0, 0)'){
5411                         out = 'transparent';
5412                     }
5413                     return out;
5414                 } :
5415                 function(prop){      
5416                     var el = this.dom, 
5417                         m, 
5418                         cs;     
5419                         
5420                     if(el == document) return null;      
5421                     if (prop == 'opacity') {
5422                         if (el.style.filter.match) {                       
5423                             if(m = el.style.filter.match(opacityRe)){
5424                                 var fv = parseFloat(m[1]);
5425                                 if(!isNaN(fv)){
5426                                     return fv ? fv / 100 : 0;
5427                                 }
5428                             }
5429                         }
5430                         return 1;
5431                     }
5432                     prop = chkCache(prop);  
5433                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5434                 };
5435         }(),
5436
5437         /**
5438          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5439          * are convert to standard 6 digit hex color.
5440          * @param {String} attr The css attribute
5441          * @param {String} defaultValue The default value to use when a valid color isn't found
5442          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5443          * color anims.
5444          */
5445         getColor : function(attr, defaultValue, prefix){
5446             var v = this.getStyle(attr),
5447                 color = Ext.isDefined(prefix) ? prefix : '#',
5448                 h;
5449                 
5450             if(!v || /transparent|inherit/.test(v)){
5451                 return defaultValue;
5452             }
5453             if(/^r/.test(v)){
5454                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
5455                     h = parseInt(s, 10);
5456                     color += (h < 16 ? '0' : '') + h.toString(16); 
5457                 });
5458             }else{
5459                 v = v.replace('#', '');
5460                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
5461             }
5462             return(color.length > 5 ? color.toLowerCase() : defaultValue);
5463         },
5464     
5465         /**
5466          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
5467          * @param {String/Object} property The style property to be set, or an object of multiple styles.
5468          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
5469          * @return {Ext.Element} this
5470          */
5471         setStyle : function(prop, value){
5472             var tmp, 
5473                 style,
5474                 camel;
5475             if (!Ext.isObject(prop)) {
5476                 tmp = {};
5477                 tmp[prop] = value;          
5478                 prop = tmp;
5479             }
5480             for (style in prop) {
5481                 value = prop[style];            
5482                 style == 'opacity' ? 
5483                     this.setOpacity(value) : 
5484                     this.dom.style[chkCache(style)] = value;
5485             }
5486             return this;
5487         },
5488         
5489         /**
5490          * Set the opacity of the element
5491          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
5492          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
5493          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
5494          * @return {Ext.Element} this
5495          */
5496          setOpacity : function(opacity, animate){
5497             var me = this,
5498                 s = me.dom.style;
5499                 
5500             if(!animate || !me.anim){            
5501                 if(Ext.isIE){
5502                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '', 
5503                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
5504
5505                     s.zoom = 1;
5506                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
5507                 }else{
5508                     s.opacity = opacity;
5509                 }
5510             }else{
5511                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
5512             }
5513             return me;
5514         },
5515         
5516         /**
5517          * Clears any opacity settings from this element. Required in some cases for IE.
5518          * @return {Ext.Element} this
5519          */
5520         clearOpacity : function(){
5521             var style = this.dom.style;
5522             if(Ext.isIE){
5523                 if(!Ext.isEmpty(style.filter)){
5524                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
5525                 }
5526             }else{
5527                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
5528             }
5529             return this;
5530         },
5531     
5532         /**
5533          * Returns the offset height of the element
5534          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
5535          * @return {Number} The element's height
5536          */
5537         getHeight : function(contentHeight){
5538             var me = this,
5539                 dom = me.dom,
5540                 hidden = Ext.isIE && me.isStyle('display', 'none'),
5541                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
5542                 
5543             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
5544             return h < 0 ? 0 : h;
5545         },
5546     
5547         /**
5548          * Returns the offset width of the element
5549          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
5550          * @return {Number} The element's width
5551          */
5552         getWidth : function(contentWidth){
5553             var me = this,
5554                 dom = me.dom,
5555                 hidden = Ext.isIE && me.isStyle('display', 'none'),
5556                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
5557             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
5558             return w < 0 ? 0 : w;
5559         },
5560     
5561         /**
5562          * Set the width of this Element.
5563          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
5564          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
5565          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
5566          * </ul></div>
5567          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5568          * @return {Ext.Element} this
5569          */
5570         setWidth : function(width, animate){
5571             var me = this;
5572             width = me.adjustWidth(width);
5573             !animate || !me.anim ? 
5574                 me.dom.style.width = me.addUnits(width) :
5575                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
5576             return me;
5577         },
5578     
5579         /**
5580          * Set the height of this Element.
5581          * <pre><code>
5582 // change the height to 200px and animate with default configuration
5583 Ext.fly('elementId').setHeight(200, true);
5584
5585 // change the height to 150px and animate with a custom configuration
5586 Ext.fly('elId').setHeight(150, {
5587     duration : .5, // animation will have a duration of .5 seconds
5588     // will change the content to "finished"
5589     callback: function(){ this.{@link #update}("finished"); } 
5590 });
5591          * </code></pre>
5592          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
5593          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
5594          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
5595          * </ul></div>
5596          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5597          * @return {Ext.Element} this
5598          */
5599          setHeight : function(height, animate){
5600             var me = this;
5601             height = me.adjustHeight(height);
5602             !animate || !me.anim ? 
5603                 me.dom.style.height = me.addUnits(height) :
5604                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
5605             return me;
5606         },
5607         
5608         /**
5609          * Gets the width of the border(s) for the specified side(s)
5610          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
5611          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
5612          * @return {Number} The width of the sides passed added together
5613          */
5614         getBorderWidth : function(side){
5615             return this.addStyles(side, borders);
5616         },
5617     
5618         /**
5619          * Gets the width of the padding(s) for the specified side(s)
5620          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
5621          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
5622          * @return {Number} The padding of the sides passed added together
5623          */
5624         getPadding : function(side){
5625             return this.addStyles(side, paddings);
5626         },
5627     
5628         /**
5629          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
5630          * @return {Ext.Element} this
5631          */
5632         clip : function(){
5633             var me = this,
5634                 dom = me.dom;
5635                 
5636             if(!data(dom, ISCLIPPED)){
5637                 data(dom, ISCLIPPED, true);
5638                 data(dom, ORIGINALCLIP, {
5639                     o: me.getStyle(OVERFLOW),
5640                     x: me.getStyle(OVERFLOWX),
5641                     y: me.getStyle(OVERFLOWY)
5642                 });
5643                 me.setStyle(OVERFLOW, HIDDEN);
5644                 me.setStyle(OVERFLOWX, HIDDEN);
5645                 me.setStyle(OVERFLOWY, HIDDEN);
5646             }
5647             return me;
5648         },
5649     
5650         /**
5651          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
5652          * @return {Ext.Element} this
5653          */
5654         unclip : function(){
5655             var me = this,
5656                 dom = me.dom;
5657                 
5658             if(data(dom, ISCLIPPED)){
5659                 data(dom, ISCLIPPED, false);
5660                 var o = data(dom, ORIGINALCLIP);
5661                 if(o.o){
5662                     me.setStyle(OVERFLOW, o.o);
5663                 }
5664                 if(o.x){
5665                     me.setStyle(OVERFLOWX, o.x);
5666                 }
5667                 if(o.y){
5668                     me.setStyle(OVERFLOWY, o.y);
5669                 }
5670             }
5671             return me;
5672         },
5673
5674         // private
5675         addStyles : function(sides, styles){
5676             var val = 0;
5677
5678             Ext.each(sides.match(/\w/g), function(s) {
5679                 if (s = parseInt(this.getStyle(styles[s]), 10)) {
5680                     val += MATH.abs(s);
5681                 }
5682             },
5683             this);
5684             return val;
5685         },
5686
5687         margins : margins
5688     }
5689 }()         
5690 );
5691 /**\r
5692  * @class Ext.Element\r
5693  */\r
5694 \r
5695 // special markup used throughout Ext when box wrapping elements\r
5696 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
5697 \r
5698 Ext.Element.addMethods(function(){\r
5699         var INTERNAL = "_internal";\r
5700         return {\r
5701             /**\r
5702              * More flexible version of {@link #setStyle} for setting style properties.\r
5703              * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
5704              * a function which returns such a specification.\r
5705              * @return {Ext.Element} this\r
5706              */\r
5707             applyStyles : function(style){\r
5708                 Ext.DomHelper.applyStyles(this.dom, style);\r
5709                 return this;\r
5710             },\r
5711 \r
5712                 /**\r
5713              * Returns an object with properties matching the styles requested.\r
5714              * For example, el.getStyles('color', 'font-size', 'width') might return\r
5715              * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
5716              * @param {String} style1 A style name\r
5717              * @param {String} style2 A style name\r
5718              * @param {String} etc.\r
5719              * @return {Object} The style object\r
5720              */\r
5721             getStyles : function(){\r
5722                     var ret = {};\r
5723                     Ext.each(arguments, function(v) {\r
5724                            ret[v] = this.getStyle(v);\r
5725                     },\r
5726                     this);\r
5727                     return ret;\r
5728             },\r
5729 \r
5730                 getStyleSize : function(){\r
5731                 var me = this,\r
5732                         w,\r
5733                         h,\r
5734                         d = this.dom,\r
5735                         s = d.style;\r
5736                 if(s.width && s.width != 'auto'){\r
5737                     w = parseInt(s.width, 10);\r
5738                     if(me.isBorderBox()){\r
5739                        w -= me.getFrameWidth('lr');\r
5740                     }\r
5741                 }\r
5742                 if(s.height && s.height != 'auto'){\r
5743                     h = parseInt(s.height, 10);\r
5744                     if(me.isBorderBox()){\r
5745                        h -= me.getFrameWidth('tb');\r
5746                     }\r
5747                 }\r
5748                 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};\r
5749             },\r
5750 \r
5751             // private  ==> used by ext full\r
5752                 setOverflow : function(v){\r
5753                         var dom = this.dom;\r
5754                 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
5755                         dom.style.overflow = 'hidden';\r
5756                         (function(){dom.style.overflow = 'auto';}).defer(1);\r
5757                 }else{\r
5758                         dom.style.overflow = v;\r
5759                 }\r
5760                 },\r
5761 \r
5762            /**\r
5763                 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as\r
5764                 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>\r
5765                 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},\r
5766                 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup\r
5767                 * is of this form:</p>\r
5768                 * <pre><code>\r
5769 Ext.Element.boxMarkup =\r
5770     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>\r
5771      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>\r
5772      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;\r
5773                 * </code></pre>\r
5774                 * <p>Example usage:</p>\r
5775                 * <pre><code>\r
5776 // Basic box wrap\r
5777 Ext.get("foo").boxWrap();\r
5778 \r
5779 // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
5780 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
5781 // for how to create a custom box wrap style.\r
5782 Ext.get("foo").boxWrap().addClass("x-box-blue");\r
5783                 * </code></pre>\r
5784                 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element\r
5785                 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on\r
5786                 * this name to make the overall effect work, so if you supply an alternate base class, make sure you\r
5787                 * also supply all of the necessary rules.\r
5788                 * @return {Ext.Element} this\r
5789                 */\r
5790             boxWrap : function(cls){\r
5791                 cls = cls || 'x-box';\r
5792                 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
5793                 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);\r
5794                 return el;\r
5795             },\r
5796 \r
5797         /**\r
5798          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.\r
5799          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
5800          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
5801          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
5802          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>\r
5803          * </ul></div>\r
5804          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
5805          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
5806          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
5807          * </ul></div>\r
5808          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
5809          * @return {Ext.Element} this\r
5810          */\r
5811             setSize : function(width, height, animate){\r
5812                         var me = this;\r
5813                         if(Ext.isObject(width)){ // in case of object from getSize()\r
5814                             height = width.height;\r
5815                             width = width.width;\r
5816                         }\r
5817                         width = me.adjustWidth(width);\r
5818                         height = me.adjustHeight(height);\r
5819                         if(!animate || !me.anim){\r
5820                             me.dom.style.width = me.addUnits(width);\r
5821                             me.dom.style.height = me.addUnits(height);\r
5822                         }else{\r
5823                             me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));\r
5824                         }\r
5825                         return me;\r
5826             },\r
5827 \r
5828             /**\r
5829              * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
5830              * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
5831              * if a height has not been set using CSS.\r
5832              * @return {Number}\r
5833              */\r
5834             getComputedHeight : function(){\r
5835                     var me = this,\r
5836                         h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);\r
5837                 if(!h){\r
5838                     h = parseInt(me.getStyle('height'), 10) || 0;\r
5839                     if(!me.isBorderBox()){\r
5840                         h += me.getFrameWidth('tb');\r
5841                     }\r
5842                 }\r
5843                 return h;\r
5844             },\r
5845 \r
5846             /**\r
5847              * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
5848              * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
5849              * if a width has not been set using CSS.\r
5850              * @return {Number}\r
5851              */\r
5852             getComputedWidth : function(){\r
5853                 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
5854                 if(!w){\r
5855                     w = parseInt(this.getStyle('width'), 10) || 0;\r
5856                     if(!this.isBorderBox()){\r
5857                         w += this.getFrameWidth('lr');\r
5858                     }\r
5859                 }\r
5860                 return w;\r
5861             },\r
5862 \r
5863             /**\r
5864              * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
5865              for more information about the sides.\r
5866              * @param {String} sides\r
5867              * @return {Number}\r
5868              */\r
5869             getFrameWidth : function(sides, onlyContentBox){\r
5870                 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
5871             },\r
5872 \r
5873             /**\r
5874              * Sets up event handlers to add and remove a css class when the mouse is over this element\r
5875              * @param {String} className\r
5876              * @return {Ext.Element} this\r
5877              */\r
5878             addClassOnOver : function(className){\r
5879                 this.hover(\r
5880                     function(){\r
5881                         Ext.fly(this, INTERNAL).addClass(className);\r
5882                     },\r
5883                     function(){\r
5884                         Ext.fly(this, INTERNAL).removeClass(className);\r
5885                     }\r
5886                 );\r
5887                 return this;\r
5888             },\r
5889 \r
5890             /**\r
5891              * Sets up event handlers to add and remove a css class when this element has the focus\r
5892              * @param {String} className\r
5893              * @return {Ext.Element} this\r
5894              */\r
5895             addClassOnFocus : function(className){\r
5896                     this.on("focus", function(){\r
5897                         Ext.fly(this, INTERNAL).addClass(className);\r
5898                     }, this.dom);\r
5899                     this.on("blur", function(){\r
5900                         Ext.fly(this, INTERNAL).removeClass(className);\r
5901                     }, this.dom);\r
5902                     return this;\r
5903             },\r
5904 \r
5905             /**\r
5906              * 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
5907              * @param {String} className\r
5908              * @return {Ext.Element} this\r
5909              */\r
5910             addClassOnClick : function(className){\r
5911                 var dom = this.dom;\r
5912                 this.on("mousedown", function(){\r
5913                     Ext.fly(dom, INTERNAL).addClass(className);\r
5914                     var d = Ext.getDoc(),\r
5915                         fn = function(){\r
5916                                 Ext.fly(dom, INTERNAL).removeClass(className);\r
5917                                 d.removeListener("mouseup", fn);\r
5918                             };\r
5919                     d.on("mouseup", fn);\r
5920                 });\r
5921                 return this;\r
5922             },\r
5923 \r
5924             /**\r
5925              * Returns the width and height of the viewport.\r
5926         * <pre><code>\r
5927         var vpSize = Ext.getBody().getViewSize();\r
5928 \r
5929         // all Windows created afterwards will have a default value of 90% height and 95% width\r
5930         Ext.Window.override({\r
5931             width: vpSize.width * 0.9,\r
5932             height: vpSize.height * 0.95\r
5933         });\r
5934         // To handle window resizing you would have to hook onto onWindowResize.\r
5935         </code></pre>\r
5936              * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}\r
5937              */\r
5938             getViewSize : function(){\r
5939                 var doc = document,\r
5940                         d = this.dom,\r
5941                         extdom = Ext.lib.Dom,\r
5942                         isDoc = (d == doc || d == doc.body);\r
5943                 return { width : (isDoc ? extdom.getViewWidth() : d.clientWidth),\r
5944                                  height : (isDoc ? extdom.getViewHeight() : d.clientHeight) };\r
5945             },\r
5946 \r
5947             /**\r
5948              * Returns the size of the element.\r
5949              * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
5950              * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
5951              */\r
5952             getSize : function(contentSize){\r
5953                 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
5954             },\r
5955 \r
5956             /**\r
5957              * Forces the browser to repaint this element\r
5958              * @return {Ext.Element} this\r
5959              */\r
5960             repaint : function(){\r
5961                 var dom = this.dom;\r
5962                 this.addClass("x-repaint");\r
5963                 setTimeout(function(){\r
5964                     Ext.fly(dom).removeClass("x-repaint");\r
5965                 }, 1);\r
5966                 return this;\r
5967             },\r
5968 \r
5969             /**\r
5970              * Disables text selection for this element (normalized across browsers)\r
5971              * @return {Ext.Element} this\r
5972              */\r
5973             unselectable : function(){\r
5974                 this.dom.unselectable = "on";\r
5975                 return this.swallowEvent("selectstart", true).\r
5976                                     applyStyles("-moz-user-select:none;-khtml-user-select:none;").\r
5977                                     addClass("x-unselectable");\r
5978             },\r
5979 \r
5980             /**\r
5981              * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
5982              * then it returns the calculated width of the sides (see getPadding)\r
5983              * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
5984              * @return {Object/Number}\r
5985              */\r
5986             getMargins : function(side){\r
5987                     var me = this,\r
5988                         key,\r
5989                         hash = {t:"top", l:"left", r:"right", b: "bottom"},\r
5990                         o = {};\r
5991 \r
5992                     if (!side) {\r
5993                         for (key in me.margins){\r
5994                                 o[hash[key]] = parseInt(me.getStyle(me.margins[key]), 10) || 0;\r
5995                 }\r
5996                         return o;\r
5997                 } else {\r
5998                     return me.addStyles.call(me, side, me.margins);\r
5999                 }\r
6000             }\r
6001     };\r
6002 }());/**\r
6003  * @class Ext.Element\r
6004  */\r
6005 (function(){\r
6006 var D = Ext.lib.Dom,\r
6007         LEFT = "left",\r
6008         RIGHT = "right",\r
6009         TOP = "top",\r
6010         BOTTOM = "bottom",\r
6011         POSITION = "position",\r
6012         STATIC = "static",\r
6013         RELATIVE = "relative",\r
6014         AUTO = "auto",\r
6015         ZINDEX = "z-index";\r
6016 \r
6017 Ext.Element.addMethods({\r
6018         /**\r
6019       * 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
6020       * @return {Number} The X position of the element\r
6021       */\r
6022     getX : function(){\r
6023         return D.getX(this.dom);\r
6024     },\r
6025 \r
6026     /**\r
6027       * 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
6028       * @return {Number} The Y position of the element\r
6029       */\r
6030     getY : function(){\r
6031         return D.getY(this.dom);\r
6032     },\r
6033 \r
6034     /**\r
6035       * 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
6036       * @return {Array} The XY position of the element\r
6037       */\r
6038     getXY : function(){\r
6039         return D.getXY(this.dom);\r
6040     },\r
6041 \r
6042     /**\r
6043       * 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
6044       * @param {Mixed} element The element to get the offsets from.\r
6045       * @return {Array} The XY page offsets (e.g. [100, -200])\r
6046       */\r
6047     getOffsetsTo : function(el){\r
6048         var o = this.getXY(),\r
6049                 e = Ext.fly(el, '_internal').getXY();\r
6050         return [o[0]-e[0],o[1]-e[1]];\r
6051     },\r
6052 \r
6053     /**\r
6054      * 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
6055      * @param {Number} The X position of the element\r
6056      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6057      * @return {Ext.Element} this\r
6058      */\r
6059     setX : function(x, animate){            \r
6060             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));\r
6061     },\r
6062 \r
6063     /**\r
6064      * 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
6065      * @param {Number} The Y position of the element\r
6066      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6067      * @return {Ext.Element} this\r
6068      */\r
6069     setY : function(y, animate){            \r
6070             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));\r
6071     },\r
6072 \r
6073     /**\r
6074      * Sets the element's left position directly using CSS style (instead of {@link #setX}).\r
6075      * @param {String} left The left CSS property value\r
6076      * @return {Ext.Element} this\r
6077      */\r
6078     setLeft : function(left){\r
6079         this.setStyle(LEFT, this.addUnits(left));\r
6080         return this;\r
6081     },\r
6082 \r
6083     /**\r
6084      * Sets the element's top position directly using CSS style (instead of {@link #setY}).\r
6085      * @param {String} top The top CSS property value\r
6086      * @return {Ext.Element} this\r
6087      */\r
6088     setTop : function(top){\r
6089         this.setStyle(TOP, this.addUnits(top));\r
6090         return this;\r
6091     },\r
6092 \r
6093     /**\r
6094      * Sets the element's CSS right style.\r
6095      * @param {String} right The right CSS property value\r
6096      * @return {Ext.Element} this\r
6097      */\r
6098     setRight : function(right){\r
6099         this.setStyle(RIGHT, this.addUnits(right));\r
6100         return this;\r
6101     },\r
6102 \r
6103     /**\r
6104      * Sets the element's CSS bottom style.\r
6105      * @param {String} bottom The bottom CSS property value\r
6106      * @return {Ext.Element} this\r
6107      */\r
6108     setBottom : function(bottom){\r
6109         this.setStyle(BOTTOM, this.addUnits(bottom));\r
6110         return this;\r
6111     },\r
6112 \r
6113     /**\r
6114      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6115      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6116      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)\r
6117      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6118      * @return {Ext.Element} this\r
6119      */\r
6120     setXY : function(pos, animate){\r
6121             var me = this;\r
6122         if(!animate || !me.anim){\r
6123             D.setXY(me.dom, pos);\r
6124         }else{\r
6125             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');\r
6126         }\r
6127         return me;\r
6128     },\r
6129 \r
6130     /**\r
6131      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6132      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6133      * @param {Number} x X value for new position (coordinates are page-based)\r
6134      * @param {Number} y Y value for new position (coordinates are page-based)\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     setLocation : function(x, y, animate){\r
6139         return this.setXY([x, y], this.animTest(arguments, animate, 2));\r
6140     },\r
6141 \r
6142     /**\r
6143      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6144      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6145      * @param {Number} x X value for new position (coordinates are page-based)\r
6146      * @param {Number} y Y value for new position (coordinates are page-based)\r
6147      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6148      * @return {Ext.Element} this\r
6149      */\r
6150     moveTo : function(x, y, animate){\r
6151         return this.setXY([x, y], this.animTest(arguments, animate, 2));        \r
6152     },    \r
6153     \r
6154     /**\r
6155      * Gets the left X coordinate\r
6156      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6157      * @return {Number}\r
6158      */\r
6159     getLeft : function(local){\r
6160             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;\r
6161     },\r
6162 \r
6163     /**\r
6164      * Gets the right X coordinate of the element (element X position + element width)\r
6165      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6166      * @return {Number}\r
6167      */\r
6168     getRight : function(local){\r
6169             var me = this;\r
6170             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;\r
6171     },\r
6172 \r
6173     /**\r
6174      * Gets the top Y coordinate\r
6175      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6176      * @return {Number}\r
6177      */\r
6178     getTop : function(local) {\r
6179             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;\r
6180     },\r
6181 \r
6182     /**\r
6183      * Gets the bottom Y coordinate of the element (element Y position + element height)\r
6184      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6185      * @return {Number}\r
6186      */\r
6187     getBottom : function(local){\r
6188             var me = this;\r
6189             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;\r
6190     },\r
6191 \r
6192     /**\r
6193     * Initializes positioning on this element. If a desired position is not passed, it will make the\r
6194     * the element positioned relative IF it is not already positioned.\r
6195     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"\r
6196     * @param {Number} zIndex (optional) The zIndex to apply\r
6197     * @param {Number} x (optional) Set the page X position\r
6198     * @param {Number} y (optional) Set the page Y position\r
6199     */\r
6200     position : function(pos, zIndex, x, y){\r
6201             var me = this;\r
6202             \r
6203         if(!pos && me.isStyle(POSITION, STATIC)){           \r
6204             me.setStyle(POSITION, RELATIVE);           \r
6205         } else if(pos) {\r
6206             me.setStyle(POSITION, pos);\r
6207         }\r
6208         if(zIndex){\r
6209             me.setStyle(ZINDEX, zIndex);\r
6210         }\r
6211         if(x || y) me.setXY([x || false, y || false]);\r
6212     },\r
6213 \r
6214     /**\r
6215     * Clear positioning back to the default when the document was loaded\r
6216     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.\r
6217     * @return {Ext.Element} this\r
6218      */\r
6219     clearPositioning : function(value){\r
6220         value = value || '';\r
6221         this.setStyle({\r
6222             left : value,\r
6223             right : value,\r
6224             top : value,\r
6225             bottom : value,\r
6226             "z-index" : "",\r
6227             position : STATIC\r
6228         });\r
6229         return this;\r
6230     },\r
6231 \r
6232     /**\r
6233     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get\r
6234     * snapshot before performing an update and then restoring the element.\r
6235     * @return {Object}\r
6236     */\r
6237     getPositioning : function(){\r
6238         var l = this.getStyle(LEFT);\r
6239         var t = this.getStyle(TOP);\r
6240         return {\r
6241             "position" : this.getStyle(POSITION),\r
6242             "left" : l,\r
6243             "right" : l ? "" : this.getStyle(RIGHT),\r
6244             "top" : t,\r
6245             "bottom" : t ? "" : this.getStyle(BOTTOM),\r
6246             "z-index" : this.getStyle(ZINDEX)\r
6247         };\r
6248     },\r
6249     \r
6250     /**\r
6251     * Set positioning with an object returned by getPositioning().\r
6252     * @param {Object} posCfg\r
6253     * @return {Ext.Element} this\r
6254      */\r
6255     setPositioning : function(pc){\r
6256             var me = this,\r
6257                 style = me.dom.style;\r
6258                 \r
6259         me.setStyle(pc);\r
6260         \r
6261         if(pc.right == AUTO){\r
6262             style.right = "";\r
6263         }\r
6264         if(pc.bottom == AUTO){\r
6265             style.bottom = "";\r
6266         }\r
6267         \r
6268         return me;\r
6269     },    \r
6270         \r
6271     /**\r
6272      * Translates the passed page coordinates into left/top css values for this element\r
6273      * @param {Number/Array} x The page x or an array containing [x, y]\r
6274      * @param {Number} y (optional) The page y, required if x is not an array\r
6275      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}\r
6276      */\r
6277     translatePoints : function(x, y){                \r
6278             y = isNaN(x[1]) ? y : x[1];\r
6279         x = isNaN(x[0]) ? x : x[0];\r
6280         var me = this,\r
6281                 relative = me.isStyle(POSITION, RELATIVE),\r
6282                 o = me.getXY(),\r
6283                 l = parseInt(me.getStyle(LEFT), 10),\r
6284                 t = parseInt(me.getStyle(TOP), 10);\r
6285         \r
6286         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);\r
6287         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        \r
6288 \r
6289         return {left: (x - o[0] + l), top: (y - o[1] + t)}; \r
6290     },\r
6291     \r
6292     animTest : function(args, animate, i) {\r
6293         return !!animate && this.preanim ? this.preanim(args, i) : false;\r
6294     }\r
6295 });\r
6296 })();/**\r
6297  * @class Ext.Element\r
6298  */\r
6299 Ext.Element.addMethods({\r
6300     /**\r
6301      * 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
6302      * @param {Object} box The box to fill {x, y, width, height}\r
6303      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically\r
6304      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6305      * @return {Ext.Element} this\r
6306      */\r
6307     setBox : function(box, adjust, animate){\r
6308         var me = this,\r
6309                 w = box.width, \r
6310                 h = box.height;\r
6311         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){\r
6312            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));\r
6313            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));\r
6314         }\r
6315         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));\r
6316         return me;\r
6317     },\r
6318     \r
6319     /**\r
6320      * Return a box {x, y, width, height} that can be used to set another elements\r
6321      * size/location to match this element.\r
6322      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.\r
6323      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.\r
6324      * @return {Object} box An object in the format {x, y, width, height}\r
6325      */\r
6326         getBox : function(contentBox, local) {      \r
6327             var me = this,\r
6328                 xy,\r
6329                 left,\r
6330                 top,\r
6331                 getBorderWidth = me.getBorderWidth,\r
6332                 getPadding = me.getPadding, \r
6333                 l,\r
6334                 r,\r
6335                 t,\r
6336                 b;\r
6337         if(!local){\r
6338             xy = me.getXY();\r
6339         }else{\r
6340             left = parseInt(me.getStyle("left"), 10) || 0;\r
6341             top = parseInt(me.getStyle("top"), 10) || 0;\r
6342             xy = [left, top];\r
6343         }\r
6344         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;\r
6345         if(!contentBox){\r
6346             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};\r
6347         }else{\r
6348             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");\r
6349             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");\r
6350             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");\r
6351             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");\r
6352             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
6353         }\r
6354         bx.right = bx.x + bx.width;\r
6355         bx.bottom = bx.y + bx.height;\r
6356         return bx;\r
6357         },\r
6358         \r
6359     /**\r
6360      * Move this element relative to its current position.\r
6361      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6362      * @param {Number} distance How far to move the element in pixels\r
6363      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6364      * @return {Ext.Element} this\r
6365      */\r
6366      move : function(direction, distance, animate){\r
6367         var me = this,          \r
6368                 xy = me.getXY(),\r
6369                 x = xy[0],\r
6370                 y = xy[1],              \r
6371                 left = [x - distance, y],\r
6372                 right = [x + distance, y],\r
6373                 top = [x, y - distance],\r
6374                 bottom = [x, y + distance],\r
6375                 hash = {\r
6376                         l :     left,\r
6377                         left : left,\r
6378                         r : right,\r
6379                         right : right,\r
6380                         t : top,\r
6381                         top : top,\r
6382                         up : top,\r
6383                         b : bottom, \r
6384                         bottom : bottom,\r
6385                         down : bottom                           \r
6386                 };\r
6387         \r
6388             direction = direction.toLowerCase();    \r
6389             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));\r
6390     },\r
6391     \r
6392     /**\r
6393      * Quick set left and top adding default units\r
6394      * @param {String} left The left CSS property value\r
6395      * @param {String} top The top CSS property value\r
6396      * @return {Ext.Element} this\r
6397      */\r
6398      setLeftTop : function(left, top){\r
6399             var me = this,\r
6400                 style = me.dom.style;\r
6401         style.left = me.addUnits(left);\r
6402         style.top = me.addUnits(top);\r
6403         return me;\r
6404     },\r
6405     \r
6406     /**\r
6407      * Returns the region of the given element.\r
6408      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).\r
6409      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.\r
6410      */\r
6411     getRegion : function(){\r
6412         return Ext.lib.Dom.getRegion(this.dom);\r
6413     },\r
6414     \r
6415     /**\r
6416      * 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
6417      * @param {Number} x X value for new position (coordinates are page-based)\r
6418      * @param {Number} y Y value for new position (coordinates are page-based)\r
6419      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6420      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6421      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6422      * </ul></div>\r
6423      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6424      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6425      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6426      * </ul></div>\r
6427      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6428      * @return {Ext.Element} this\r
6429      */\r
6430     setBounds : function(x, y, width, height, animate){\r
6431             var me = this;\r
6432         if (!animate || !me.anim) {\r
6433             me.setSize(width, height);\r
6434             me.setLocation(x, y);\r
6435         } else {\r
6436             me.anim({points: {to: [x, y]}, \r
6437                          width: {to: me.adjustWidth(width)}, \r
6438                          height: {to: me.adjustHeight(height)}},\r
6439                      me.preanim(arguments, 4), \r
6440                      'motion');\r
6441         }\r
6442         return me;\r
6443     },\r
6444 \r
6445     /**\r
6446      * 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
6447      * @param {Ext.lib.Region} region The region to fill\r
6448      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6449      * @return {Ext.Element} this\r
6450      */\r
6451     setRegion : function(region, animate) {\r
6452         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));\r
6453     }\r
6454 });/**\r
6455  * @class Ext.Element\r
6456  */\r
6457 Ext.Element.addMethods({\r
6458     /**\r
6459      * Returns true if this element is scrollable.\r
6460      * @return {Boolean}\r
6461      */\r
6462     isScrollable : function(){\r
6463         var dom = this.dom;\r
6464         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;\r
6465     },\r
6466 \r
6467     /**\r
6468      * 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
6469      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6470      * @param {Number} value The new scroll value.\r
6471      * @return {Element} this\r
6472      */\r
6473     scrollTo : function(side, value){\r
6474         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;\r
6475         return this;\r
6476     },\r
6477 \r
6478     /**\r
6479      * Returns the current scroll position of the element.\r
6480      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}\r
6481      */\r
6482     getScroll : function(){\r
6483         var d = this.dom, \r
6484             doc = document,\r
6485             body = doc.body,\r
6486             docElement = doc.documentElement,\r
6487             l,\r
6488             t,\r
6489             ret;\r
6490 \r
6491         if(d == doc || d == body){\r
6492             if(Ext.isIE && Ext.isStrict){\r
6493                 l = docElement.scrollLeft; \r
6494                 t = docElement.scrollTop;\r
6495             }else{\r
6496                 l = window.pageXOffset;\r
6497                 t = window.pageYOffset;\r
6498             }\r
6499             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};\r
6500         }else{\r
6501             ret = {left: d.scrollLeft, top: d.scrollTop};\r
6502         }\r
6503         return ret;\r
6504     }\r
6505 });/**\r
6506  * @class Ext.Element\r
6507  */\r
6508 Ext.Element.addMethods({\r
6509     /**\r
6510      * 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
6511      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6512      * @param {Number} value The new scroll value\r
6513      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6514      * @return {Element} this\r
6515      */\r
6516     scrollTo : function(side, value, animate){\r
6517         var top = /top/i.test(side), //check if we're scrolling top or left\r
6518             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop\r
6519             me = this,\r
6520             dom = me.dom;\r
6521         if (!animate || !me.anim) {\r
6522             dom[prop] = value;\r
6523         } else {\r
6524             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},\r
6525                      me.preanim(arguments, 2), 'scroll');\r
6526         }\r
6527         return me;\r
6528     },\r
6529     \r
6530     /**\r
6531      * Scrolls this element into view within the passed container.\r
6532      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a\r
6533      * string (id), dom node, or Ext.Element.\r
6534      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)\r
6535      * @return {Ext.Element} this\r
6536      */\r
6537     scrollIntoView : function(container, hscroll){\r
6538         var c = Ext.getDom(container) || Ext.getBody().dom,\r
6539                 el = this.dom,\r
6540                 o = this.getOffsetsTo(c),\r
6541             l = o[0] + c.scrollLeft,\r
6542             t = o[1] + c.scrollTop,\r
6543             b = t + el.offsetHeight,\r
6544             r = l + el.offsetWidth,\r
6545                 ch = c.clientHeight,\r
6546                 ct = parseInt(c.scrollTop, 10),\r
6547                 cl = parseInt(c.scrollLeft, 10),\r
6548                 cb = ct + ch,\r
6549                 cr = cl + c.clientWidth;\r
6550 \r
6551         if (el.offsetHeight > ch || t < ct) {\r
6552                 c.scrollTop = t;\r
6553         } else if (b > cb){\r
6554             c.scrollTop = b-ch;\r
6555         }\r
6556         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore\r
6557 \r
6558         if(hscroll !== false){\r
6559                         if(el.offsetWidth > c.clientWidth || l < cl){\r
6560                 c.scrollLeft = l;\r
6561             }else if(r > cr){\r
6562                 c.scrollLeft = r - c.clientWidth;\r
6563             }\r
6564             c.scrollLeft = c.scrollLeft;\r
6565         }\r
6566         return this;\r
6567     },\r
6568 \r
6569     // private\r
6570     scrollChildIntoView : function(child, hscroll){\r
6571         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);\r
6572     },\r
6573     \r
6574     /**\r
6575      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is\r
6576      * within this element's scrollable range.\r
6577      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6578      * @param {Number} distance How far to scroll the element in pixels\r
6579      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6580      * @return {Boolean} Returns true if a scroll was triggered or false if the element\r
6581      * was scrolled as far as it could go.\r
6582      */\r
6583      scroll : function(direction, distance, animate){\r
6584          if(!this.isScrollable()){\r
6585              return;\r
6586          }\r
6587          var el = this.dom,\r
6588             l = el.scrollLeft, t = el.scrollTop,\r
6589             w = el.scrollWidth, h = el.scrollHeight,\r
6590             cw = el.clientWidth, ch = el.clientHeight,\r
6591             scrolled = false, v,\r
6592             hash = {\r
6593                 l: Math.min(l + distance, w-cw),\r
6594                 r: v = Math.max(l - distance, 0),\r
6595                 t: Math.max(t - distance, 0),\r
6596                 b: Math.min(t + distance, h-ch)\r
6597             };\r
6598             hash.d = hash.b;\r
6599             hash.u = hash.t;\r
6600             \r
6601          direction = direction.substr(0, 1);\r
6602          if((v = hash[direction]) > -1){\r
6603             scrolled = true;\r
6604             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));\r
6605          }\r
6606          return scrolled;\r
6607     }\r
6608 });/**\r
6609  * @class Ext.Element\r
6610  */\r
6611 /**\r
6612  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element\r
6613  * @static\r
6614  * @type Number\r
6615  */\r
6616 Ext.Element.VISIBILITY = 1;\r
6617 /**\r
6618  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element\r
6619  * @static\r
6620  * @type Number\r
6621  */\r
6622 Ext.Element.DISPLAY = 2;\r
6623 \r
6624 Ext.Element.addMethods(function(){\r
6625     var VISIBILITY = "visibility",\r
6626         DISPLAY = "display",\r
6627         HIDDEN = "hidden",\r
6628         NONE = "none",      \r
6629         ORIGINALDISPLAY = 'originalDisplay',\r
6630         VISMODE = 'visibilityMode',\r
6631         ELDISPLAY = Ext.Element.DISPLAY,\r
6632         data = Ext.Element.data,\r
6633         getDisplay = function(dom){\r
6634             var d = data(dom, ORIGINALDISPLAY);\r
6635             if(d === undefined){\r
6636                 data(dom, ORIGINALDISPLAY, d = '');\r
6637             }\r
6638             return d;\r
6639         },\r
6640         getVisMode = function(dom){\r
6641             var m = data(dom, VISMODE);\r
6642             if(m === undefined){\r
6643                 data(dom, VISMODE, m = 1)\r
6644             }\r
6645             return m;\r
6646         };\r
6647     \r
6648     return {\r
6649         /**\r
6650          * The element's default display mode  (defaults to "")\r
6651          * @type String\r
6652          */\r
6653         originalDisplay : "",\r
6654         visibilityMode : 1,\r
6655         \r
6656         /**\r
6657          * Sets the element's visibility mode. When setVisible() is called it\r
6658          * will use this to determine whether to set the visibility or the display property.\r
6659          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY\r
6660          * @return {Ext.Element} this\r
6661          */\r
6662         setVisibilityMode : function(visMode){  \r
6663             data(this.dom, VISMODE, visMode);\r
6664             return this;\r
6665         },\r
6666         \r
6667         /**\r
6668          * Perform custom animation on this element.\r
6669          * <div><ul class="mdetail-params">\r
6670          * <li><u>Animation Properties</u></li>\r
6671          * \r
6672          * <p>The Animation Control Object enables gradual transitions for any member of an\r
6673          * element's style object that takes a numeric value including but not limited to\r
6674          * these properties:</p><div><ul class="mdetail-params">\r
6675          * <li><tt>bottom, top, left, right</tt></li>\r
6676          * <li><tt>height, width</tt></li>\r
6677          * <li><tt>margin, padding</tt></li>\r
6678          * <li><tt>borderWidth</tt></li>\r
6679          * <li><tt>opacity</tt></li>\r
6680          * <li><tt>fontSize</tt></li>\r
6681          * <li><tt>lineHeight</tt></li>\r
6682          * </ul></div>\r
6683          * \r
6684          * \r
6685          * <li><u>Animation Property Attributes</u></li>\r
6686          * \r
6687          * <p>Each Animation Property is a config object with optional properties:</p>\r
6688          * <div><ul class="mdetail-params">\r
6689          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>\r
6690          * <li><tt>from</tt> : ignore current value, start from this value</li>\r
6691          * <li><tt>to</tt>*  : start at current value, go to this value</li>\r
6692          * <li><tt>unit</tt> : any allowable unit specification</li>\r
6693          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>\r
6694          * </ul></div>\r
6695          * \r
6696          * <li><u>Animation Types</u></li>\r
6697          * \r
6698          * <p>The supported animation types:</p><div><ul class="mdetail-params">\r
6699          * <li><tt>'run'</tt> : Default\r
6700          * <pre><code>\r
6701 var el = Ext.get('complexEl');\r
6702 el.animate(\r
6703     // animation control object\r
6704     {\r
6705         borderWidth: {to: 3, from: 0},\r
6706         opacity: {to: .3, from: 1},\r
6707         height: {to: 50, from: el.getHeight()},\r
6708         width: {to: 300, from: el.getWidth()},\r
6709         top  : {by: - 100, unit: 'px'},\r
6710     },\r
6711     0.35,      // animation duration\r
6712     null,      // callback\r
6713     'easeOut', // easing method\r
6714     'run'      // animation type ('run','color','motion','scroll')    \r
6715 );\r
6716          * </code></pre>\r
6717          * </li>\r
6718          * <li><tt>'color'</tt>\r
6719          * <p>Animates transition of background, text, or border colors.</p>\r
6720          * <pre><code>\r
6721 el.animate(\r
6722     // animation control object\r
6723     {\r
6724         color: { to: '#06e' },\r
6725         backgroundColor: { to: '#e06' }\r
6726     },\r
6727     0.35,      // animation duration\r
6728     null,      // callback\r
6729     'easeOut', // easing method\r
6730     'color'    // animation type ('run','color','motion','scroll')    \r
6731 );\r
6732          * </code></pre> \r
6733          * </li>\r
6734          * \r
6735          * <li><tt>'motion'</tt>\r
6736          * <p>Animates the motion of an element to/from specific points using optional bezier\r
6737          * way points during transit.</p>\r
6738          * <pre><code>\r
6739 el.animate(\r
6740     // animation control object\r
6741     {\r
6742         borderWidth: {to: 3, from: 0},\r
6743         opacity: {to: .3, from: 1},\r
6744         height: {to: 50, from: el.getHeight()},\r
6745         width: {to: 300, from: el.getWidth()},\r
6746         top  : {by: - 100, unit: 'px'},\r
6747         points: {\r
6748             to: [50, 100],  // go to this point\r
6749             control: [      // optional bezier way points\r
6750                 [ 600, 800],\r
6751                 [-100, 200]\r
6752             ]\r
6753         }\r
6754     },\r
6755     3000,      // animation duration (milliseconds!)\r
6756     null,      // callback\r
6757     'easeOut', // easing method\r
6758     'motion'   // animation type ('run','color','motion','scroll')    \r
6759 );\r
6760          * </code></pre> \r
6761          * </li>\r
6762          * <li><tt>'scroll'</tt>\r
6763          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>\r
6764          * <pre><code>\r
6765 el.animate(\r
6766     // animation control object\r
6767     {\r
6768         scroll: {to: [400, 300]}\r
6769     },\r
6770     0.35,      // animation duration\r
6771     null,      // callback\r
6772     'easeOut', // easing method\r
6773     'scroll'   // animation type ('run','color','motion','scroll')    \r
6774 );\r
6775          * </code></pre> \r
6776          * </li>\r
6777          * </ul></div>\r
6778          * \r
6779          * </ul></div>\r
6780          * \r
6781          * @param {Object} args The animation control args\r
6782          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)\r
6783          * @param {Function} onComplete (optional) Function to call when animation completes\r
6784          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)\r
6785          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,\r
6786          * <tt>'motion'</tt>, or <tt>'scroll'</tt>\r
6787          * @return {Ext.Element} this\r
6788          */\r
6789         animate : function(args, duration, onComplete, easing, animType){       \r
6790             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);\r
6791             return this;\r
6792         },\r
6793     \r
6794         /*\r
6795          * @private Internal animation call\r
6796          */\r
6797         anim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
6798             animType = animType || 'run';\r
6799             opt = opt || {};\r
6800             var me = this,              \r
6801                 anim = Ext.lib.Anim[animType](\r
6802                     me.dom, \r
6803                     args,\r
6804                     (opt.duration || defaultDur) || .35,\r
6805                     (opt.easing || defaultEase) || 'easeOut',\r
6806                     function(){\r
6807                         if(cb) cb.call(me);\r
6808                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);\r
6809                     },\r
6810                     me\r
6811                 );\r
6812             opt.anim = anim;\r
6813             return anim;\r
6814         },\r
6815     \r
6816         // private legacy anim prep\r
6817         preanim : function(a, i){\r
6818             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});\r
6819         },\r
6820         \r
6821         /**\r
6822          * Checks whether the element is currently visible using both visibility and display properties.         \r
6823          * @return {Boolean} True if the element is currently visible, else false\r
6824          */\r
6825         isVisible : function() {\r
6826             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);\r
6827         },\r
6828         \r
6829         /**\r
6830          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use\r
6831          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.\r
6832          * @param {Boolean} visible Whether the element is visible\r
6833          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6834          * @return {Ext.Element} this\r
6835          */\r
6836          setVisible : function(visible, animate){\r
6837             var me = this,\r
6838                 dom = me.dom,\r
6839                 isDisplay = getVisMode(this.dom) == ELDISPLAY;\r
6840                 \r
6841             if (!animate || !me.anim) {\r
6842                 if(isDisplay){\r
6843                     me.setDisplayed(visible);\r
6844                 }else{\r
6845                     me.fixDisplay();\r
6846                     dom.style.visibility = visible ? "visible" : HIDDEN;\r
6847                 }\r
6848             }else{\r
6849                 // closure for composites            \r
6850                 if(visible){\r
6851                     me.setOpacity(.01);\r
6852                     me.setVisible(true);\r
6853                 }\r
6854                 me.anim({opacity: { to: (visible?1:0) }},\r
6855                         me.preanim(arguments, 1),\r
6856                         null,\r
6857                         .35,\r
6858                         'easeIn',\r
6859                         function(){\r
6860                              if(!visible){\r
6861                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;                     \r
6862                                  Ext.fly(dom).setOpacity(1);\r
6863                              }\r
6864                         });\r
6865             }\r
6866             return me;\r
6867         },\r
6868     \r
6869         /**\r
6870          * Toggles the element's visibility or display, depending on visibility mode.\r
6871          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6872          * @return {Ext.Element} this\r
6873          */\r
6874         toggle : function(animate){\r
6875             var me = this;\r
6876             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));\r
6877             return me;\r
6878         },\r
6879     \r
6880         /**\r
6881          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.\r
6882          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.\r
6883          * @return {Ext.Element} this\r
6884          */\r
6885         setDisplayed : function(value) {            \r
6886             if(typeof value == "boolean"){\r
6887                value = value ? getDisplay(this.dom) : NONE;\r
6888             }\r
6889             this.setStyle(DISPLAY, value);\r
6890             return this;\r
6891         },\r
6892         \r
6893         // private\r
6894         fixDisplay : function(){\r
6895             var me = this;\r
6896             if(me.isStyle(DISPLAY, NONE)){\r
6897                 me.setStyle(VISIBILITY, HIDDEN);\r
6898                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default\r
6899                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block\r
6900                     me.setStyle(DISPLAY, "block");\r
6901                 }\r
6902             }\r
6903         },\r
6904     \r
6905         /**\r
6906          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
6907          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6908          * @return {Ext.Element} this\r
6909          */\r
6910         hide : function(animate){\r
6911             this.setVisible(false, this.preanim(arguments, 0));\r
6912             return this;\r
6913         },\r
6914     \r
6915         /**\r
6916         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
6917         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6918          * @return {Ext.Element} this\r
6919          */\r
6920         show : function(animate){\r
6921             this.setVisible(true, this.preanim(arguments, 0));\r
6922             return this;\r
6923         }\r
6924     }\r
6925 }());/**\r
6926  * @class Ext.Element\r
6927  */\r
6928 Ext.Element.addMethods(\r
6929 function(){\r
6930     var VISIBILITY = "visibility",\r
6931         DISPLAY = "display",\r
6932         HIDDEN = "hidden",\r
6933         NONE = "none",\r
6934             XMASKED = "x-masked",\r
6935                 XMASKEDRELATIVE = "x-masked-relative",\r
6936         data = Ext.Element.data;\r
6937                 \r
6938         return {\r
6939                 /**\r
6940              * Checks whether the element is currently visible using both visibility and display properties.\r
6941              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)\r
6942              * @return {Boolean} True if the element is currently visible, else false\r
6943              */\r
6944             isVisible : function(deep) {\r
6945                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),\r
6946                         p = this.dom.parentNode;\r
6947                 if(deep !== true || !vis){\r
6948                     return vis;\r
6949                 }               \r
6950                 while(p && !/body/i.test(p.tagName)){\r
6951                     if(!Ext.fly(p, '_isVisible').isVisible()){\r
6952                         return false;\r
6953                     }\r
6954                     p = p.parentNode;\r
6955                 }\r
6956                 return true;\r
6957             },\r
6958             \r
6959             /**\r
6960              * Returns true if display is not "none"\r
6961              * @return {Boolean}\r
6962              */\r
6963             isDisplayed : function() {\r
6964                 return !this.isStyle(DISPLAY, NONE);\r
6965             },\r
6966             \r
6967                 /**\r
6968              * Convenience method for setVisibilityMode(Element.DISPLAY)\r
6969              * @param {String} display (optional) What to set display to when visible\r
6970              * @return {Ext.Element} this\r
6971              */\r
6972             enableDisplayMode : function(display){          \r
6973                 this.setVisibilityMode(Ext.Element.DISPLAY);\r
6974                 if(!Ext.isEmpty(display)){\r
6975                 data(this.dom, 'originalDisplay', display);\r
6976             }\r
6977                 return this;\r
6978             },\r
6979             \r
6980                 /**\r
6981              * Puts a mask over this element to disable user interaction. Requires core.css.\r
6982              * This method can only be applied to elements which accept child nodes.\r
6983              * @param {String} msg (optional) A message to display in the mask\r
6984              * @param {String} msgCls (optional) A css class to apply to the msg element\r
6985              * @return {Element} The mask element\r
6986              */\r
6987             mask : function(msg, msgCls){\r
6988                     var me = this,\r
6989                         dom = me.dom,\r
6990                         dh = Ext.DomHelper,\r
6991                         EXTELMASKMSG = "ext-el-mask-msg",\r
6992                 el, \r
6993                 mask;\r
6994                         \r
6995                 if(me.getStyle("position") == "static"){\r
6996                     me.addClass(XMASKEDRELATIVE);\r
6997                 }\r
6998                 if((el = data(dom, 'maskMsg'))){\r
6999                     el.remove();\r
7000                 }\r
7001                 if((el = data(dom, 'mask'))){\r
7002                     el.remove();\r
7003                 }\r
7004         \r
7005             mask = dh.append(dom, {cls : "ext-el-mask"}, true);\r
7006                 data(dom, 'mask', mask);\r
7007         \r
7008                 me.addClass(XMASKED);\r
7009                 mask.setDisplayed(true);\r
7010                 if(typeof msg == 'string'){\r
7011                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);\r
7012                 data(dom, 'maskMsg', mm);\r
7013                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;\r
7014                     mm.dom.firstChild.innerHTML = msg;\r
7015                     mm.setDisplayed(true);\r
7016                     mm.center(me);\r
7017                 }\r
7018                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically\r
7019                     mask.setSize(undefined, me.getHeight());\r
7020                 }\r
7021                 return mask;\r
7022             },\r
7023         \r
7024             /**\r
7025              * Removes a previously applied mask.\r
7026              */\r
7027             unmask : function(){\r
7028                     var me = this,\r
7029                 dom = me.dom,\r
7030                         mask = data(dom, 'mask'),\r
7031                         maskMsg = data(dom, 'maskMsg');\r
7032                 if(mask){\r
7033                     if(maskMsg){\r
7034                         maskMsg.remove();\r
7035                     data(dom, 'maskMsg', undefined);\r
7036                     }\r
7037                     mask.remove();\r
7038                 data(dom, 'mask', undefined);\r
7039                 }\r
7040                 me.removeClass([XMASKED, XMASKEDRELATIVE]);\r
7041             },\r
7042         \r
7043             /**\r
7044              * Returns true if this element is masked\r
7045              * @return {Boolean}\r
7046              */\r
7047             isMasked : function(){\r
7048             var m = data(this.dom, 'mask');\r
7049                 return m && m.isVisible();\r
7050             },\r
7051             \r
7052             /**\r
7053              * Creates an iframe shim for this element to keep selects and other windowed objects from\r
7054              * showing through.\r
7055              * @return {Ext.Element} The new shim element\r
7056              */\r
7057             createShim : function(){\r
7058                 var el = document.createElement('iframe'),              \r
7059                         shim;\r
7060                 el.frameBorder = '0';\r
7061                 el.className = 'ext-shim';\r
7062                 el.src = Ext.SSL_SECURE_URL;\r
7063                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));\r
7064                 shim.autoBoxAdjust = false;\r
7065                 return shim;\r
7066             }\r
7067     };\r
7068 }());/**\r
7069  * @class Ext.Element\r
7070  */\r
7071 Ext.Element.addMethods({\r
7072     /**\r
7073      * Convenience method for constructing a KeyMap\r
7074      * @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
7075      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
7076      * @param {Function} fn The function to call\r
7077      * @param {Object} scope (optional) The scope of the function\r
7078      * @return {Ext.KeyMap} The KeyMap created\r
7079      */\r
7080     addKeyListener : function(key, fn, scope){\r
7081         var config;\r
7082         if(!Ext.isObject(key) || Ext.isArray(key)){\r
7083             config = {\r
7084                 key: key,\r
7085                 fn: fn,\r
7086                 scope: scope\r
7087             };\r
7088         }else{\r
7089             config = {\r
7090                 key : key.key,\r
7091                 shift : key.shift,\r
7092                 ctrl : key.ctrl,\r
7093                 alt : key.alt,\r
7094                 fn: fn,\r
7095                 scope: scope\r
7096             };\r
7097         }\r
7098         return new Ext.KeyMap(this, config);\r
7099     },\r
7100 \r
7101     /**\r
7102      * Creates a KeyMap for this element\r
7103      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details\r
7104      * @return {Ext.KeyMap} The KeyMap created\r
7105      */\r
7106     addKeyMap : function(config){\r
7107         return new Ext.KeyMap(this, config);\r
7108     }\r
7109 });(function(){\r
7110     // contants\r
7111     var NULL = null,\r
7112         UNDEFINED = undefined,\r
7113         TRUE = true,\r
7114         FALSE = false,\r
7115         SETX = "setX",\r
7116         SETY = "setY",\r
7117         SETXY = "setXY",\r
7118         LEFT = "left",\r
7119         BOTTOM = "bottom",\r
7120         TOP = "top",\r
7121         RIGHT = "right",\r
7122         HEIGHT = "height",\r
7123         WIDTH = "width",\r
7124         POINTS = "points",\r
7125         HIDDEN = "hidden",\r
7126         ABSOLUTE = "absolute",\r
7127         VISIBLE = "visible",\r
7128         MOTION = "motion",\r
7129         POSITION = "position",\r
7130         EASEOUT = "easeOut",\r
7131         /*\r
7132          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element\r
7133          */\r
7134         flyEl = new Ext.Element.Flyweight(),\r
7135         queues = {},\r
7136         getObject = function(o){\r
7137             return o || {};\r
7138         },\r
7139         fly = function(dom){\r
7140             flyEl.dom = dom;\r
7141             flyEl.id = Ext.id(dom);\r
7142             return flyEl;\r
7143         },\r
7144         /*\r
7145          * Queueing now stored outside of the element due to closure issues\r
7146          */\r
7147         getQueue = function(id){\r
7148             if(!queues[id]){\r
7149                 queues[id] = [];\r
7150             }\r
7151             return queues[id];\r
7152         },\r
7153         setQueue = function(id, value){\r
7154             queues[id] = value;\r
7155         };\r
7156         \r
7157 //Notifies Element that fx methods are available\r
7158 Ext.enableFx = TRUE;\r
7159 \r
7160 /**\r
7161  * @class Ext.Fx\r
7162  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied\r
7163  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.\r
7164  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be\r
7165  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>\r
7166  * \r
7167  * <p><b><u>Method Chaining</u></b></p>\r
7168  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that\r
7169  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single\r
7170  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.\r
7171  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,\r
7172  * 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
7173  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>\r
7174  *\r
7175  * <p><b><u>Anchor Options for Motion Effects</u></b></p>\r
7176  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element\r
7177  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>\r
7178 <pre>\r
7179 Value  Description\r
7180 -----  -----------------------------\r
7181 tl     The top left corner\r
7182 t      The center of the top edge\r
7183 tr     The top right corner\r
7184 l      The center of the left edge\r
7185 r      The center of the right edge\r
7186 bl     The bottom left corner\r
7187 b      The center of the bottom edge\r
7188 br     The bottom right corner\r
7189 </pre>\r
7190  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options\r
7191  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>\r
7192  * \r
7193  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the\r
7194  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together\r
7195  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>\r
7196  * el.slideIn().highlight();\r
7197  * </code></pre>\r
7198  * The callback is intended for any additional code that should run once a particular effect has completed. The Element\r
7199  * being operated upon is passed as the first parameter.\r
7200  * \r
7201  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.\r
7202  * \r
7203  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>\r
7204  * <li><b><tt>backBoth</tt></b></li>\r
7205  * <li><b><tt>backIn</tt></b></li>\r
7206  * <li><b><tt>backOut</tt></b></li>\r
7207  * <li><b><tt>bounceBoth</tt></b></li>\r
7208  * <li><b><tt>bounceIn</tt></b></li>\r
7209  * <li><b><tt>bounceOut</tt></b></li>\r
7210  * <li><b><tt>easeBoth</tt></b></li>\r
7211  * <li><b><tt>easeBothStrong</tt></b></li>\r
7212  * <li><b><tt>easeIn</tt></b></li>\r
7213  * <li><b><tt>easeInStrong</tt></b></li>\r
7214  * <li><b><tt>easeNone</tt></b></li>\r
7215  * <li><b><tt>easeOut</tt></b></li>\r
7216  * <li><b><tt>easeOutStrong</tt></b></li>\r
7217  * <li><b><tt>elasticBoth</tt></b></li>\r
7218  * <li><b><tt>elasticIn</tt></b></li>\r
7219  * <li><b><tt>elasticOut</tt></b></li>\r
7220  * </ul></div>\r
7221  *\r
7222  * @cfg {String} afterCls A css class to apply after the effect\r
7223  * @cfg {Number} duration The length of time (in seconds) that the effect should last\r
7224  * \r
7225  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between\r
7226  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.\r
7227  *  \r
7228  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes\r
7229  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to \r
7230  * effects that end with the element being visually hidden, ignored otherwise)\r
7231  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object\r
7232  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the\r
7233  * Element after the effect finishes.\r
7234  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs\r
7235  * @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
7236  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)\r
7237  */\r
7238 Ext.Fx = {\r
7239     \r
7240     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.\r
7241     //           this is useful for replacing switch statements (for example).\r
7242     switchStatements : function(key, fn, argHash){\r
7243         return fn.apply(this, argHash[key]);\r
7244     },\r
7245     \r
7246     /**\r
7247      * Slides the element into view.  An anchor point can be optionally passed to set the point of\r
7248      * origin for the slide effect.  This function automatically handles wrapping the element with\r
7249      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7250      * Usage:\r
7251      *<pre><code>\r
7252 // default: slide the element in from the top\r
7253 el.slideIn();\r
7254 \r
7255 // custom: slide the element in from the right with a 2-second duration\r
7256 el.slideIn('r', { duration: 2 });\r
7257 \r
7258 // common config options shown with default values\r
7259 el.slideIn('t', {\r
7260     easing: 'easeOut',\r
7261     duration: .5\r
7262 });\r
7263 </code></pre>\r
7264      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7265      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7266      * @return {Ext.Element} The Element\r
7267      */\r
7268     slideIn : function(anchor, o){ \r
7269         o = getObject(o);\r
7270         var me = this,\r
7271             dom = me.dom,\r
7272             st = dom.style,\r
7273             xy,\r
7274             r,\r
7275             b,              \r
7276             wrap,               \r
7277             after,\r
7278             st,\r
7279             args, \r
7280             pt,\r
7281             bw,\r
7282             bh;\r
7283             \r
7284         anchor = anchor || "t";\r
7285 \r
7286         me.queueFx(o, function(){            \r
7287             xy = fly(dom).getXY();\r
7288             // fix display to visibility\r
7289             fly(dom).fixDisplay();            \r
7290             \r
7291             // restore values after effect\r
7292             r = fly(dom).getFxRestore();      \r
7293             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7294             b.right = b.x + b.width;\r
7295             b.bottom = b.y + b.height;\r
7296             \r
7297             // fixed size for slide\r
7298             fly(dom).setWidth(b.width).setHeight(b.height);            \r
7299             \r
7300             // wrap if needed\r
7301             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);\r
7302             \r
7303             st.visibility = VISIBLE;\r
7304             st.position = ABSOLUTE;\r
7305             \r
7306             // clear out temp styles after slide and unwrap\r
7307             function after(){\r
7308                  fly(dom).fxUnwrap(wrap, r.pos, o);\r
7309                  st.width = r.width;\r
7310                  st.height = r.height;\r
7311                  fly(dom).afterFx(o);\r
7312             }\r
7313             \r
7314             // time to calculate the positions        \r
7315             pt = {to: [b.x, b.y]}; \r
7316             bw = {to: b.width};\r
7317             bh = {to: b.height};\r
7318                 \r
7319             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    \r
7320                 var ret = {};\r
7321                 fly(wrap).setWidth(ww).setHeight(wh);\r
7322                 if(fly(wrap)[sXY]){\r
7323                     fly(wrap)[sXY](sXYval);                  \r
7324                 }\r
7325                 style[s1] = style[s2] = "0";                    \r
7326                 if(w){\r
7327                     ret.width = w\r
7328                 };\r
7329                 if(h){\r
7330                     ret.height = h;\r
7331                 }\r
7332                 if(p){\r
7333                     ret.points = p;\r
7334                 }\r
7335                 return ret;\r
7336             };\r
7337 \r
7338             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7339                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],\r
7340                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],\r
7341                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],\r
7342                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],\r
7343                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],\r
7344                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],\r
7345                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],\r
7346                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]\r
7347                 });\r
7348             \r
7349             st.visibility = VISIBLE;\r
7350             fly(wrap).show();\r
7351 \r
7352             arguments.callee.anim = fly(wrap).fxanim(args,\r
7353                 o,\r
7354                 MOTION,\r
7355                 .5,\r
7356                 EASEOUT, \r
7357                 after);\r
7358         });\r
7359         return me;\r
7360     },\r
7361     \r
7362     /**\r
7363      * Slides the element out of view.  An anchor point can be optionally passed to set the end point\r
7364      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = \r
7365      * 'hidden') but block elements will still take up space in the document.  The element must be removed\r
7366      * from the DOM using the 'remove' config option if desired.  This function automatically handles \r
7367      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7368      * Usage:\r
7369      *<pre><code>\r
7370 // default: slide the element out to the top\r
7371 el.slideOut();\r
7372 \r
7373 // custom: slide the element out to the right with a 2-second duration\r
7374 el.slideOut('r', { duration: 2 });\r
7375 \r
7376 // common config options shown with default values\r
7377 el.slideOut('t', {\r
7378     easing: 'easeOut',\r
7379     duration: .5,\r
7380     remove: false,\r
7381     useDisplay: false\r
7382 });\r
7383 </code></pre>\r
7384      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7385      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7386      * @return {Ext.Element} The Element\r
7387      */\r
7388     slideOut : function(anchor, o){\r
7389         o = getObject(o);\r
7390         var me = this,\r
7391             dom = me.dom,\r
7392             st = dom.style,\r
7393             xy = me.getXY(),\r
7394             wrap,\r
7395             r,\r
7396             b,\r
7397             a,\r
7398             zero = {to: 0}; \r
7399                     \r
7400         anchor = anchor || "t";\r
7401 \r
7402         me.queueFx(o, function(){\r
7403             \r
7404             // restore values after effect\r
7405             r = fly(dom).getFxRestore(); \r
7406             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7407             b.right = b.x + b.width;\r
7408             b.bottom = b.y + b.height;\r
7409                 \r
7410             // fixed size for slide   \r
7411             fly(dom).setWidth(b.width).setHeight(b.height);\r
7412 \r
7413             // wrap if needed\r
7414             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);\r
7415                 \r
7416             st.visibility = VISIBLE;\r
7417             st.position = ABSOLUTE;\r
7418             fly(wrap).setWidth(b.width).setHeight(b.height);            \r
7419 \r
7420             function after(){\r
7421                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                \r
7422                 fly(dom).fxUnwrap(wrap, r.pos, o);\r
7423                 st.width = r.width;\r
7424                 st.height = r.height;\r
7425                 fly(dom).afterFx(o);\r
7426             }            \r
7427             \r
7428             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    \r
7429                 var ret = {};\r
7430                 \r
7431                 style[s1] = style[s2] = "0";\r
7432                 ret[p1] = v1;               \r
7433                 if(p2){\r
7434                     ret[p2] = v2;               \r
7435                 }\r
7436                 if(p3){\r
7437                     ret[p3] = v3;\r
7438                 }\r
7439                 \r
7440                 return ret;\r
7441             };\r
7442             \r
7443             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7444                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],\r
7445                 l  : [st, RIGHT, TOP, WIDTH, zero],\r
7446                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],\r
7447                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7448                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],\r
7449                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7450                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],\r
7451                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]\r
7452             });\r
7453             \r
7454             arguments.callee.anim = fly(wrap).fxanim(a,\r
7455                 o,\r
7456                 MOTION,\r
7457                 .5,\r
7458                 EASEOUT, \r
7459                 after);\r
7460         });\r
7461         return me;\r
7462     },\r
7463 \r
7464     /**\r
7465      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the \r
7466      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. \r
7467      * The element must be removed from the DOM using the 'remove' config option if desired.\r
7468      * Usage:\r
7469      *<pre><code>\r
7470 // default\r
7471 el.puff();\r
7472 \r
7473 // common config options shown with default values\r
7474 el.puff({\r
7475     easing: 'easeOut',\r
7476     duration: .5,\r
7477     remove: false,\r
7478     useDisplay: false\r
7479 });\r
7480 </code></pre>\r
7481      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7482      * @return {Ext.Element} The Element\r
7483      */\r
7484     puff : function(o){\r
7485         o = getObject(o);\r
7486         var me = this,\r
7487             dom = me.dom,\r
7488             st = dom.style,\r
7489             width,\r
7490             height,\r
7491             r;\r
7492 \r
7493         me.queueFx(o, function(){\r
7494             width = fly(dom).getWidth();\r
7495             height = fly(dom).getHeight();\r
7496             fly(dom).clearOpacity();\r
7497             fly(dom).show();\r
7498 \r
7499             // restore values after effect\r
7500             r = fly(dom).getFxRestore();                   \r
7501             \r
7502             function after(){\r
7503                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  \r
7504                 fly(dom).clearOpacity();  \r
7505                 fly(dom).setPositioning(r.pos);\r
7506                 st.width = r.width;\r
7507                 st.height = r.height;\r
7508                 st.fontSize = '';\r
7509                 fly(dom).afterFx(o);\r
7510             }   \r
7511 \r
7512             arguments.callee.anim = fly(dom).fxanim({\r
7513                     width : {to : fly(dom).adjustWidth(width * 2)},\r
7514                     height : {to : fly(dom).adjustHeight(height * 2)},\r
7515                     points : {by : [-width * .5, -height * .5]},\r
7516                     opacity : {to : 0},\r
7517                     fontSize: {to : 200, unit: "%"}\r
7518                 },\r
7519                 o,\r
7520                 MOTION,\r
7521                 .5,\r
7522                 EASEOUT,\r
7523                  after);\r
7524         });\r
7525         return me;\r
7526     },\r
7527 \r
7528     /**\r
7529      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).\r
7530      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still \r
7531      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.\r
7532      * Usage:\r
7533      *<pre><code>\r
7534 // default\r
7535 el.switchOff();\r
7536 \r
7537 // all config options shown with default values\r
7538 el.switchOff({\r
7539     easing: 'easeIn',\r
7540     duration: .3,\r
7541     remove: false,\r
7542     useDisplay: false\r
7543 });\r
7544 </code></pre>\r
7545      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7546      * @return {Ext.Element} The Element\r
7547      */\r
7548     switchOff : function(o){\r
7549         o = getObject(o);\r
7550         var me = this,\r
7551             dom = me.dom,\r
7552             st = dom.style,\r
7553             r;\r
7554 \r
7555         me.queueFx(o, function(){\r
7556             fly(dom).clearOpacity();\r
7557             fly(dom).clip();\r
7558 \r
7559             // restore values after effect\r
7560             r = fly(dom).getFxRestore();\r
7561                 \r
7562             function after(){\r
7563                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  \r
7564                 fly(dom).clearOpacity();\r
7565                 fly(dom).setPositioning(r.pos);\r
7566                 st.width = r.width;\r
7567                 st.height = r.height;   \r
7568                 fly(dom).afterFx(o);\r
7569             };\r
7570 \r
7571             fly(dom).fxanim({opacity : {to : 0.3}}, \r
7572                 NULL, \r
7573                 NULL, \r
7574                 .1, \r
7575                 NULL, \r
7576                 function(){                                 \r
7577                     fly(dom).clearOpacity();\r
7578                         (function(){                            \r
7579                             fly(dom).fxanim({\r
7580                                 height : {to : 1},\r
7581                                 points : {by : [0, fly(dom).getHeight() * .5]}\r
7582                             }, \r
7583                             o, \r
7584                             MOTION, \r
7585                             0.3, \r
7586                             'easeIn', \r
7587                             after);\r
7588                         }).defer(100);\r
7589                 });\r
7590         });\r
7591         return me;\r
7592     },\r
7593 \r
7594     /**\r
7595      * Highlights the Element by setting a color (applies to the background-color by default, but can be\r
7596      * changed using the "attr" config option) and then fading back to the original color. If no original\r
7597      * color is available, you should provide the "endColor" config option which will be cleared after the animation.\r
7598      * Usage:\r
7599 <pre><code>\r
7600 // default: highlight background to yellow\r
7601 el.highlight();\r
7602 \r
7603 // custom: highlight foreground text to blue for 2 seconds\r
7604 el.highlight("0000ff", { attr: 'color', duration: 2 });\r
7605 \r
7606 // common config options shown with default values\r
7607 el.highlight("ffff9c", {\r
7608     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value\r
7609     endColor: (current color) or "ffffff",\r
7610     easing: 'easeIn',\r
7611     duration: 1\r
7612 });\r
7613 </code></pre>\r
7614      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')\r
7615      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7616      * @return {Ext.Element} The Element\r
7617      */ \r
7618     highlight : function(color, o){\r
7619         o = getObject(o);\r
7620         var me = this,\r
7621             dom = me.dom,\r
7622             attr = o.attr || "backgroundColor",\r
7623             a = {},\r
7624             restore;\r
7625 \r
7626         me.queueFx(o, function(){\r
7627             fly(dom).clearOpacity();\r
7628             fly(dom).show();\r
7629 \r
7630             function after(){\r
7631                 dom.style[attr] = restore;\r
7632                 fly(dom).afterFx(o);\r
7633             }            \r
7634             restore = dom.style[attr];\r
7635             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};\r
7636             arguments.callee.anim = fly(dom).fxanim(a,\r
7637                 o,\r
7638                 'color',\r
7639                 1,\r
7640                 'easeIn', \r
7641                 after);\r
7642         });\r
7643         return me;\r
7644     },\r
7645 \r
7646    /**\r
7647     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.\r
7648     * Usage:\r
7649 <pre><code>\r
7650 // default: a single light blue ripple\r
7651 el.frame();\r
7652 \r
7653 // custom: 3 red ripples lasting 3 seconds total\r
7654 el.frame("ff0000", 3, { duration: 3 });\r
7655 \r
7656 // common config options shown with default values\r
7657 el.frame("C3DAF9", 1, {\r
7658     duration: 1 //duration of each individual ripple.\r
7659     // Note: Easing is not configurable and will be ignored if included\r
7660 });\r
7661 </code></pre>\r
7662     * @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
7663     * @param {Number} count (optional) The number of ripples to display (defaults to 1)\r
7664     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7665     * @return {Ext.Element} The Element\r
7666     */\r
7667     frame : function(color, count, o){\r
7668         o = getObject(o);\r
7669         var me = this,\r
7670             dom = me.dom,\r
7671             proxy,\r
7672             active;\r
7673 \r
7674         me.queueFx(o, function(){\r
7675             color = color || '#C3DAF9'\r
7676             if(color.length == 6){\r
7677                 color = '#' + color;\r
7678             }            \r
7679             count = count || 1;\r
7680             fly(dom).show();\r
7681 \r
7682             var xy = fly(dom).getXY(),\r
7683                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},\r
7684                 queue = function(){\r
7685                     proxy = fly(document.body || document.documentElement).createChild({\r
7686                         style:{\r
7687                             position : ABSOLUTE,\r
7688                             'z-index': 35000, // yee haw\r
7689                             border : '0px solid ' + color\r
7690                         }\r
7691                     });\r
7692                     return proxy.queueFx({}, animFn);\r
7693                 };\r
7694             \r
7695             \r
7696             arguments.callee.anim = {\r
7697                 isAnimated: true,\r
7698                 stop: function() {\r
7699                     count = 0;\r
7700                     proxy.stopFx();\r
7701                 }\r
7702             };\r
7703             \r
7704             function animFn(){\r
7705                 var scale = Ext.isBorderBox ? 2 : 1;\r
7706                 active = proxy.anim({\r
7707                     top : {from : b.y, to : b.y - 20},\r
7708                     left : {from : b.x, to : b.x - 20},\r
7709                     borderWidth : {from : 0, to : 10},\r
7710                     opacity : {from : 1, to : 0},\r
7711                     height : {from : b.height, to : b.height + 20 * scale},\r
7712                     width : {from : b.width, to : b.width + 20 * scale}\r
7713                 },{\r
7714                     duration: o.duration || 1,\r
7715                     callback: function() {\r
7716                         proxy.remove();\r
7717                         --count > 0 ? queue() : fly(dom).afterFx(o);\r
7718                     }\r
7719                 });\r
7720                 arguments.callee.anim = {\r
7721                     isAnimated: true,\r
7722                     stop: function(){\r
7723                         active.stop();\r
7724                     }\r
7725                 };\r
7726             };\r
7727             queue();\r
7728         });\r
7729         return me;\r
7730     },\r
7731 \r
7732    /**\r
7733     * Creates a pause before any subsequent queued effects begin.  If there are\r
7734     * no effects queued after the pause it will have no effect.\r
7735     * Usage:\r
7736 <pre><code>\r
7737 el.pause(1);\r
7738 </code></pre>\r
7739     * @param {Number} seconds The length of time to pause (in seconds)\r
7740     * @return {Ext.Element} The Element\r
7741     */\r
7742     pause : function(seconds){        \r
7743         var dom = this.dom,\r
7744             t;\r
7745 \r
7746         this.queueFx({}, function(){\r
7747             t = setTimeout(function(){\r
7748                 fly(dom).afterFx({});\r
7749             }, seconds * 1000);\r
7750             arguments.callee.anim = {\r
7751                 isAnimated: true,\r
7752                 stop: function(){\r
7753                     clearTimeout(t);\r
7754                     fly(dom).afterFx({});\r
7755                 }\r
7756             };\r
7757         });\r
7758         return this;\r
7759     },\r
7760 \r
7761    /**\r
7762     * Fade an element in (from transparent to opaque).  The ending opacity can be specified\r
7763     * using the <tt>{@link #endOpacity}</tt> config option.\r
7764     * Usage:\r
7765 <pre><code>\r
7766 // default: fade in from opacity 0 to 100%\r
7767 el.fadeIn();\r
7768 \r
7769 // custom: fade in from opacity 0 to 75% over 2 seconds\r
7770 el.fadeIn({ endOpacity: .75, duration: 2});\r
7771 \r
7772 // common config options shown with default values\r
7773 el.fadeIn({\r
7774     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)\r
7775     easing: 'easeOut',\r
7776     duration: .5\r
7777 });\r
7778 </code></pre>\r
7779     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7780     * @return {Ext.Element} The Element\r
7781     */\r
7782     fadeIn : function(o){\r
7783         o = getObject(o);\r
7784         var me = this,\r
7785             dom = me.dom,\r
7786             to = o.endOpacity || 1;\r
7787         \r
7788         me.queueFx(o, function(){\r
7789             fly(dom).setOpacity(0);\r
7790             fly(dom).fixDisplay();\r
7791             dom.style.visibility = VISIBLE;\r
7792             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},\r
7793                 o, NULL, .5, EASEOUT, function(){\r
7794                 if(to == 1){\r
7795                     fly(dom).clearOpacity();\r
7796                 }\r
7797                 fly(dom).afterFx(o);\r
7798             });\r
7799         });\r
7800         return me;\r
7801     },\r
7802 \r
7803    /**\r
7804     * Fade an element out (from opaque to transparent).  The ending opacity can be specified\r
7805     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require\r
7806     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.\r
7807     * Usage:\r
7808 <pre><code>\r
7809 // default: fade out from the element's current opacity to 0\r
7810 el.fadeOut();\r
7811 \r
7812 // custom: fade out from the element's current opacity to 25% over 2 seconds\r
7813 el.fadeOut({ endOpacity: .25, duration: 2});\r
7814 \r
7815 // common config options shown with default values\r
7816 el.fadeOut({\r
7817     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)\r
7818     easing: 'easeOut',\r
7819     duration: .5,\r
7820     remove: false,\r
7821     useDisplay: false\r
7822 });\r
7823 </code></pre>\r
7824     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7825     * @return {Ext.Element} The Element\r
7826     */\r
7827     fadeOut : function(o){\r
7828         o = getObject(o);\r
7829         var me = this,\r
7830             dom = me.dom,\r
7831             style = dom.style,\r
7832             to = o.endOpacity || 0;         \r
7833         \r
7834         me.queueFx(o, function(){  \r
7835             arguments.callee.anim = fly(dom).fxanim({ \r
7836                 opacity : {to : to}},\r
7837                 o, \r
7838                 NULL, \r
7839                 .5, \r
7840                 EASEOUT, \r
7841                 function(){\r
7842                     if(to == 0){\r
7843                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? \r
7844                             style.display = "none" :\r
7845                             style.visibility = HIDDEN;\r
7846                             \r
7847                         fly(dom).clearOpacity();\r
7848                     }\r
7849                     fly(dom).afterFx(o);\r
7850             });\r
7851         });\r
7852         return me;\r
7853     },\r
7854 \r
7855    /**\r
7856     * Animates the transition of an element's dimensions from a starting height/width\r
7857     * to an ending height/width.  This method is a convenience implementation of {@link shift}.\r
7858     * Usage:\r
7859 <pre><code>\r
7860 // change height and width to 100x100 pixels\r
7861 el.scale(100, 100);\r
7862 \r
7863 // common config options shown with default values.  The height and width will default to\r
7864 // the element&#39;s existing values if passed as null.\r
7865 el.scale(\r
7866     [element&#39;s width],\r
7867     [element&#39;s height], {\r
7868         easing: 'easeOut',\r
7869         duration: .35\r
7870     }\r
7871 );\r
7872 </code></pre>\r
7873     * @param {Number} width  The new width (pass undefined to keep the original width)\r
7874     * @param {Number} height  The new height (pass undefined to keep the original height)\r
7875     * @param {Object} options (optional) Object literal with any of the Fx config options\r
7876     * @return {Ext.Element} The Element\r
7877     */\r
7878     scale : function(w, h, o){\r
7879         this.shift(Ext.apply({}, o, {\r
7880             width: w,\r
7881             height: h\r
7882         }));\r
7883         return this;\r
7884     },\r
7885 \r
7886    /**\r
7887     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.\r
7888     * Any of these properties not specified in the config object will not be changed.  This effect \r
7889     * requires that at least one new dimension, position or opacity setting must be passed in on\r
7890     * the config object in order for the function to have any effect.\r
7891     * Usage:\r
7892 <pre><code>\r
7893 // slide the element horizontally to x position 200 while changing the height and opacity\r
7894 el.shift({ x: 200, height: 50, opacity: .8 });\r
7895 \r
7896 // common config options shown with default values.\r
7897 el.shift({\r
7898     width: [element&#39;s width],\r
7899     height: [element&#39;s height],\r
7900     x: [element&#39;s x position],\r
7901     y: [element&#39;s y position],\r
7902     opacity: [element&#39;s opacity],\r
7903     easing: 'easeOut',\r
7904     duration: .35\r
7905 });\r
7906 </code></pre>\r
7907     * @param {Object} options  Object literal with any of the Fx config options\r
7908     * @return {Ext.Element} The Element\r
7909     */\r
7910     shift : function(o){\r
7911         o = getObject(o);\r
7912         var dom = this.dom,\r
7913             a = {};\r
7914                 \r
7915         this.queueFx(o, function(){\r
7916             for (var prop in o) {\r
7917                 if (o[prop] != UNDEFINED) {                                                 \r
7918                     a[prop] = {to : o[prop]};                   \r
7919                 }\r
7920             } \r
7921             \r
7922             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;\r
7923             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   \r
7924             \r
7925             if (a.x || a.y || a.xy) {\r
7926                 a.points = a.xy || \r
7927                            {to : [ a.x ? a.x.to : fly(dom).getX(),\r
7928                                    a.y ? a.y.to : fly(dom).getY()]};                  \r
7929             }\r
7930 \r
7931             arguments.callee.anim = fly(dom).fxanim(a,\r
7932                 o, \r
7933                 MOTION, \r
7934                 .35, \r
7935                 EASEOUT, \r
7936                 function(){\r
7937                     fly(dom).afterFx(o);\r
7938                 });\r
7939         });\r
7940         return this;\r
7941     },\r
7942 \r
7943     /**\r
7944      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the \r
7945      * ending point of the effect.\r
7946      * Usage:\r
7947      *<pre><code>\r
7948 // default: slide the element downward while fading out\r
7949 el.ghost();\r
7950 \r
7951 // custom: slide the element out to the right with a 2-second duration\r
7952 el.ghost('r', { duration: 2 });\r
7953 \r
7954 // common config options shown with default values\r
7955 el.ghost('b', {\r
7956     easing: 'easeOut',\r
7957     duration: .5,\r
7958     remove: false,\r
7959     useDisplay: false\r
7960 });\r
7961 </code></pre>\r
7962      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')\r
7963      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7964      * @return {Ext.Element} The Element\r
7965      */\r
7966     ghost : function(anchor, o){\r
7967         o = getObject(o);\r
7968         var me = this,\r
7969             dom = me.dom,\r
7970             st = dom.style,\r
7971             a = {opacity: {to: 0}, points: {}},\r
7972             pt = a.points,\r
7973             r,\r
7974             w,\r
7975             h;\r
7976             \r
7977         anchor = anchor || "b";\r
7978 \r
7979         me.queueFx(o, function(){\r
7980             // restore values after effect\r
7981             r = fly(dom).getFxRestore();\r
7982             w = fly(dom).getWidth();\r
7983             h = fly(dom).getHeight();\r
7984             \r
7985             function after(){\r
7986                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   \r
7987                 fly(dom).clearOpacity();\r
7988                 fly(dom).setPositioning(r.pos);\r
7989                 st.width = r.width;\r
7990                 st.height = r.height;\r
7991                 fly(dom).afterFx(o);\r
7992             }\r
7993                 \r
7994             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {\r
7995                t  : [0, -h],\r
7996                l  : [-w, 0],\r
7997                r  : [w, 0],\r
7998                b  : [0, h],\r
7999                tl : [-w, -h],\r
8000                bl : [-w, h],\r
8001                br : [w, h],\r
8002                tr : [w, -h] \r
8003             });\r
8004                 \r
8005             arguments.callee.anim = fly(dom).fxanim(a,\r
8006                 o,\r
8007                 MOTION,\r
8008                 .5,\r
8009                 EASEOUT, after);\r
8010         });\r
8011         return me;\r
8012     },\r
8013 \r
8014     /**\r
8015      * Ensures that all effects queued after syncFx is called on the element are\r
8016      * run concurrently.  This is the opposite of {@link #sequenceFx}.\r
8017      * @return {Ext.Element} The Element\r
8018      */\r
8019     syncFx : function(){\r
8020         var me = this;\r
8021         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8022             block : FALSE,\r
8023             concurrent : TRUE,\r
8024             stopFx : FALSE\r
8025         });\r
8026         return me;\r
8027     },\r
8028 \r
8029     /**\r
8030      * Ensures that all effects queued after sequenceFx is called on the element are\r
8031      * run in sequence.  This is the opposite of {@link #syncFx}.\r
8032      * @return {Ext.Element} The Element\r
8033      */\r
8034     sequenceFx : function(){\r
8035         var me = this;\r
8036         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8037             block : FALSE,\r
8038             concurrent : FALSE,\r
8039             stopFx : FALSE\r
8040         });\r
8041         return me;\r
8042     },\r
8043 \r
8044     /* @private */\r
8045     nextFx : function(){        \r
8046         var ef = getQueue(this.dom.id)[0];\r
8047         if(ef){\r
8048             ef.call(this);\r
8049         }\r
8050     },\r
8051 \r
8052     /**\r
8053      * Returns true if the element has any effects actively running or queued, else returns false.\r
8054      * @return {Boolean} True if element has active effects, else false\r
8055      */\r
8056     hasActiveFx : function(){\r
8057         return getQueue(this.dom.id)[0];\r
8058     },\r
8059 \r
8060     /**\r
8061      * Stops any running effects and clears the element's internal effects queue if it contains\r
8062      * any additional effects that haven't started yet.\r
8063      * @return {Ext.Element} The Element\r
8064      */\r
8065     stopFx : function(finish){\r
8066         var me = this,\r
8067             id = me.dom.id;\r
8068         if(me.hasActiveFx()){\r
8069             var cur = getQueue(id)[0];\r
8070             if(cur && cur.anim){\r
8071                 if(cur.anim.isAnimated){\r
8072                     setQueue(id, [cur]); //clear\r
8073                     cur.anim.stop(finish !== undefined ? finish : TRUE);\r
8074                 }else{\r
8075                     setQueue(id, []);\r
8076                 }\r
8077             }\r
8078         }\r
8079         return me;\r
8080     },\r
8081 \r
8082     /* @private */\r
8083     beforeFx : function(o){\r
8084         if(this.hasActiveFx() && !o.concurrent){\r
8085            if(o.stopFx){\r
8086                this.stopFx();\r
8087                return TRUE;\r
8088            }\r
8089            return FALSE;\r
8090         }\r
8091         return TRUE;\r
8092     },\r
8093 \r
8094     /**\r
8095      * Returns true if the element is currently blocking so that no other effect can be queued\r
8096      * until this effect is finished, else returns false if blocking is not set.  This is commonly\r
8097      * used to ensure that an effect initiated by a user action runs to completion prior to the\r
8098      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).\r
8099      * @return {Boolean} True if blocking, else false\r
8100      */\r
8101     hasFxBlock : function(){\r
8102         var q = getQueue(this.dom.id);\r
8103         return q && q[0] && q[0].block;\r
8104     },\r
8105 \r
8106     /* @private */\r
8107     queueFx : function(o, fn){\r
8108         var me = fly(this.dom);\r
8109         if(!me.hasFxBlock()){\r
8110             Ext.applyIf(o, me.fxDefaults);\r
8111             if(!o.concurrent){\r
8112                 var run = me.beforeFx(o);\r
8113                 fn.block = o.block;\r
8114                 getQueue(me.dom.id).push(fn);\r
8115                 if(run){\r
8116                     me.nextFx();\r
8117                 }\r
8118             }else{\r
8119                 fn.call(me);\r
8120             }\r
8121         }\r
8122         return me;\r
8123     },\r
8124 \r
8125     /* @private */\r
8126     fxWrap : function(pos, o, vis){ \r
8127         var dom = this.dom,\r
8128             wrap,\r
8129             wrapXY;\r
8130         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            \r
8131             if(o.fixPosition){\r
8132                 wrapXY = fly(dom).getXY();\r
8133             }\r
8134             var div = document.createElement("div");\r
8135             div.style.visibility = vis;\r
8136             wrap = dom.parentNode.insertBefore(div, dom);\r
8137             fly(wrap).setPositioning(pos);\r
8138             if(fly(wrap).isStyle(POSITION, "static")){\r
8139                 fly(wrap).position("relative");\r
8140             }\r
8141             fly(dom).clearPositioning('auto');\r
8142             fly(wrap).clip();\r
8143             wrap.appendChild(dom);\r
8144             if(wrapXY){\r
8145                 fly(wrap).setXY(wrapXY);\r
8146             }\r
8147         }\r
8148         return wrap;\r
8149     },\r
8150 \r
8151     /* @private */\r
8152     fxUnwrap : function(wrap, pos, o){      \r
8153         var dom = this.dom;\r
8154         fly(dom).clearPositioning();\r
8155         fly(dom).setPositioning(pos);\r
8156         if(!o.wrap){\r
8157             var pn = fly(wrap).dom.parentNode;
8158             pn.insertBefore(dom, wrap); \r
8159             fly(wrap).remove();\r
8160         }\r
8161     },\r
8162 \r
8163     /* @private */\r
8164     getFxRestore : function(){\r
8165         var st = this.dom.style;\r
8166         return {pos: this.getPositioning(), width: st.width, height : st.height};\r
8167     },\r
8168 \r
8169     /* @private */\r
8170     afterFx : function(o){\r
8171         var dom = this.dom,\r
8172             id = dom.id;\r
8173         if(o.afterStyle){\r
8174             fly(dom).setStyle(o.afterStyle);            \r
8175         }\r
8176         if(o.afterCls){\r
8177             fly(dom).addClass(o.afterCls);\r
8178         }\r
8179         if(o.remove == TRUE){\r
8180             fly(dom).remove();\r
8181         }\r
8182         if(o.callback){\r
8183             o.callback.call(o.scope, fly(dom));\r
8184         }\r
8185         if(!o.concurrent){\r
8186             getQueue(id).shift();\r
8187             fly(dom).nextFx();\r
8188         }\r
8189     },\r
8190 \r
8191     /* @private */\r
8192     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
8193         animType = animType || 'run';\r
8194         opt = opt || {};\r
8195         var anim = Ext.lib.Anim[animType](\r
8196                 this.dom, \r
8197                 args,\r
8198                 (opt.duration || defaultDur) || .35,\r
8199                 (opt.easing || defaultEase) || EASEOUT,\r
8200                 cb,            \r
8201                 this\r
8202             );\r
8203         opt.anim = anim;\r
8204         return anim;\r
8205     }\r
8206 };\r
8207 \r
8208 // backwards compat\r
8209 Ext.Fx.resize = Ext.Fx.scale;\r
8210 \r
8211 //When included, Ext.Fx is automatically applied to Element so that all basic\r
8212 //effects are available directly via the Element API\r
8213 Ext.Element.addMethods(Ext.Fx);\r
8214 })();
8215 /**\r
8216  * @class Ext.CompositeElementLite\r
8217  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter\r
8218  * members, or to perform collective actions upon the whole set.</p>\r
8219  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and\r
8220  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>\r
8221  * Example:<pre><code>\r
8222 var els = Ext.select("#some-el div.some-class");\r
8223 // or select directly from an existing element\r
8224 var el = Ext.get('some-el');\r
8225 el.select('div.some-class');\r
8226 \r
8227 els.setWidth(100); // all elements become 100 width\r
8228 els.hide(true); // all elements fade out and hide\r
8229 // or\r
8230 els.setWidth(100).hide(true);\r
8231 </code>\r
8232  */\r
8233 Ext.CompositeElementLite = function(els, root){\r
8234     /**\r
8235      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>\r
8236      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing\r
8237      * to augment the capabilities of the CompositeElementLite class may use it when adding\r
8238      * methods to the class.</p>\r
8239      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all\r
8240      * following siblings of selected elements, the code would be</p><code><pre>\r
8241 Ext.override(Ext.CompositeElementLite, {\r
8242     nextAll: function() {\r
8243         var els = this.elements, i, l = els.length, n, r = [], ri = -1;\r
8244 \r
8245 //      Loop through all elements in this Composite, accumulating\r
8246 //      an Array of all siblings.\r
8247         for (i = 0; i < l; i++) {\r
8248             for (n = els[i].nextSibling; n; n = n.nextSibling) {\r
8249                 r[++ri] = n;\r
8250             }\r
8251         }\r
8252 \r
8253 //      Add all found siblings to this Composite\r
8254         return this.add(r);\r
8255     }\r
8256 });</pre></code>\r
8257      * @type Array\r
8258      * @property elements\r
8259      */\r
8260     this.elements = [];\r
8261     this.add(els, root);\r
8262     this.el = new Ext.Element.Flyweight();\r
8263 };\r
8264 \r
8265 Ext.CompositeElementLite.prototype = {\r
8266     isComposite: true,    \r
8267     /**\r
8268      * Returns the number of elements in this Composite.\r
8269      * @return Number\r
8270      */\r
8271     getCount : function(){\r
8272         return this.elements.length;\r
8273     },    \r
8274     /**\r
8275      * Adds elements to this Composite object.\r
8276      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.\r
8277      * @return {CompositeElement} This Composite object.\r
8278      */\r
8279     add : function(els){\r
8280         if(els){\r
8281             if (Ext.isArray(els)) {\r
8282                 this.elements = this.elements.concat(els);\r
8283             } else {\r
8284                 var yels = this.elements;                                    \r
8285                 Ext.each(els, function(e) {\r
8286                     yels.push(e);\r
8287                 });\r
8288             }\r
8289         }\r
8290         return this;\r
8291     },\r
8292     invoke : function(fn, args){\r
8293         var els = this.elements,\r
8294             el = this.el;        \r
8295         Ext.each(els, function(e) {    \r
8296             el.dom = e;\r
8297             Ext.Element.prototype[fn].apply(el, args);\r
8298         });\r
8299         return this;\r
8300     },\r
8301     /**\r
8302      * Returns a flyweight Element of the dom element object at the specified index\r
8303      * @param {Number} index\r
8304      * @return {Ext.Element}\r
8305      */\r
8306     item : function(index){\r
8307         var me = this;\r
8308         if(!me.elements[index]){\r
8309             return null;\r
8310         }\r
8311         me.el.dom = me.elements[index];\r
8312         return me.el;\r
8313     },\r
8314 \r
8315     // fixes scope with flyweight\r
8316     addListener : function(eventName, handler, scope, opt){\r
8317         Ext.each(this.elements, function(e) {\r
8318             Ext.EventManager.on(e, eventName, handler, scope || e, opt);\r
8319         });\r
8320         return this;\r
8321     },\r
8322     /**\r
8323      * <p>Calls the passed function for each element in this composite.</p>\r
8324      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>\r
8325      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.\r
8326      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a\r
8327      * a reference to the dom node, use el.dom.</b></div></li>\r
8328      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>\r
8329      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>\r
8330      * </ul>\r
8331      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)\r
8332      * @return {CompositeElement} this\r
8333      */\r
8334     each : function(fn, scope){       \r
8335         var me = this,\r
8336             el = me.el;\r
8337        \r
8338         Ext.each(me.elements, function(e,i) {    \r
8339             el.dom = e;\r
8340             return fn.call(scope || el, el, me, i);\r
8341         });\r
8342         return me;\r
8343     },\r
8344     \r
8345     /**\r
8346     * Clears this Composite and adds the elements passed.\r
8347     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.\r
8348     * @return {CompositeElement} this\r
8349     */\r
8350     fill : function(els){\r
8351         var me = this;\r
8352         me.elements = [];\r
8353         me.add(els);\r
8354         return me;\r
8355     },\r
8356     \r
8357     /**\r
8358      * Filters this composite to only elements that match the passed selector.\r
8359      * @param {String/Function} selector A string CSS selector or a comparison function.\r
8360      * The comparison function will be called with the following arguments:<ul>\r
8361      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>\r
8362      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>\r
8363      * </ul>\r
8364      * @return {CompositeElement} this\r
8365      */\r
8366     filter : function(selector){\r
8367         var els = [],\r
8368             me = this,\r
8369             fn = Ext.isFunction(selector) ? selector\r
8370                 : function(el){\r
8371                     return el.is(selector);\r
8372                 }\r
8373         me.each(function(el, self, i){\r
8374             if(fn(el, i) !== false){\r
8375                 els[els.length] = el.dom;\r
8376             }\r
8377         });\r
8378         me.fill(els);\r
8379         return me;\r
8380     },\r
8381     \r
8382     /**\r
8383      * Find the index of the passed element within the composite collection.\r
8384      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8385      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.\r
8386      */\r
8387     indexOf : function(el){\r
8388         return this.elements.indexOf(Ext.getDom(el));\r
8389     },\r
8390     \r
8391     /**\r
8392     * Replaces the specified element with the passed element.\r
8393     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8394     * to replace.\r
8395     * @param {Mixed} replacement The id of an element or the Element itself.\r
8396     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.\r
8397     * @return {CompositeElement} this\r
8398     */    \r
8399     replaceElement : function(el, replacement, domReplace){\r
8400         var index = !isNaN(el) ? el : this.indexOf(el),\r
8401             d;\r
8402         if(index > -1){\r
8403             replacement = Ext.getDom(replacement);\r
8404             if(domReplace){\r
8405                 d = this.elements[index];\r
8406                 d.parentNode.insertBefore(replacement, d);\r
8407                 Ext.removeNode(d);\r
8408             }\r
8409             this.elements.splice(index, 1, replacement);\r
8410         }\r
8411         return this;\r
8412     },\r
8413     \r
8414     /**\r
8415      * Removes all elements.\r
8416      */\r
8417     clear : function(){\r
8418         this.elements = [];\r
8419     }\r
8420 };\r
8421 \r
8422 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;\r
8423 \r
8424 (function(){\r
8425 var fnName,\r
8426     ElProto = Ext.Element.prototype,\r
8427     CelProto = Ext.CompositeElementLite.prototype;\r
8428     \r
8429 for(fnName in ElProto){\r
8430     if(Ext.isFunction(ElProto[fnName])){\r
8431         (function(fnName){ \r
8432             CelProto[fnName] = CelProto[fnName] || function(){\r
8433                 return this.invoke(fnName, arguments);\r
8434             };\r
8435         }).call(CelProto, fnName);\r
8436         \r
8437     }\r
8438 }\r
8439 })();\r
8440 \r
8441 if(Ext.DomQuery){\r
8442     Ext.Element.selectorFunction = Ext.DomQuery.select;\r
8443\r
8444 \r
8445 /**\r
8446  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8447  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8448  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8449  * @param {String/Array} selector The CSS selector or an array of elements\r
8450  * @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
8451  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8452  * @return {CompositeElementLite/CompositeElement}\r
8453  * @member Ext.Element\r
8454  * @method select\r
8455  */\r
8456 Ext.Element.select = function(selector, unique, root){\r
8457     var els;\r
8458     if(typeof selector == "string"){\r
8459         els = Ext.Element.selectorFunction(selector, root);\r
8460     }else if(selector.length !== undefined){\r
8461         els = selector;\r
8462     }else{\r
8463         throw "Invalid selector";\r
8464     }\r
8465     return new Ext.CompositeElementLite(els);\r
8466 };\r
8467 /**\r
8468  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8469  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8470  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8471  * @param {String/Array} selector The CSS selector or an array of elements\r
8472  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8473  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8474  * @return {CompositeElementLite/CompositeElement}\r
8475  * @member Ext\r
8476  * @method select\r
8477  */\r
8478 Ext.select = Ext.Element.select;/**\r
8479  * @class Ext.CompositeElementLite\r
8480  */\r
8481 Ext.apply(Ext.CompositeElementLite.prototype, { \r
8482         addElements : function(els, root){\r
8483         if(!els){\r
8484             return this;\r
8485         }\r
8486         if(typeof els == "string"){\r
8487             els = Ext.Element.selectorFunction(els, root);\r
8488         }\r
8489         var yels = this.elements;        \r
8490             Ext.each(els, function(e) {\r
8491                 yels.push(Ext.get(e));\r
8492         });\r
8493         return this;\r
8494     },\r
8495     \r
8496     /**\r
8497      * Returns the first Element\r
8498      * @return {Ext.Element}\r
8499      */\r
8500     first : function(){\r
8501         return this.item(0);\r
8502     },   \r
8503     \r
8504     /**\r
8505      * Returns the last Element\r
8506      * @return {Ext.Element}\r
8507      */\r
8508     last : function(){\r
8509         return this.item(this.getCount()-1);\r
8510     },\r
8511     \r
8512     /**\r
8513      * Returns true if this composite contains the passed element\r
8514      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8515      * @return Boolean\r
8516      */\r
8517     contains : function(el){\r
8518         return this.indexOf(el) != -1;\r
8519     },
8520     
8521     /**\r
8522     * Removes the specified element(s).\r
8523     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8524     * or an array of any of those.\r
8525     * @param {Boolean} removeDom (optional) True to also remove the element from the document\r
8526     * @return {CompositeElement} this\r
8527     */\r
8528     removeElement : function(keys, removeDom){\r
8529         var me = this,\r
8530                 els = this.elements,        \r
8531                 el;             \r
8532             Ext.each(keys, function(val){\r
8533                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {\r
8534                         if(removeDom){\r
8535                     if(el.dom){\r
8536                         el.remove();\r
8537                     }else{\r
8538                         Ext.removeNode(el);\r
8539                     }\r
8540                 }\r
8541                         els.splice(val, 1);                     \r
8542                         }\r
8543             });\r
8544         return this;\r
8545     }    \r
8546 });
8547 /**\r
8548  * @class Ext.CompositeElement\r
8549  * @extends Ext.CompositeElementLite\r
8550  * Standard composite class. Creates a Ext.Element for every element in the collection.\r
8551  * <br><br>\r
8552  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element\r
8553  * actions will be performed on all the elements in this collection.</b>\r
8554  * <br><br>\r
8555  * All methods return <i>this</i> and can be chained.\r
8556  <pre><code>\r
8557  var els = Ext.select("#some-el div.some-class", true);\r
8558  // or select directly from an existing element\r
8559  var el = Ext.get('some-el');\r
8560  el.select('div.some-class', true);\r
8561 \r
8562  els.setWidth(100); // all elements become 100 width\r
8563  els.hide(true); // all elements fade out and hide\r
8564  // or\r
8565  els.setWidth(100).hide(true);\r
8566  </code></pre>\r
8567  */\r
8568 Ext.CompositeElement = function(els, root){\r
8569     this.elements = [];\r
8570     this.add(els, root);\r
8571 };\r
8572 \r
8573 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {\r
8574     invoke : function(fn, args){\r
8575             Ext.each(this.elements, function(e) {\r
8576                 Ext.Element.prototype[fn].apply(e, args);\r
8577         });\r
8578         return this;\r
8579     },\r
8580 \r
8581     /**\r
8582     * Adds elements to this composite.\r
8583     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
8584     * @return {CompositeElement} this\r
8585     */\r
8586     add : function(els, root){\r
8587             if(!els){\r
8588             return this;\r
8589         }\r
8590         if(typeof els == "string"){\r
8591             els = Ext.Element.selectorFunction(els, root);\r
8592         }\r
8593         var yels = this.elements;\r
8594             Ext.each(els, function(e) {\r
8595                 yels.push(Ext.get(e));\r
8596         });\r
8597         return this;\r
8598     },\r
8599 \r
8600     /**\r
8601      * Returns the Element object at the specified index\r
8602      * @param {Number} index\r
8603      * @return {Ext.Element}\r
8604      */\r
8605     item : function(index){\r
8606         return this.elements[index] || null;\r
8607     },\r
8608 \r
8609 \r
8610     indexOf : function(el){\r
8611         return this.elements.indexOf(Ext.get(el));\r
8612     },\r
8613 \r
8614     filter : function(selector){\r
8615                 var me = this,\r
8616                         out = [];\r
8617 \r
8618                 Ext.each(me.elements, function(el) {\r
8619                         if(el.is(selector)){\r
8620                                 out.push(Ext.get(el));\r
8621                         }\r
8622                 });\r
8623                 me.elements = out;\r
8624                 return me;\r
8625         },\r
8626 \r
8627     /**\r
8628      * Iterates each <code>element</code> in this <code>composite</code>\r
8629      * calling the supplied function using {@link Ext#each}.\r
8630      * @param {Function} fn The function to be called with each\r
8631      * <code>element</code>. If the supplied function returns <tt>false</tt>,\r
8632      * iteration stops. This function is called with the following arguments:\r
8633      * <div class="mdetail-params"><ul>\r
8634      * <li><code>element</code> : <i>Object</i>\r
8635      * <div class="sub-desc">The element at the current <code>index</code>\r
8636      * in the <code>composite</code></div></li>\r
8637      * <li><code>composite</code> : <i>Object</i>\r
8638      * <div class="sub-desc">This composite.</div></li>\r
8639      * <li><code>index</code> : <i>Number</i>\r
8640      * <div class="sub-desc">The current index within the <code>composite</code>\r
8641      * </div></li>\r
8642      * </ul></div>\r
8643      * @param {Object} scope (optional) The scope to call the specified function.\r
8644      * Defaults to the <code>element</code> at the current <code>index</code>\r
8645      * within the composite.\r
8646      * @return {CompositeElement} this\r
8647      */\r
8648     each : function(fn, scope){\r
8649         Ext.each(this.elements, function(e, i){\r
8650             return fn.call(scope || e, e, this, i);\r
8651         }, this);\r
8652         return this;\r
8653     }\r
8654 });\r
8655 \r
8656 /**\r
8657  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8658  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8659  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8660  * @param {String/Array} selector The CSS selector or an array of elements\r
8661  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8662  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8663  * @return {CompositeElementLite/CompositeElement}\r
8664  * @member Ext.Element\r
8665  * @method select\r
8666  */\r
8667 Ext.Element.select = function(selector, unique, root){\r
8668     var els;\r
8669     if(typeof selector == "string"){\r
8670         els = Ext.Element.selectorFunction(selector, root);\r
8671     }else if(selector.length !== undefined){\r
8672         els = selector;\r
8673     }else{\r
8674         throw "Invalid selector";\r
8675     }\r
8676 \r
8677     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);\r
8678 };\r
8679 \r
8680 /**\r
8681  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8682  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8683  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8684  * @param {String/Array} selector The CSS selector or an array of elements\r
8685  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
8686  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8687  * @return {CompositeElementLite/CompositeElement}\r
8688  * @member Ext.Element\r
8689  * @method select\r
8690  */\r
8691 Ext.select = Ext.Element.select;(function(){\r
8692     var BEFOREREQUEST = "beforerequest",\r
8693         REQUESTCOMPLETE = "requestcomplete",\r
8694         REQUESTEXCEPTION = "requestexception",\r
8695         UNDEFINED = undefined,\r
8696         LOAD = 'load',\r
8697         POST = 'POST',\r
8698         GET = 'GET',\r
8699         WINDOW = window;\r
8700     \r
8701     /**\r
8702      * @class Ext.data.Connection\r
8703      * @extends Ext.util.Observable\r
8704      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made\r
8705      * either to a configured URL, or to a URL specified at request time.</p>\r
8706      * <p>Requests made by this class are asynchronous, and will return immediately. No data from\r
8707      * the server will be available to the statement immediately following the {@link #request} call.\r
8708      * To process returned data, use a\r
8709      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>\r
8710      * in the request options object,\r
8711      * or an {@link #requestcomplete event listener}.</p>\r
8712      * <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
8713      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard\r
8714      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its\r
8715      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
8716      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
8717      * but removed after the return data has been gathered.</p>\r
8718      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
8719      * server is using JSON to send the return object, then the\r
8720      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
8721      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
8722      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode\r
8723      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>\r
8724      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
8725      * is created containing a <tt>responseText</tt> property in order to conform to the\r
8726      * requirements of event handlers and callbacks.</p>\r
8727      * <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
8728      * and some server technologies (notably JEE) may require some custom processing in order to\r
8729      * retrieve parameter names and parameter values from the packet content.</p>\r
8730      * @constructor\r
8731      * @param {Object} config a configuration object.\r
8732      */\r
8733     Ext.data.Connection = function(config){    \r
8734         Ext.apply(this, config);\r
8735         this.addEvents(\r
8736             /**\r
8737              * @event beforerequest\r
8738              * Fires before a network request is made to retrieve a data object.\r
8739              * @param {Connection} conn This Connection object.\r
8740              * @param {Object} options The options config object passed to the {@link #request} method.\r
8741              */\r
8742             BEFOREREQUEST,\r
8743             /**\r
8744              * @event requestcomplete\r
8745              * Fires if the request was successfully completed.\r
8746              * @param {Connection} conn This Connection object.\r
8747              * @param {Object} response The XHR object containing the response data.\r
8748              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
8749              * for details.\r
8750              * @param {Object} options The options config object passed to the {@link #request} method.\r
8751              */\r
8752             REQUESTCOMPLETE,\r
8753             /**\r
8754              * @event requestexception\r
8755              * Fires if an error HTTP status was returned from the server.\r
8756              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>\r
8757              * for details of HTTP status codes.\r
8758              * @param {Connection} conn This Connection object.\r
8759              * @param {Object} response The XHR object containing the response data.\r
8760              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
8761              * for details.\r
8762              * @param {Object} options The options config object passed to the {@link #request} method.\r
8763              */\r
8764             REQUESTEXCEPTION\r
8765         );\r
8766         Ext.data.Connection.superclass.constructor.call(this);\r
8767     };\r
8768 \r
8769     Ext.extend(Ext.data.Connection, Ext.util.Observable, {\r
8770         /**\r
8771          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>\r
8772          * <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
8773          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>\r
8774          */\r
8775         /**\r
8776          * @cfg {Object} extraParams (Optional) An object containing properties which are used as\r
8777          * extra parameters to each request made by this object. (defaults to undefined)\r
8778          */\r
8779         /**\r
8780          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added\r
8781          *  to each request made by this object. (defaults to undefined)\r
8782          */\r
8783         /**\r
8784          * @cfg {String} method (Optional) The default HTTP method to be used for requests.\r
8785          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;\r
8786          * otherwise, GET will be used.)\r
8787          */\r
8788         /**\r
8789          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)\r
8790          */\r
8791         timeout : 30000,\r
8792         /**\r
8793          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)\r
8794          * @type Boolean\r
8795          */\r
8796         autoAbort:false,\r
8797     \r
8798         /**\r
8799          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)\r
8800          * @type Boolean\r
8801          */\r
8802         disableCaching: true,\r
8803         \r
8804         /**\r
8805          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching\r
8806          * through a cache buster. Defaults to '_dc'\r
8807          * @type String\r
8808          */\r
8809         disableCachingParam: '_dc',\r
8810         \r
8811         /**\r
8812          * <p>Sends an HTTP request to a remote server.</p>\r
8813          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will\r
8814          * return before the response has been received. Process any returned data\r
8815          * in a callback function.</p>\r
8816          * <pre><code>\r
8817 Ext.Ajax.request({\r
8818    url: 'ajax_demo/sample.json',\r
8819    success: function(response, opts) {\r
8820       var obj = Ext.decode(response.responseText);\r
8821       console.dir(obj);\r
8822    },\r
8823    failure: function(response, opts) {\r
8824       console.log('server-side failure with status code ' + response.status);\r
8825    }\r
8826 });\r
8827          * </code></pre>\r
8828          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>\r
8829          * @param {Object} options An object which may contain the following properties:<ul>\r
8830          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to\r
8831          * which to send the request, or a function to call which returns a URL string. The scope of the\r
8832          * function is specified by the <tt>scope</tt> option. Defaults to the configured\r
8833          * <tt>{@link #url}</tt>.</div></li>\r
8834          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">\r
8835          * An object containing properties which are used as parameters to the\r
8836          * request, a url encoded string or a function to call to get either. The scope of the function\r
8837          * is specified by the <tt>scope</tt> option.</div></li>\r
8838          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use\r
8839          * for the request. Defaults to the configured method, or if no method was configured,\r
8840          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that\r
8841          * the method name is case-sensitive and should be all caps.</div></li>\r
8842          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The\r
8843          * function to be called upon receipt of the HTTP response. The callback is\r
8844          * called regardless of success or failure and is passed the following\r
8845          * parameters:<ul>\r
8846          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8847          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>\r
8848          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data. \r
8849          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about \r
8850          * accessing elements of the response.</div></li>\r
8851          * </ul></div></li>\r
8852          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function\r
8853          * to be called upon success of the request. The callback is passed the following\r
8854          * parameters:<ul>\r
8855          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
8856          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8857          * </ul></div></li>\r
8858          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function\r
8859          * to be called upon failure of the request. The callback is passed the\r
8860          * following parameters:<ul>\r
8861          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
8862          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
8863          * </ul></div></li>\r
8864          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in\r
8865          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were\r
8866          * specified as functions from which to draw values, then this also serves as the scope for those function calls.\r
8867          * Defaults to the browser window.</div></li>\r
8868          * <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
8869          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>\r
8870          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>\r
8871          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used \r
8872          * with the <tt>form</tt> option</b>.\r
8873          * <p>True if the form object is a file upload (will be set automatically if the form was\r
8874          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>\r
8875          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>\r
8876          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the\r
8877          * DOM <tt>&lt;form></tt> element temporarily modified to have its\r
8878          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
8879          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
8880          * but removed after the return data has been gathered.</p>\r
8881          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
8882          * server is using JSON to send the return object, then the\r
8883          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
8884          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
8885          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
8886          * is created containing a <tt>responseText</tt> property in order to conform to the\r
8887          * requirements of event handlers and callbacks.</p>\r
8888          * <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
8889          * and some server technologies (notably JEE) may require some custom processing in order to\r
8890          * retrieve parameter names and parameter values from the packet content.</p>\r
8891          * </div></li>\r
8892          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request\r
8893          * headers to set for the request.</div></li>\r
8894          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document\r
8895          * to use for the post. Note: This will be used instead of params for the post\r
8896          * data. Any params will be appended to the URL.</div></li>\r
8897          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON\r
8898          * data to use as the post. Note: This will be used instead of params for the post\r
8899          * data. Any params will be appended to the URL.</div></li>\r
8900          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True\r
8901          * to add a unique cache-buster param to GET requests.</div></li>\r
8902          * </ul></p>\r
8903          * <p>The options object may also contain any other property which might be needed to perform\r
8904          * postprocessing in a callback because it is passed to callback functions.</p>\r
8905          * @return {Number} transactionId The id of the server transaction. This may be used\r
8906          * to cancel the request.\r
8907          */\r
8908         request : function(o){\r
8909             var me = this;\r
8910             if(me.fireEvent(BEFOREREQUEST, me, o)){\r
8911                 if (o.el) {\r
8912                     if(!Ext.isEmpty(o.indicatorText)){\r
8913                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";\r
8914                     }\r
8915                     if(me.indicatorText) {\r
8916                         Ext.getDom(o.el).innerHTML = me.indicatorText;                        \r
8917                     }\r
8918                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {\r
8919                         Ext.getDom(o.el).innerHTML = response.responseText;\r
8920                     });\r
8921                 }\r
8922                 \r
8923                 var p = o.params,\r
8924                     url = o.url || me.url,                \r
8925                     method,\r
8926                     cb = {success: me.handleResponse,\r
8927                           failure: me.handleFailure,\r
8928                           scope: me,\r
8929                           argument: {options: o},\r
8930                           timeout : o.timeout || me.timeout\r
8931                     },\r
8932                     form,                    \r
8933                     serForm;                    \r
8934                   \r
8935                      \r
8936                 if (Ext.isFunction(p)) {\r
8937                     p = p.call(o.scope||WINDOW, o);\r
8938                 }\r
8939                                                            \r
8940                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);    \r
8941                 \r
8942                 if (Ext.isFunction(url)) {\r
8943                     url = url.call(o.scope || WINDOW, o);\r
8944                 }\r
8945     \r
8946                 if((form = Ext.getDom(o.form))){\r
8947                     url = url || form.action;\r
8948                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) { \r
8949                          return me.doFormUpload.call(me, o, p, url);\r
8950                      }\r
8951                     serForm = Ext.lib.Ajax.serializeForm(form);                    \r
8952                     p = p ? (p + '&' + serForm) : serForm;\r
8953                 }\r
8954                 \r
8955                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);\r
8956                 \r
8957                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){\r
8958                     var dcp = o.disableCachingParam || me.disableCachingParam;\r
8959                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));\r
8960                 }\r
8961                 \r
8962                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});\r
8963                 \r
8964                 if(o.autoAbort === true || me.autoAbort) {\r
8965                     me.abort();\r
8966                 }\r
8967                  \r
8968                 if((method == GET || o.xmlData || o.jsonData) && p){\r
8969                     url = Ext.urlAppend(url, p);  \r
8970                     p = '';\r
8971                 }\r
8972                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));\r
8973             }else{                \r
8974                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;\r
8975             }\r
8976         },\r
8977     \r
8978         /**\r
8979          * Determine whether this object has a request outstanding.\r
8980          * @param {Number} transactionId (Optional) defaults to the last transaction\r
8981          * @return {Boolean} True if there is an outstanding request.\r
8982          */\r
8983         isLoading : function(transId){\r
8984             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;            \r
8985         },\r
8986     \r
8987         /**\r
8988          * Aborts any outstanding request.\r
8989          * @param {Number} transactionId (Optional) defaults to the last transaction\r
8990          */\r
8991         abort : function(transId){\r
8992             if(transId || this.isLoading()){\r
8993                 Ext.lib.Ajax.abort(transId || this.transId);\r
8994             }\r
8995         },\r
8996 \r
8997         // private\r
8998         handleResponse : function(response){\r
8999             this.transId = false;\r
9000             var options = response.argument.options;\r
9001             response.argument = options ? options.argument : null;\r
9002             this.fireEvent(REQUESTCOMPLETE, this, response, options);\r
9003             if(options.success){\r
9004                 options.success.call(options.scope, response, options);\r
9005             }\r
9006             if(options.callback){\r
9007                 options.callback.call(options.scope, options, true, response);\r
9008             }\r
9009         },\r
9010 \r
9011         // private\r
9012         handleFailure : function(response, e){\r
9013             this.transId = false;\r
9014             var options = response.argument.options;\r
9015             response.argument = options ? options.argument : null;\r
9016             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);\r
9017             if(options.failure){\r
9018                 options.failure.call(options.scope, response, options);\r
9019             }\r
9020             if(options.callback){\r
9021                 options.callback.call(options.scope, options, false, response);\r
9022             }\r
9023         },\r
9024 \r
9025         // private\r
9026         doFormUpload : function(o, ps, url){\r
9027             var id = Ext.id(),\r
9028                 doc = document,\r
9029                 frame = doc.createElement('iframe'),\r
9030                 form = Ext.getDom(o.form),\r
9031                 hiddens = [],\r
9032                 hd,\r
9033                 encoding = 'multipart/form-data',\r
9034                 buf = {\r
9035                     target: form.target,\r
9036                     method: form.method,\r
9037                     encoding: form.encoding,\r
9038                     enctype: form.enctype,\r
9039                     action: form.action\r
9040                 };\r
9041 \r
9042             Ext.fly(frame).set({\r
9043                 id: id,\r
9044                 name: id,\r
9045                 cls: 'x-hidden',\r
9046                 src: Ext.SSL_SECURE_URL // for IE\r
9047             });\r
9048             doc.body.appendChild(frame);\r
9049 \r
9050             // This is required so that IE doesn't pop the response up in a new window.\r
9051             if(Ext.isIE){\r
9052                document.frames[id].name = id;\r
9053             }\r
9054 \r
9055             Ext.fly(form).set({\r
9056                 target: id,\r
9057                 method: POST,\r
9058                 enctype: encoding,\r
9059                 encoding: encoding,\r
9060                 action: url || buf.action\r
9061             });\r
9062 \r
9063             // add dynamic params\r
9064             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){\r
9065                 hd = doc.createElement('input');\r
9066                 Ext.fly(hd).set({\r
9067                     type: 'hidden',\r
9068                     value: v,\r
9069                     name: k\r
9070                 });\r
9071                 form.appendChild(hd);\r
9072                 hiddens.push(hd);\r
9073             });\r
9074 \r
9075             function cb(){\r
9076                 var me = this,\r
9077                     // bogus response object\r
9078                     r = {responseText : '',\r
9079                          responseXML : null,\r
9080                          argument : o.argument},\r
9081                     doc,\r
9082                     firstChild;\r
9083 \r
9084                 try{\r
9085                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;\r
9086                     if(doc){\r
9087                         if(doc.body){\r
9088                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea\r
9089                                 r.responseText = firstChild.value;\r
9090                             }else{\r
9091                                 r.responseText = doc.body.innerHTML;\r
9092                             }\r
9093                         }\r
9094                         //in IE the document may still have a body even if returns XML.\r
9095                         r.responseXML = doc.XMLDocument || doc;\r
9096                     }\r
9097                 }\r
9098                 catch(e) {}\r
9099 \r
9100                 Ext.EventManager.removeListener(frame, LOAD, cb, me);\r
9101 \r
9102                 me.fireEvent(REQUESTCOMPLETE, me, r, o);\r
9103 \r
9104                 function runCallback(fn, scope, args){\r
9105                     if(Ext.isFunction(fn)){\r
9106                         fn.apply(scope, args);\r
9107                     }\r
9108                 }\r
9109 \r
9110                 runCallback(o.success, o.scope, [r, o]);\r
9111                 runCallback(o.callback, o.scope, [o, true, r]);\r
9112 \r
9113                 if(!me.debugUploads){\r
9114                     setTimeout(function(){Ext.removeNode(frame);}, 100);\r
9115                 }\r
9116             }\r
9117 \r
9118             Ext.EventManager.on(frame, LOAD, cb, this);\r
9119             form.submit();\r
9120 \r
9121             Ext.fly(form).set(buf);\r
9122             Ext.each(hiddens, function(h) {\r
9123                 Ext.removeNode(h);\r
9124             });\r
9125         }\r
9126     });\r
9127 })();\r
9128 \r
9129 /**\r
9130  * @class Ext.Ajax\r
9131  * @extends Ext.data.Connection\r
9132  * <p>The global Ajax request class that provides a simple way to make Ajax requests\r
9133  * with maximum flexibility.</p>\r
9134  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once\r
9135  * and override them at the request function level only if necessary.</p>\r
9136  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>\r
9137  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>\r
9138  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>\r
9139  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>\r
9140  * </ul></div>\r
9141  * <pre><code>\r
9142 // Default headers to pass in every request\r
9143 Ext.Ajax.defaultHeaders = {\r
9144     'Powered-By': 'Ext'\r
9145 };\r
9146  * </code></pre> \r
9147  * </p>\r
9148  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>\r
9149  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>\r
9150  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>\r
9151  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>\r
9152  * </ul></div>\r
9153  * <pre><code>\r
9154 // Example: show a spinner during all Ajax requests\r
9155 Ext.Ajax.on('beforerequest', this.showSpinner, this);\r
9156 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);\r
9157 Ext.Ajax.on('requestexception', this.hideSpinner, this);\r
9158  * </code></pre> \r
9159  * </p>\r
9160  * <p>An example request:</p>\r
9161  * <pre><code>\r
9162 // Basic request\r
9163 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9164    url: 'foo.php',\r
9165    success: someFn,\r
9166    failure: otherFn,\r
9167    headers: {\r
9168        'my-header': 'foo'\r
9169    },\r
9170    params: { foo: 'bar' }\r
9171 });\r
9172 \r
9173 // Simple ajax form submission\r
9174 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9175     form: 'some-form',\r
9176     params: 'foo=bar'\r
9177 });\r
9178  * </code></pre> \r
9179  * </p>\r
9180  * @singleton\r
9181  */\r
9182 Ext.Ajax = new Ext.data.Connection({\r
9183     /**\r
9184      * @cfg {String} url @hide\r
9185      */\r
9186     /**\r
9187      * @cfg {Object} extraParams @hide\r
9188      */\r
9189     /**\r
9190      * @cfg {Object} defaultHeaders @hide\r
9191      */\r
9192     /**\r
9193      * @cfg {String} method (Optional) @hide\r
9194      */\r
9195     /**\r
9196      * @cfg {Number} timeout (Optional) @hide\r
9197      */\r
9198     /**\r
9199      * @cfg {Boolean} autoAbort (Optional) @hide\r
9200      */\r
9201 \r
9202     /**\r
9203      * @cfg {Boolean} disableCaching (Optional) @hide\r
9204      */\r
9205 \r
9206     /**\r
9207      * @property  disableCaching\r
9208      * True to add a unique cache-buster param to GET requests. (defaults to true)\r
9209      * @type Boolean\r
9210      */\r
9211     /**\r
9212      * @property  url\r
9213      * The default URL to be used for requests to the server. (defaults to undefined)\r
9214      * If the server receives all requests through one URL, setting this once is easier than\r
9215      * entering it on every request.\r
9216      * @type String\r
9217      */\r
9218     /**\r
9219      * @property  extraParams\r
9220      * An object containing properties which are used as extra parameters to each request made\r
9221      * by this object (defaults to undefined). Session information and other data that you need\r
9222      * to pass with each request are commonly put here.\r
9223      * @type Object\r
9224      */\r
9225     /**\r
9226      * @property  defaultHeaders\r
9227      * An object containing request headers which are added to each request made by this object\r
9228      * (defaults to undefined).\r
9229      * @type Object\r
9230      */\r
9231     /**\r
9232      * @property  method\r
9233      * The default HTTP method to be used for requests. Note that this is case-sensitive and\r
9234      * should be all caps (defaults to undefined; if not set but params are present will use\r
9235      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)\r
9236      * @type String\r
9237      */\r
9238     /**\r
9239      * @property  timeout\r
9240      * The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9241      * @type Number\r
9242      */\r
9243 \r
9244     /**\r
9245      * @property  autoAbort\r
9246      * Whether a new request should abort any pending requests. (defaults to false)\r
9247      * @type Boolean\r
9248      */\r
9249     autoAbort : false,\r
9250 \r
9251     /**\r
9252      * Serialize the passed form into a url encoded string\r
9253      * @param {String/HTMLElement} form\r
9254      * @return {String}\r
9255      */\r
9256     serializeForm : function(form){\r
9257         return Ext.lib.Ajax.serializeForm(form);\r
9258     }\r
9259 });\r
9260 /**
9261  * @class Ext.Updater
9262  * @extends Ext.util.Observable
9263  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9264  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9265  * {@link Ext.Element Element} on a specific interval.<br><br>
9266  * Usage:<br>
9267  * <pre><code>
9268  * var el = Ext.get("foo"); // Get Ext.Element object
9269  * var mgr = el.getUpdater();
9270  * mgr.update({
9271         url: "http://myserver.com/index.php",
9272         params: {
9273             param1: "foo",
9274             param2: "bar"
9275         }
9276  * });
9277  * ...
9278  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9279  * <br>
9280  * // or directly (returns the same Updater instance)
9281  * var mgr = new Ext.Updater("myElementId");
9282  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9283  * mgr.on("update", myFcnNeedsToKnow);
9284  * <br>
9285  * // short handed call directly from the element object
9286  * Ext.get("foo").load({
9287         url: "bar.php",
9288         scripts: true,
9289         params: "param1=foo&amp;param2=bar",
9290         text: "Loading Foo..."
9291  * });
9292  * </code></pre>
9293  * @constructor
9294  * Create new Updater directly.
9295  * @param {Mixed} el The element to update
9296  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9297  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9298  */
9299 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable, 
9300 function() {
9301         var BEFOREUPDATE = "beforeupdate",
9302                 UPDATE = "update",
9303                 FAILURE = "failure";
9304                 
9305         // private
9306     function processSuccess(response){      
9307             var me = this;
9308         me.transaction = null;
9309         if (response.argument.form && response.argument.reset) {
9310             try { // put in try/catch since some older FF releases had problems with this
9311                 response.argument.form.reset();
9312             } catch(e){}
9313         }
9314         if (me.loadScripts) {
9315             me.renderer.render(me.el, response, me,
9316                updateComplete.createDelegate(me, [response]));
9317         } else {
9318             me.renderer.render(me.el, response, me);
9319             updateComplete.call(me, response);
9320         }
9321     }
9322     
9323     // private
9324     function updateComplete(response, type, success){
9325         this.fireEvent(type || UPDATE, this.el, response);
9326         if(Ext.isFunction(response.argument.callback)){
9327             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9328         }
9329     }
9330
9331     // private
9332     function processFailure(response){              
9333         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9334     }
9335             
9336         return {
9337             constructor: function(el, forceNew){
9338                     var me = this;
9339                 el = Ext.get(el);
9340                 if(!forceNew && el.updateManager){
9341                     return el.updateManager;
9342                 }
9343                 /**
9344                  * The Element object
9345                  * @type Ext.Element
9346                  */
9347                 me.el = el;
9348                 /**
9349                  * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9350                  * @type String
9351                  */
9352                 me.defaultUrl = null;
9353         
9354                 me.addEvents(
9355                     /**
9356                      * @event beforeupdate
9357                      * Fired before an update is made, return false from your handler and the update is cancelled.
9358                      * @param {Ext.Element} el
9359                      * @param {String/Object/Function} url
9360                      * @param {String/Object} params
9361                      */
9362                     BEFOREUPDATE,
9363                     /**
9364                      * @event update
9365                      * Fired after successful update is made.
9366                      * @param {Ext.Element} el
9367                      * @param {Object} oResponseObject The response Object
9368                      */
9369                     UPDATE,
9370                     /**
9371                      * @event failure
9372                      * Fired on update failure.
9373                      * @param {Ext.Element} el
9374                      * @param {Object} oResponseObject The response Object
9375                      */
9376                     FAILURE
9377                 );
9378         
9379                 Ext.apply(me, Ext.Updater.defaults);
9380                 /**
9381                  * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9382                  * @property sslBlankUrl
9383                  * @type String
9384                  */
9385                 /**
9386                  * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9387                  * @property disableCaching
9388                  * @type Boolean
9389                  */
9390                 /**
9391                  * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9392                  * @property indicatorText
9393                  * @type String
9394                  */
9395                 /**
9396                  * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9397                  * @property showLoadIndicator
9398                  * @type String
9399                  */
9400                 /**
9401                  * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9402                  * @property timeout
9403                  * @type Number
9404                  */
9405                 /**
9406                  * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9407                  * @property loadScripts
9408                  * @type Boolean
9409                  */
9410         
9411                 /**
9412                  * Transaction object of the current executing transaction, or null if there is no active transaction.
9413                  */
9414                 me.transaction = null;
9415                 /**
9416                  * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9417                  * @type Function
9418                  */
9419                 me.refreshDelegate = me.refresh.createDelegate(me);
9420                 /**
9421                  * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9422                  * @type Function
9423                  */
9424                 me.updateDelegate = me.update.createDelegate(me);
9425                 /**
9426                  * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9427                  * @type Function
9428                  */
9429                 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);     
9430                 
9431                         /**
9432                          * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9433                          */
9434                 me.renderer = me.renderer || me.getDefaultRenderer();
9435                 
9436                 Ext.Updater.superclass.constructor.call(me);
9437             },
9438         
9439                 /**
9440              * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9441              * @param {Object} renderer The object implementing the render() method
9442              */
9443             setRenderer : function(renderer){
9444                 this.renderer = renderer;
9445             },  
9446         
9447             /**
9448              * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9449              * @return {Object}
9450              */
9451             getRenderer : function(){
9452                return this.renderer;
9453             },
9454
9455             /**
9456              * This is an overrideable method which returns a reference to a default
9457              * renderer class if none is specified when creating the Ext.Updater.
9458              * Defaults to {@link Ext.Updater.BasicRenderer}
9459              */
9460             getDefaultRenderer: function() {
9461                 return new Ext.Updater.BasicRenderer();
9462             },
9463                 
9464             /**
9465              * Sets the default URL used for updates.
9466              * @param {String/Function} defaultUrl The url or a function to call to get the url
9467              */
9468             setDefaultUrl : function(defaultUrl){
9469                 this.defaultUrl = defaultUrl;
9470             },
9471         
9472             /**
9473              * Get the Element this Updater is bound to
9474              * @return {Ext.Element} The element
9475              */
9476             getEl : function(){
9477                 return this.el;
9478             },
9479         
9480                 /**
9481              * Performs an <b>asynchronous</b> request, updating this element with the response.
9482              * If params are specified it uses POST, otherwise it uses GET.<br><br>
9483              * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
9484              * will not have been fully updated when the function returns. To post-process the returned
9485              * data, use the callback option, or an <b><tt>update</tt></b> event handler.
9486              * @param {Object} options A config object containing any of the following options:<ul>
9487              * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
9488              * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
9489              * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
9490              * use. Defaults to POST if the <tt>params</tt> argument is present, otherwise GET.</p></li>
9491              * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
9492              * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
9493              * string, or as an object containing properties which represent parameters,
9494              * or as a function, which returns such an object.</p></li>
9495              * <li>scripts : <b>Boolean</b><p class="sub-desc">If <tt>true</tt>
9496              * any &lt;script&gt; tags embedded in the response text will be extracted
9497              * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
9498              * the callback will be called <i>after</i> the execution of the scripts.</p></li>
9499              * <li>callback : <b>Function</b><p class="sub-desc">A function to
9500              * be called when the response from the server arrives. The following
9501              * parameters are passed:<ul>
9502              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9503              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9504              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
9505              * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
9506              * </p></li>
9507              * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
9508              * to execute the callback (The callback's <tt>this</tt> reference.) If the
9509              * <tt>params</tt> argument is a function, this scope is used for that function also.</p></li>
9510              * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
9511              * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
9512              * calls.  To bypass this behavior, pass <tt>discardUrl:true</tt> (defaults to false).</p></li>
9513              * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
9514              * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
9515              * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
9516              * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
9517              * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
9518              * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
9519              * requests, this option causes an extra, auto-generated parameter to be appended to the request
9520              * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
9521              * <p>
9522              * For example:
9523         <pre><code>
9524         um.update({
9525             url: "your-url.php",
9526             params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9527             callback: yourFunction,
9528             scope: yourObject, //(optional scope)
9529             discardUrl: true,
9530             nocache: true,
9531             text: "Loading...",
9532             timeout: 60,
9533             scripts: false // Save time by avoiding RegExp execution.
9534         });
9535         </code></pre>
9536              */
9537             update : function(url, params, callback, discardUrl){
9538                     var me = this,
9539                         cfg, 
9540                         callerScope;
9541                         
9542                 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){               
9543                     if(Ext.isObject(url)){ // must be config object
9544                         cfg = url;
9545                         url = cfg.url;
9546                         params = params || cfg.params;
9547                         callback = callback || cfg.callback;
9548                         discardUrl = discardUrl || cfg.discardUrl;
9549                         callerScope = cfg.scope;                        
9550                         if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
9551                         if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
9552                         if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
9553                         if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
9554                     }
9555                     me.showLoading();
9556         
9557                     if(!discardUrl){
9558                         me.defaultUrl = url;
9559                     }
9560                     if(Ext.isFunction(url)){
9561                         url = url.call(me);
9562                     }
9563         
9564                     var o = Ext.apply({}, {
9565                         url : url,
9566                         params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
9567                         success: processSuccess,
9568                         failure: processFailure,
9569                         scope: me,
9570                         callback: undefined,
9571                         timeout: (me.timeout*1000),
9572                         disableCaching: me.disableCaching,
9573                         argument: {
9574                             "options": cfg,
9575                             "url": url,
9576                             "form": null,
9577                             "callback": callback,
9578                             "scope": callerScope || window,
9579                             "params": params
9580                         }
9581                     }, cfg);
9582         
9583                     me.transaction = Ext.Ajax.request(o);
9584                 }
9585             },          
9586
9587                 /**
9588              * <p>Performs an async form post, updating this element with the response. If the form has the attribute
9589              * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
9590              * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
9591              * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9592              * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9593              * DOM <tt>&lt;form></tt> element temporarily modified to have its
9594              * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9595              * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9596              * but removed after the return data has been gathered.</p>
9597              * <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>
9598              * and some server technologies (notably JEE) may require some custom processing in order to
9599              * retrieve parameter names and parameter values from the packet content.</p>
9600              * @param {String/HTMLElement} form The form Id or form element
9601              * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
9602              * @param {Boolean} reset (optional) Whether to try to reset the form after the update
9603              * @param {Function} callback (optional) Callback when transaction is complete. The following
9604              * parameters are passed:<ul>
9605              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9606              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9607              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
9608              */
9609             formUpdate : function(form, url, reset, callback){
9610                     var me = this;
9611                 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
9612                     if(Ext.isFunction(url)){
9613                         url = url.call(me);
9614                     }
9615                     form = Ext.getDom(form)
9616                     me.transaction = Ext.Ajax.request({
9617                         form: form,
9618                         url:url,
9619                         success: processSuccess,
9620                         failure: processFailure,
9621                         scope: me,
9622                         timeout: (me.timeout*1000),
9623                         argument: {
9624                             "url": url,
9625                             "form": form,
9626                             "callback": callback,
9627                             "reset": reset
9628                         }
9629                     });
9630                     me.showLoading.defer(1, me);
9631                 }
9632             },
9633                         
9634             /**
9635              * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
9636              * @param {Number} interval How often to update (in seconds).
9637              * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
9638              * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
9639              * the url used in a load call can be reused by this method, other load config options will not be reused and must be
9640              * sepcified as part of a config object passed as this paramter if needed.
9641              * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
9642              * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
9643              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9644              * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
9645              */
9646             startAutoRefresh : function(interval, url, params, callback, refreshNow){
9647                     var me = this;
9648                 if(refreshNow){
9649                     me.update(url || me.defaultUrl, params, callback, true);
9650                 }
9651                 if(me.autoRefreshProcId){
9652                     clearInterval(me.autoRefreshProcId);
9653                 }
9654                 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
9655             },
9656         
9657             /**
9658              * Stop auto refresh on this element.
9659              */
9660             stopAutoRefresh : function(){
9661                 if(this.autoRefreshProcId){
9662                     clearInterval(this.autoRefreshProcId);
9663                     delete this.autoRefreshProcId;
9664                 }
9665             },
9666         
9667             /**
9668              * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
9669              */
9670             isAutoRefreshing : function(){
9671                return !!this.autoRefreshProcId;
9672             },
9673         
9674             /**
9675              * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
9676              * method may be overridden to perform a custom action while this Updater is actively updating its contents.
9677              */
9678             showLoading : function(){
9679                 if(this.showLoadIndicator){
9680                 this.el.dom.innerHTML = this.indicatorText;
9681                 }
9682             },
9683         
9684             /**
9685              * Aborts the currently executing transaction, if any.
9686              */
9687             abort : function(){
9688                 if(this.transaction){
9689                     Ext.Ajax.abort(this.transaction);
9690                 }
9691             },
9692         
9693             /**
9694              * Returns true if an update is in progress, otherwise false.
9695              * @return {Boolean}
9696              */
9697             isUpdating : function(){        
9698                 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;        
9699             },
9700             
9701             /**
9702              * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
9703              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9704              */
9705             refresh : function(callback){
9706                 if(this.defaultUrl){
9707                         this.update(this.defaultUrl, null, callback, true);
9708                 }
9709             }
9710     }
9711 }());
9712
9713 /**
9714  * @class Ext.Updater.defaults
9715  * The defaults collection enables customizing the default properties of Updater
9716  */
9717 Ext.Updater.defaults = {
9718    /**
9719      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
9720      * @type Number
9721      */
9722     timeout : 30,    
9723     /**
9724      * True to append a unique parameter to GET requests to disable caching (defaults to false).
9725      * @type Boolean
9726      */
9727     disableCaching : false,
9728     /**
9729      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
9730      * @type Boolean
9731      */
9732     showLoadIndicator : true,
9733     /**
9734      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
9735      * @type String
9736      */
9737     indicatorText : '<div class="loading-indicator">Loading...</div>',
9738      /**
9739      * True to process scripts by default (defaults to false).
9740      * @type Boolean
9741      */
9742     loadScripts : false,
9743     /**
9744     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
9745     * @type String
9746     */
9747     sslBlankUrl : Ext.SSL_SECURE_URL      
9748 };
9749
9750
9751 /**
9752  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
9753  * Usage:
9754  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
9755  * @param {Mixed} el The element to update
9756  * @param {String} url The url
9757  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
9758  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
9759  * example: {disableCaching:true, indicatorText: "Loading data..."}
9760  * @static
9761  * @deprecated
9762  * @member Ext.Updater
9763  */
9764 Ext.Updater.updateElement = function(el, url, params, options){
9765     var um = Ext.get(el).getUpdater();
9766     Ext.apply(um, options);
9767     um.update(url, params, options ? options.callback : null);
9768 };
9769
9770 /**
9771  * @class Ext.Updater.BasicRenderer
9772  * Default Content renderer. Updates the elements innerHTML with the responseText.
9773  */
9774 Ext.Updater.BasicRenderer = function(){};
9775
9776 Ext.Updater.BasicRenderer.prototype = {
9777     /**
9778      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
9779      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
9780      * create an object with a "render(el, response)" method and pass it to setRenderer on the Updater.
9781      * @param {Ext.Element} el The element being rendered
9782      * @param {Object} response The XMLHttpRequest object
9783      * @param {Updater} updateManager The calling update manager
9784      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
9785      */
9786      render : function(el, response, updateManager, callback){       
9787         el.update(response.responseText, updateManager.loadScripts, callback);
9788     }
9789 };/**
9790  * @class Date
9791  *
9792  * The date parsing and formatting syntax contains a subset of
9793  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
9794  * supported will provide results equivalent to their PHP versions.
9795  *
9796  * The following is a list of all currently supported formats:
9797  * <pre>
9798 Format  Description                                                               Example returned values
9799 ------  -----------------------------------------------------------------------   -----------------------
9800   d     Day of the month, 2 digits with leading zeros                             01 to 31
9801   D     A short textual representation of the day of the week                     Mon to Sun
9802   j     Day of the month without leading zeros                                    1 to 31
9803   l     A full textual representation of the day of the week                      Sunday to Saturday
9804   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
9805   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
9806   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
9807   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
9808   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
9809   F     A full textual representation of a month, such as January or March        January to December
9810   m     Numeric representation of a month, with leading zeros                     01 to 12
9811   M     A short textual representation of a month                                 Jan to Dec
9812   n     Numeric representation of a month, without leading zeros                  1 to 12
9813   t     Number of days in the given month                                         28 to 31
9814   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
9815   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
9816         belongs to the previous or next year, that year is used instead)
9817   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
9818   y     A two digit representation of a year                                      Examples: 99 or 03
9819   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
9820   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
9821   g     12-hour format of an hour without leading zeros                           1 to 12
9822   G     24-hour format of an hour without leading zeros                           0 to 23
9823   h     12-hour format of an hour with leading zeros                              01 to 12
9824   H     24-hour format of an hour with leading zeros                              00 to 23
9825   i     Minutes, with leading zeros                                               00 to 59
9826   s     Seconds, with leading zeros                                               00 to 59
9827   u     Decimal fraction of a second                                              Examples:
9828         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
9829                                                                                   100 (i.e. 0.100s) or
9830                                                                                   999 (i.e. 0.999s) or
9831                                                                                   999876543210 (i.e. 0.999876543210s)
9832   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
9833   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
9834   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
9835   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
9836   c     ISO 8601 date
9837         Notes:                                                                    Examples:
9838         1) If unspecified, the month / day defaults to the current month / day,   1991 or
9839            the time defaults to midnight, while the timezone defaults to the      1992-10 or
9840            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
9841            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
9842            are optional.                                                          1995-07-18T17:21:28-02:00 or
9843         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
9844            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
9845            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
9846         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
9847         date-time granularity which are supported, or see                         2000-02-13T21:25:33
9848         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
9849   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
9850   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
9851                                                                                   \/Date(1238606590509+0800)\/
9852 </pre>
9853  *
9854  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
9855  * <pre><code>
9856 // Sample date:
9857 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
9858
9859 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
9860 document.write(dt.format('Y-m-d'));                           // 2007-01-10
9861 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
9862 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
9863 </code></pre>
9864  *
9865  * Here are some standard date/time patterns that you might find helpful.  They
9866  * are not part of the source of Date.js, but to use them you can simply copy this
9867  * block of code into any script that is included after Date.js and they will also become
9868  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
9869  * <pre><code>
9870 Date.patterns = {
9871     ISO8601Long:"Y-m-d H:i:s",
9872     ISO8601Short:"Y-m-d",
9873     ShortDate: "n/j/Y",
9874     LongDate: "l, F d, Y",
9875     FullDateTime: "l, F d, Y g:i:s A",
9876     MonthDay: "F d",
9877     ShortTime: "g:i A",
9878     LongTime: "g:i:s A",
9879     SortableDateTime: "Y-m-d\\TH:i:s",
9880     UniversalSortableDateTime: "Y-m-d H:i:sO",
9881     YearMonth: "F, Y"
9882 };
9883 </code></pre>
9884  *
9885  * Example usage:
9886  * <pre><code>
9887 var dt = new Date();
9888 document.write(dt.format(Date.patterns.ShortDate));
9889 </code></pre>
9890  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
9891  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
9892  */
9893
9894 /*
9895  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
9896  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
9897  * They generate precompiled functions from format patterns instead of parsing and
9898  * processing each pattern every time a date is formatted. These functions are available
9899  * on every Date object.
9900  */
9901
9902 (function() {
9903
9904 /**
9905  * Global flag which determines if strict date parsing should be used.
9906  * Strict date parsing will not roll-over invalid dates, which is the
9907  * default behaviour of javascript Date objects.
9908  * (see {@link #parseDate} for more information)
9909  * Defaults to <tt>false</tt>.
9910  * @static
9911  * @type Boolean
9912 */
9913 Date.useStrict = false;
9914
9915
9916 // create private copy of Ext's String.format() method
9917 // - to remove unnecessary dependency
9918 // - to resolve namespace conflict with M$-Ajax's implementation
9919 function xf(format) {
9920     var args = Array.prototype.slice.call(arguments, 1);
9921     return format.replace(/\{(\d+)\}/g, function(m, i) {
9922         return args[i];
9923     });
9924 }
9925
9926
9927 // private
9928 Date.formatCodeToRegex = function(character, currentGroup) {
9929     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
9930     var p = Date.parseCodes[character];
9931
9932     if (p) {
9933       p = typeof p == 'function'? p() : p;
9934       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
9935     }
9936
9937     return p? Ext.applyIf({
9938       c: p.c? xf(p.c, currentGroup || "{0}") : p.c
9939     }, p) : {
9940         g:0,
9941         c:null,
9942         s:Ext.escapeRe(character) // treat unrecognised characters as literals
9943     }
9944 }
9945
9946 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
9947 var $f = Date.formatCodeToRegex;
9948
9949 Ext.apply(Date, {
9950     /**
9951      * <p>An object hash in which each property is a date parsing function. The property name is the
9952      * format string which that function parses.</p>
9953      * <p>This object is automatically populated with date parsing functions as
9954      * date formats are requested for Ext standard formatting strings.</p>
9955      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
9956      * may be used as a format string to {@link #parseDate}.<p>
9957      * <p>Example:</p><pre><code>
9958 Date.parseFunctions['x-date-format'] = myDateParser;
9959 </code></pre>
9960      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
9961      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
9962      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
9963      * (i.e. prevent javascript Date "rollover") (The default must be false).
9964      * Invalid date strings should return null when parsed.</div></li>
9965      * </ul></div></p>
9966      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
9967      * formatting function must be placed into the {@link #formatFunctions} property.
9968      * @property parseFunctions
9969      * @static
9970      * @type Object
9971      */
9972     parseFunctions: {
9973         "M$": function(input, strict) {
9974             // note: the timezone offset is ignored since the M$ Ajax server sends
9975             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
9976             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
9977             var r = (input || '').match(re);
9978             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
9979         }
9980     },
9981     parseRegexes: [],
9982
9983     /**
9984      * <p>An object hash in which each property is a date formatting function. The property name is the
9985      * format string which corresponds to the produced formatted date string.</p>
9986      * <p>This object is automatically populated with date formatting functions as
9987      * date formats are requested for Ext standard formatting strings.</p>
9988      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
9989      * may be used as a format string to {@link #format}. Example:</p><pre><code>
9990 Date.formatFunctions['x-date-format'] = myDateFormatter;
9991 </code></pre>
9992      * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
9993      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
9994      * </ul></div></p>
9995      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
9996      * parsing function must be placed into the {@link #parseFunctions} property.
9997      * @property formatFunctions
9998      * @static
9999      * @type Object
10000      */
10001     formatFunctions: {
10002         "M$": function() {
10003             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10004             return '\\/Date(' + this.getTime() + ')\\/';
10005         }
10006     },
10007
10008     y2kYear : 50,
10009
10010     /**
10011      * Date interval constant
10012      * @static
10013      * @type String
10014      */
10015     MILLI : "ms",
10016
10017     /**
10018      * Date interval constant
10019      * @static
10020      * @type String
10021      */
10022     SECOND : "s",
10023
10024     /**
10025      * Date interval constant
10026      * @static
10027      * @type String
10028      */
10029     MINUTE : "mi",
10030
10031     /** Date interval constant
10032      * @static
10033      * @type String
10034      */
10035     HOUR : "h",
10036
10037     /**
10038      * Date interval constant
10039      * @static
10040      * @type String
10041      */
10042     DAY : "d",
10043
10044     /**
10045      * Date interval constant
10046      * @static
10047      * @type String
10048      */
10049     MONTH : "mo",
10050
10051     /**
10052      * Date interval constant
10053      * @static
10054      * @type String
10055      */
10056     YEAR : "y",
10057
10058     /**
10059      * <p>An object hash containing default date values used during date parsing.</p>
10060      * <p>The following properties are available:<div class="mdetail-params"><ul>
10061      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10062      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10063      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10064      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10065      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10066      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10067      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10068      * </ul></div></p>
10069      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10070      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10071      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10072      * It is the responsiblity of the developer to account for this.</b></p>
10073      * Example Usage:
10074      * <pre><code>
10075 // set default day value to the first day of the month
10076 Date.defaults.d = 1;
10077
10078 // parse a February date string containing only year and month values.
10079 // setting the default day value to 1 prevents weird date rollover issues
10080 // when attempting to parse the following date string on, for example, March 31st 2009.
10081 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10082 </code></pre>
10083      * @property defaults
10084      * @static
10085      * @type Object
10086      */
10087     defaults: {},
10088
10089     /**
10090      * An array of textual day names.
10091      * Override these values for international dates.
10092      * Example:
10093      * <pre><code>
10094 Date.dayNames = [
10095     'SundayInYourLang',
10096     'MondayInYourLang',
10097     ...
10098 ];
10099 </code></pre>
10100      * @type Array
10101      * @static
10102      */
10103     dayNames : [
10104         "Sunday",
10105         "Monday",
10106         "Tuesday",
10107         "Wednesday",
10108         "Thursday",
10109         "Friday",
10110         "Saturday"
10111     ],
10112
10113     /**
10114      * An array of textual month names.
10115      * Override these values for international dates.
10116      * Example:
10117      * <pre><code>
10118 Date.monthNames = [
10119     'JanInYourLang',
10120     'FebInYourLang',
10121     ...
10122 ];
10123 </code></pre>
10124      * @type Array
10125      * @static
10126      */
10127     monthNames : [
10128         "January",
10129         "February",
10130         "March",
10131         "April",
10132         "May",
10133         "June",
10134         "July",
10135         "August",
10136         "September",
10137         "October",
10138         "November",
10139         "December"
10140     ],
10141
10142     /**
10143      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10144      * Override these values for international dates.
10145      * Example:
10146      * <pre><code>
10147 Date.monthNumbers = {
10148     'ShortJanNameInYourLang':0,
10149     'ShortFebNameInYourLang':1,
10150     ...
10151 };
10152 </code></pre>
10153      * @type Object
10154      * @static
10155      */
10156     monthNumbers : {
10157         Jan:0,
10158         Feb:1,
10159         Mar:2,
10160         Apr:3,
10161         May:4,
10162         Jun:5,
10163         Jul:6,
10164         Aug:7,
10165         Sep:8,
10166         Oct:9,
10167         Nov:10,
10168         Dec:11
10169     },
10170
10171     /**
10172      * Get the short month name for the given month number.
10173      * Override this function for international dates.
10174      * @param {Number} month A zero-based javascript month number.
10175      * @return {String} The short month name.
10176      * @static
10177      */
10178     getShortMonthName : function(month) {
10179         return Date.monthNames[month].substring(0, 3);
10180     },
10181
10182     /**
10183      * Get the short day name for the given day number.
10184      * Override this function for international dates.
10185      * @param {Number} day A zero-based javascript day number.
10186      * @return {String} The short day name.
10187      * @static
10188      */
10189     getShortDayName : function(day) {
10190         return Date.dayNames[day].substring(0, 3);
10191     },
10192
10193     /**
10194      * Get the zero-based javascript month number for the given short/full month name.
10195      * Override this function for international dates.
10196      * @param {String} name The short/full month name.
10197      * @return {Number} The zero-based javascript month number.
10198      * @static
10199      */
10200     getMonthNumber : function(name) {
10201         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10202         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10203     },
10204
10205     /**
10206      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10207      * Formatting functions are strings (or functions which return strings) which
10208      * will return the appropriate value when evaluated in the context of the Date object
10209      * from which the {@link #format} method is called.
10210      * Add to / override these mappings for custom date formatting.
10211      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10212      * Example:
10213      * <pre><code>
10214 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10215 (new Date()).format("X"); // returns the current day of the month
10216 </code></pre>
10217      * @type Object
10218      * @static
10219      */
10220     formatCodes : {
10221         d: "String.leftPad(this.getDate(), 2, '0')",
10222         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10223         j: "this.getDate()",
10224         l: "Date.dayNames[this.getDay()]",
10225         N: "(this.getDay() ? this.getDay() : 7)",
10226         S: "this.getSuffix()",
10227         w: "this.getDay()",
10228         z: "this.getDayOfYear()",
10229         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10230         F: "Date.monthNames[this.getMonth()]",
10231         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10232         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10233         n: "(this.getMonth() + 1)",
10234         t: "this.getDaysInMonth()",
10235         L: "(this.isLeapYear() ? 1 : 0)",
10236         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10237         Y: "this.getFullYear()",
10238         y: "('' + this.getFullYear()).substring(2, 4)",
10239         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10240         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10241         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10242         G: "this.getHours()",
10243         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10244         H: "String.leftPad(this.getHours(), 2, '0')",
10245         i: "String.leftPad(this.getMinutes(), 2, '0')",
10246         s: "String.leftPad(this.getSeconds(), 2, '0')",
10247         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10248         O: "this.getGMTOffset()",
10249         P: "this.getGMTOffset(true)",
10250         T: "this.getTimezone()",
10251         Z: "(this.getTimezoneOffset() * -60)",
10252
10253         c: function() { // ISO-8601 -- GMT format
10254             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10255                 var e = c.charAt(i);
10256                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10257             }
10258             return code.join(" + ");
10259         },
10260         /*
10261         c: function() { // ISO-8601 -- UTC format
10262             return [
10263               "this.getUTCFullYear()", "'-'",
10264               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10265               "String.leftPad(this.getUTCDate(), 2, '0')",
10266               "'T'",
10267               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10268               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10269               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10270               "'Z'"
10271             ].join(" + ");
10272         },
10273         */
10274
10275         U: "Math.round(this.getTime() / 1000)"
10276     },
10277
10278     /**
10279      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10280      * @param {Number} year 4-digit year
10281      * @param {Number} month 1-based month-of-year
10282      * @param {Number} day Day of month
10283      * @param {Number} hour (optional) Hour
10284      * @param {Number} minute (optional) Minute
10285      * @param {Number} second (optional) Second
10286      * @param {Number} millisecond (optional) Millisecond
10287      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10288      * @static
10289      */
10290     isValid : function(y, m, d, h, i, s, ms) {
10291         // setup defaults
10292         h = h || 0;
10293         i = i || 0;
10294         s = s || 0;
10295         ms = ms || 0;
10296
10297         var dt = new Date(y, m - 1, d, h, i, s, ms);
10298
10299         return y == dt.getFullYear() &&
10300             m == dt.getMonth() + 1 &&
10301             d == dt.getDate() &&
10302             h == dt.getHours() &&
10303             i == dt.getMinutes() &&
10304             s == dt.getSeconds() &&
10305             ms == dt.getMilliseconds();
10306     },
10307
10308     /**
10309      * Parses the passed string using the specified date format.
10310      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10311      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10312      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10313      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10314      * Keep in mind that the input date string must precisely match the specified format string
10315      * in order for the parse operation to be successful (failed parse operations return a null value).
10316      * <p>Example:</p><pre><code>
10317 //dt = Fri May 25 2007 (current date)
10318 var dt = new Date();
10319
10320 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10321 dt = Date.parseDate("2006", "Y");
10322
10323 //dt = Sun Jan 15 2006 (all date parts specified)
10324 dt = Date.parseDate("2006-01-15", "Y-m-d");
10325
10326 //dt = Sun Jan 15 2006 15:20:01
10327 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10328
10329 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10330 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10331 </code></pre>
10332      * @param {String} input The raw date string.
10333      * @param {String} format The expected date string format.
10334      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10335                         (defaults to false). Invalid date strings will return null when parsed.
10336      * @return {Date} The parsed Date.
10337      * @static
10338      */
10339     parseDate : function(input, format, strict) {
10340         var p = Date.parseFunctions;
10341         if (p[format] == null) {
10342             Date.createParser(format);
10343         }
10344         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10345     },
10346
10347     // private
10348     getFormatCode : function(character) {
10349         var f = Date.formatCodes[character];
10350
10351         if (f) {
10352           f = typeof f == 'function'? f() : f;
10353           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10354         }
10355
10356         // note: unknown characters are treated as literals
10357         return f || ("'" + String.escape(character) + "'");
10358     },
10359
10360     // private
10361     createFormat : function(format) {
10362         var code = [],
10363             special = false,
10364             ch = '';
10365
10366         for (var i = 0; i < format.length; ++i) {
10367             ch = format.charAt(i);
10368             if (!special && ch == "\\") {
10369                 special = true;
10370             } else if (special) {
10371                 special = false;
10372                 code.push("'" + String.escape(ch) + "'");
10373             } else {
10374                 code.push(Date.getFormatCode(ch))
10375             }
10376         }
10377         Date.formatFunctions[format] = new Function("return " + code.join('+'));
10378     },
10379
10380     // private
10381     createParser : function() {
10382         var code = [
10383             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10384                 "def = Date.defaults,",
10385                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10386
10387             "if(results){",
10388                 "{1}",
10389
10390                 "if(u != null){", // i.e. unix time is defined
10391                     "v = new Date(u * 1000);", // give top priority to UNIX time
10392                 "}else{",
10393                     // create Date object representing midnight of the current day;
10394                     // this will provide us with our date defaults
10395                     // (note: clearTime() handles Daylight Saving Time automatically)
10396                     "dt = (new Date()).clearTime();",
10397
10398                     // date calculations (note: these calculations create a dependency on Ext.num())
10399                     "y = y >= 0? y : Ext.num(def.y, dt.getFullYear());",
10400                     "m = m >= 0? m : Ext.num(def.m - 1, dt.getMonth());",
10401                     "d = d >= 0? d : Ext.num(def.d, dt.getDate());",
10402
10403                     // time calculations (note: these calculations create a dependency on Ext.num())
10404                     "h  = h || Ext.num(def.h, dt.getHours());",
10405                     "i  = i || Ext.num(def.i, dt.getMinutes());",
10406                     "s  = s || Ext.num(def.s, dt.getSeconds());",
10407                     "ms = ms || Ext.num(def.ms, dt.getMilliseconds());",
10408
10409                     "if(z >= 0 && y >= 0){",
10410                         // both the year and zero-based day of year are defined and >= 0.
10411                         // these 2 values alone provide sufficient info to create a full date object
10412
10413                         // create Date object representing January 1st for the given year
10414                         "v = new Date(y, 0, 1, h, i, s, ms);",
10415
10416                         // then add day of year, checking for Date "rollover" if necessary
10417                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10418                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10419                         "v = null;", // invalid date, so return null
10420                     "}else{",
10421                         // plain old Date object
10422                         "v = new Date(y, m, d, h, i, s, ms);",
10423                     "}",
10424                 "}",
10425             "}",
10426
10427             "if(v){",
10428                 // favour UTC offset over GMT offset
10429                 "if(zz != null){",
10430                     // reset to UTC, then add offset
10431                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10432                 "}else if(o){",
10433                     // reset to GMT, then add offset
10434                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10435                 "}",
10436             "}",
10437
10438             "return v;"
10439         ].join('\n');
10440
10441         return function(format) {
10442             var regexNum = Date.parseRegexes.length,
10443                 currentGroup = 1,
10444                 calc = [],
10445                 regex = [],
10446                 special = false,
10447                 ch = "";
10448
10449             for (var i = 0; i < format.length; ++i) {
10450                 ch = format.charAt(i);
10451                 if (!special && ch == "\\") {
10452                     special = true;
10453                 } else if (special) {
10454                     special = false;
10455                     regex.push(String.escape(ch));
10456                 } else {
10457                     var obj = $f(ch, currentGroup);
10458                     currentGroup += obj.g;
10459                     regex.push(obj.s);
10460                     if (obj.g && obj.c) {
10461                         calc.push(obj.c);
10462                     }
10463                 }
10464             }
10465
10466             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10467             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10468         }
10469     }(),
10470
10471     // private
10472     parseCodes : {
10473         /*
10474          * Notes:
10475          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10476          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10477          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
10478          */
10479         d: {
10480             g:1,
10481             c:"d = parseInt(results[{0}], 10);\n",
10482             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
10483         },
10484         j: {
10485             g:1,
10486             c:"d = parseInt(results[{0}], 10);\n",
10487             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
10488         },
10489         D: function() {
10490             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
10491             return {
10492                 g:0,
10493                 c:null,
10494                 s:"(?:" + a.join("|") +")"
10495             }
10496         },
10497         l: function() {
10498             return {
10499                 g:0,
10500                 c:null,
10501                 s:"(?:" + Date.dayNames.join("|") + ")"
10502             }
10503         },
10504         N: {
10505             g:0,
10506             c:null,
10507             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
10508         },
10509         S: {
10510             g:0,
10511             c:null,
10512             s:"(?:st|nd|rd|th)"
10513         },
10514         w: {
10515             g:0,
10516             c:null,
10517             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
10518         },
10519         z: {
10520             g:1,
10521             c:"z = parseInt(results[{0}], 10);\n",
10522             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
10523         },
10524         W: {
10525             g:0,
10526             c:null,
10527             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
10528         },
10529         F: function() {
10530             return {
10531                 g:1,
10532                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
10533                 s:"(" + Date.monthNames.join("|") + ")"
10534             }
10535         },
10536         M: function() {
10537             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
10538             return Ext.applyIf({
10539                 s:"(" + a.join("|") + ")"
10540             }, $f("F"));
10541         },
10542         m: {
10543             g:1,
10544             c:"m = parseInt(results[{0}], 10) - 1;\n",
10545             s:"(\\d{2})" // month number with leading zeros (01 - 12)
10546         },
10547         n: {
10548             g:1,
10549             c:"m = parseInt(results[{0}], 10) - 1;\n",
10550             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
10551         },
10552         t: {
10553             g:0,
10554             c:null,
10555             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
10556         },
10557         L: {
10558             g:0,
10559             c:null,
10560             s:"(?:1|0)"
10561         },
10562         o: function() {
10563             return $f("Y");
10564         },
10565         Y: {
10566             g:1,
10567             c:"y = parseInt(results[{0}], 10);\n",
10568             s:"(\\d{4})" // 4-digit year
10569         },
10570         y: {
10571             g:1,
10572             c:"var ty = parseInt(results[{0}], 10);\n"
10573                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
10574             s:"(\\d{1,2})"
10575         },
10576         a: {
10577             g:1,
10578             c:"if (results[{0}] == 'am') {\n"
10579                 + "if (!h || h == 12) { h = 0; }\n"
10580                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
10581             s:"(am|pm)"
10582         },
10583         A: {
10584             g:1,
10585             c:"if (results[{0}] == 'AM') {\n"
10586                 + "if (!h || h == 12) { h = 0; }\n"
10587                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
10588             s:"(AM|PM)"
10589         },
10590         g: function() {
10591             return $f("G");
10592         },
10593         G: {
10594             g:1,
10595             c:"h = parseInt(results[{0}], 10);\n",
10596             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
10597         },
10598         h: function() {
10599             return $f("H");
10600         },
10601         H: {
10602             g:1,
10603             c:"h = parseInt(results[{0}], 10);\n",
10604             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
10605         },
10606         i: {
10607             g:1,
10608             c:"i = parseInt(results[{0}], 10);\n",
10609             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
10610         },
10611         s: {
10612             g:1,
10613             c:"s = parseInt(results[{0}], 10);\n",
10614             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
10615         },
10616         u: {
10617             g:1,
10618             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
10619             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
10620         },
10621         O: {
10622             g:1,
10623             c:[
10624                 "o = results[{0}];",
10625                 "var sn = o.substring(0,1),", // get + / - sign
10626                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10627                     "mn = o.substring(3,5) % 60;", // get minutes
10628                 "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
10629             ].join("\n"),
10630             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
10631         },
10632         P: {
10633             g:1,
10634             c:[
10635                 "o = results[{0}];",
10636                 "var sn = o.substring(0,1),", // get + / - sign
10637                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10638                     "mn = o.substring(4,6) % 60;", // get minutes
10639                 "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
10640             ].join("\n"),
10641             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
10642         },
10643         T: {
10644             g:0,
10645             c:null,
10646             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
10647         },
10648         Z: {
10649             g:1,
10650             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
10651                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
10652             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
10653         },
10654         c: function() {
10655             var calc = [],
10656                 arr = [
10657                     $f("Y", 1), // year
10658                     $f("m", 2), // month
10659                     $f("d", 3), // day
10660                     $f("h", 4), // hour
10661                     $f("i", 5), // minute
10662                     $f("s", 6), // second
10663                     {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)
10664                     {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
10665                         "if(results[8]) {", // timezone specified
10666                             "if(results[8] == 'Z'){",
10667                                 "zz = 0;", // UTC
10668                             "}else if (results[8].indexOf(':') > -1){",
10669                                 $f("P", 8).c, // timezone offset with colon separator
10670                             "}else{",
10671                                 $f("O", 8).c, // timezone offset without colon separator
10672                             "}",
10673                         "}"
10674                     ].join('\n')}
10675                 ];
10676
10677             for (var i = 0, l = arr.length; i < l; ++i) {
10678                 calc.push(arr[i].c);
10679             }
10680
10681             return {
10682                 g:1,
10683                 c:calc.join(""),
10684                 s:[
10685                     arr[0].s, // year (required)
10686                     "(?:", "-", arr[1].s, // month (optional)
10687                         "(?:", "-", arr[2].s, // day (optional)
10688                             "(?:",
10689                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
10690                                 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
10691                                 "(?::", arr[5].s, ")?", // seconds (optional)
10692                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
10693                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
10694                             ")?",
10695                         ")?",
10696                     ")?"
10697                 ].join("")
10698             }
10699         },
10700         U: {
10701             g:1,
10702             c:"u = parseInt(results[{0}], 10);\n",
10703             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
10704         }
10705     }
10706 });
10707
10708 }());
10709
10710 Ext.apply(Date.prototype, {
10711     // private
10712     dateFormat : function(format) {
10713         if (Date.formatFunctions[format] == null) {
10714             Date.createFormat(format);
10715         }
10716         return Date.formatFunctions[format].call(this);
10717     },
10718
10719     /**
10720      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
10721      *
10722      * Note: The date string returned by the javascript Date object's toString() method varies
10723      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
10724      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
10725      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
10726      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
10727      * from the GMT offset portion of the date string.
10728      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
10729      */
10730     getTimezone : function() {
10731         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
10732         //
10733         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
10734         // 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)
10735         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
10736         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
10737         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
10738         //
10739         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
10740         // step 1: (?:\((.*)\) -- find timezone in parentheses
10741         // 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
10742         // step 3: remove all non uppercase characters found in step 1 and 2
10743         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
10744     },
10745
10746     /**
10747      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
10748      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
10749      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
10750      */
10751     getGMTOffset : function(colon) {
10752         return (this.getTimezoneOffset() > 0 ? "-" : "+")
10753             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
10754             + (colon ? ":" : "")
10755             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
10756     },
10757
10758     /**
10759      * Get the numeric day number of the year, adjusted for leap year.
10760      * @return {Number} 0 to 364 (365 in leap years).
10761      */
10762     getDayOfYear: function() {
10763         var num = 0,
10764             d = this.clone(),
10765             m = this.getMonth(),
10766             i;
10767
10768         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
10769             num += d.getDaysInMonth();
10770         }
10771         return num + this.getDate() - 1;
10772     },
10773
10774     /**
10775      * Get the numeric ISO-8601 week number of the year.
10776      * (equivalent to the format specifier 'W', but without a leading zero).
10777      * @return {Number} 1 to 53
10778      */
10779     getWeekOfYear : function() {
10780         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
10781         var ms1d = 864e5, // milliseconds in a day
10782             ms7d = 7 * ms1d; // milliseconds in a week
10783
10784         return function() { // return a closure so constants get calculated only once
10785             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
10786                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
10787                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
10788
10789             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
10790         }
10791     }(),
10792
10793     /**
10794      * Checks if the current date falls within a leap year.
10795      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
10796      */
10797     isLeapYear : function() {
10798         var year = this.getFullYear();
10799         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
10800     },
10801
10802     /**
10803      * Get the first day of the current month, adjusted for leap year.  The returned value
10804      * is the numeric day index within the week (0-6) which can be used in conjunction with
10805      * the {@link #monthNames} array to retrieve the textual day name.
10806      * Example:
10807      * <pre><code>
10808 var dt = new Date('1/10/2007');
10809 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
10810 </code></pre>
10811      * @return {Number} The day number (0-6).
10812      */
10813     getFirstDayOfMonth : function() {
10814         var day = (this.getDay() - (this.getDate() - 1)) % 7;
10815         return (day < 0) ? (day + 7) : day;
10816     },
10817
10818     /**
10819      * Get the last day of the current month, adjusted for leap year.  The returned value
10820      * is the numeric day index within the week (0-6) which can be used in conjunction with
10821      * the {@link #monthNames} array to retrieve the textual day name.
10822      * Example:
10823      * <pre><code>
10824 var dt = new Date('1/10/2007');
10825 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
10826 </code></pre>
10827      * @return {Number} The day number (0-6).
10828      */
10829     getLastDayOfMonth : function() {
10830         return this.getLastDateOfMonth().getDay();
10831     },
10832
10833
10834     /**
10835      * Get the date of the first day of the month in which this date resides.
10836      * @return {Date}
10837      */
10838     getFirstDateOfMonth : function() {
10839         return new Date(this.getFullYear(), this.getMonth(), 1);
10840     },
10841
10842     /**
10843      * Get the date of the last day of the month in which this date resides.
10844      * @return {Date}
10845      */
10846     getLastDateOfMonth : function() {
10847         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
10848     },
10849
10850     /**
10851      * Get the number of days in the current month, adjusted for leap year.
10852      * @return {Number} The number of days in the month.
10853      */
10854     getDaysInMonth: function() {
10855         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
10856
10857         return function() { // return a closure for efficiency
10858             var m = this.getMonth();
10859
10860             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
10861         }
10862     }(),
10863
10864     /**
10865      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
10866      * @return {String} 'st, 'nd', 'rd' or 'th'.
10867      */
10868     getSuffix : function() {
10869         switch (this.getDate()) {
10870             case 1:
10871             case 21:
10872             case 31:
10873                 return "st";
10874             case 2:
10875             case 22:
10876                 return "nd";
10877             case 3:
10878             case 23:
10879                 return "rd";
10880             default:
10881                 return "th";
10882         }
10883     },
10884
10885     /**
10886      * Creates and returns a new Date instance with the exact same date value as the called instance.
10887      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
10888      * variable will also be changed.  When the intention is to create a new variable that will not
10889      * modify the original instance, you should create a clone.
10890      *
10891      * Example of correctly cloning a date:
10892      * <pre><code>
10893 //wrong way:
10894 var orig = new Date('10/1/2006');
10895 var copy = orig;
10896 copy.setDate(5);
10897 document.write(orig);  //returns 'Thu Oct 05 2006'!
10898
10899 //correct way:
10900 var orig = new Date('10/1/2006');
10901 var copy = orig.clone();
10902 copy.setDate(5);
10903 document.write(orig);  //returns 'Thu Oct 01 2006'
10904 </code></pre>
10905      * @return {Date} The new Date instance.
10906      */
10907     clone : function() {
10908         return new Date(this.getTime());
10909     },
10910
10911     /**
10912      * Checks if the current date is affected by Daylight Saving Time (DST).
10913      * @return {Boolean} True if the current date is affected by DST.
10914      */
10915     isDST : function() {
10916         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
10917         // courtesy of @geoffrey.mcgill
10918         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
10919     },
10920
10921     /**
10922      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
10923      * automatically adjusting for Daylight Saving Time (DST) where applicable.
10924      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
10925      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
10926      * @return {Date} this or the clone.
10927      */
10928     clearTime : function(clone) {
10929         if (clone) {
10930             return this.clone().clearTime();
10931         }
10932
10933         // get current date before clearing time
10934         var d = this.getDate();
10935
10936         // clear time
10937         this.setHours(0);
10938         this.setMinutes(0);
10939         this.setSeconds(0);
10940         this.setMilliseconds(0);
10941
10942         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
10943             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
10944             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
10945
10946             // increment hour until cloned date == current date
10947             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
10948
10949             this.setDate(d);
10950             this.setHours(c.getHours());
10951         }
10952
10953         return this;
10954     },
10955
10956     /**
10957      * Provides a convenient method for performing basic date arithmetic. This method
10958      * does not modify the Date instance being called - it creates and returns
10959      * a new Date instance containing the resulting date value.
10960      *
10961      * Examples:
10962      * <pre><code>
10963 // Basic usage:
10964 var dt = new Date('10/29/2006').add(Date.DAY, 5);
10965 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
10966
10967 // Negative values will be subtracted:
10968 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
10969 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
10970
10971 // You can even chain several calls together in one line:
10972 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
10973 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
10974 </code></pre>
10975      *
10976      * @param {String} interval A valid date interval enum value.
10977      * @param {Number} value The amount to add to the current date.
10978      * @return {Date} The new Date instance.
10979      */
10980     add : function(interval, value) {
10981         var d = this.clone();
10982         if (!interval || value === 0) return d;
10983
10984         switch(interval.toLowerCase()) {
10985             case Date.MILLI:
10986                 d.setMilliseconds(this.getMilliseconds() + value);
10987                 break;
10988             case Date.SECOND:
10989                 d.setSeconds(this.getSeconds() + value);
10990                 break;
10991             case Date.MINUTE:
10992                 d.setMinutes(this.getMinutes() + value);
10993                 break;
10994             case Date.HOUR:
10995                 d.setHours(this.getHours() + value);
10996                 break;
10997             case Date.DAY:
10998                 d.setDate(this.getDate() + value);
10999                 break;
11000             case Date.MONTH:
11001                 var day = this.getDate();
11002                 if (day > 28) {
11003                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11004                 }
11005                 d.setDate(day);
11006                 d.setMonth(this.getMonth() + value);
11007                 break;
11008             case Date.YEAR:
11009                 d.setFullYear(this.getFullYear() + value);
11010                 break;
11011         }
11012         return d;
11013     },
11014
11015     /**
11016      * Checks if this date falls on or between the given start and end dates.
11017      * @param {Date} start Start date
11018      * @param {Date} end End date
11019      * @return {Boolean} true if this date falls on or between the given start and end dates.
11020      */
11021     between : function(start, end) {
11022         var t = this.getTime();
11023         return start.getTime() <= t && t <= end.getTime();
11024     }
11025 });
11026
11027
11028 /**
11029  * Formats a date given the supplied format string.
11030  * @param {String} format The format string.
11031  * @return {String} The formatted date.
11032  * @method format
11033  */
11034 Date.prototype.format = Date.prototype.dateFormat;
11035
11036
11037 // private
11038 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11039     Ext.apply(Date.prototype, {
11040         _xMonth : Date.prototype.setMonth,
11041         _xDate  : Date.prototype.setDate,
11042
11043         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11044         // Date.setMonth does not work consistently if iMonth is not 0-11
11045         setMonth : function(num) {
11046             if (num <= -1) {
11047                 var n = Math.ceil(-num),
11048                     back_year = Math.ceil(n / 12),
11049                     month = (n % 12) ? 12 - n % 12 : 0;
11050
11051                 this.setFullYear(this.getFullYear() - back_year);
11052
11053                 return this._xMonth(month);
11054             } else {
11055                 return this._xMonth(num);
11056             }
11057         },
11058
11059         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11060         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11061         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11062         setDate : function(d) {
11063             // use setTime() to workaround setDate() bug
11064             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11065             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11066         }
11067     });
11068 }
11069
11070
11071
11072 /* Some basic Date tests... (requires Firebug)
11073
11074 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11075 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11076
11077 // standard tests
11078 console.group('Standard Date.parseDate() Tests');
11079     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11080     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
11081     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11082     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
11083     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
11084 console.groupEnd();
11085
11086 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11087 // -- accepts ALL 6 levels of date-time granularity
11088 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11089     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11090     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11091     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11092     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)
11093     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)
11094     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)
11095     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)
11096     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
11097 console.groupEnd();
11098
11099 //*//**
11100  * @class Ext.util.DelayedTask
11101  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
11102  * performing setTimeout where a new timeout cancels the old timeout. When called, the
11103  * task will wait the specified time period before executing. If durng that time period,
11104  * the task is called again, the original call will be cancelled. This continues so that
11105  * the function is only called a single time for each iteration.</p>
11106  * <p>This method is especially useful for things like detecting whether a user has finished
11107  * typing in a text field. An example would be performing validation on a keypress. You can
11108  * use this class to buffer the keypress events for a certain number of milliseconds, and
11109  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
11110 var task = new Ext.util.DelayedTask(function(){
11111     alert(Ext.getDom('myInputField').value.length);
11112 });
11113 // Wait 500ms before calling our function. If the user presses another key 
11114 // during that 500ms, it will be cancelled and we'll wait another 500ms.
11115 Ext.get('myInputField').on('keypress', function(){
11116     task.{@link #delay}(500); 
11117 });
11118  * </code></pre> 
11119  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
11120  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
11121  * also setup a delayed task for you to buffer events.</p> 
11122  * @constructor The parameters to this constructor serve as defaults and are not required.
11123  * @param {Function} fn (optional) The default function to call.
11124  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
11125  * function is called. If not specified, <code>this</code> will refer to the browser window.
11126  * @param {Array} args (optional) The default Array of arguments.
11127  */
11128 Ext.util.DelayedTask = function(fn, scope, args){
11129     var me = this,
11130         id,     
11131         call = function(){
11132                 clearInterval(id);
11133                 id = null;
11134                 fn.apply(scope, args || []);
11135             };
11136             
11137     /**
11138      * Cancels any pending timeout and queues a new one
11139      * @param {Number} delay The milliseconds to delay
11140      * @param {Function} newFn (optional) Overrides function passed to constructor
11141      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
11142      * is specified, <code>this</code> will refer to the browser window.
11143      * @param {Array} newArgs (optional) Overrides args passed to constructor
11144      */
11145     me.delay = function(delay, newFn, newScope, newArgs){
11146         me.cancel();
11147         fn = newFn || fn;
11148         scope = newScope || scope;
11149         args = newArgs || args;
11150         id = setInterval(call, delay);
11151     };
11152
11153     /**
11154      * Cancel the last queued timeout
11155      */
11156     me.cancel = function(){
11157         if(id){
11158             clearInterval(id);
11159             id = null;
11160         }
11161     };
11162 };/**\r
11163  * @class Ext.util.MixedCollection\r
11164  * @extends Ext.util.Observable\r
11165  * A Collection class that maintains both numeric indexes and keys and exposes events.\r
11166  * @constructor\r
11167  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}\r
11168  * function should add function references to the collection. Defaults to\r
11169  * <tt>false</tt>.\r
11170  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection\r
11171  * and return the key value for that item.  This is used when available to look up the key on items that\r
11172  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is\r
11173  * equivalent to providing an implementation for the {@link #getKey} method.\r
11174  */\r
11175 Ext.util.MixedCollection = function(allowFunctions, keyFn){\r
11176     this.items = [];\r
11177     this.map = {};\r
11178     this.keys = [];\r
11179     this.length = 0;\r
11180     this.addEvents(\r
11181         /**\r
11182          * @event clear\r
11183          * Fires when the collection is cleared.\r
11184          */\r
11185         'clear',\r
11186         /**\r
11187          * @event add\r
11188          * Fires when an item is added to the collection.\r
11189          * @param {Number} index The index at which the item was added.\r
11190          * @param {Object} o The item added.\r
11191          * @param {String} key The key associated with the added item.\r
11192          */\r
11193         'add',\r
11194         /**\r
11195          * @event replace\r
11196          * Fires when an item is replaced in the collection.\r
11197          * @param {String} key he key associated with the new added.\r
11198          * @param {Object} old The item being replaced.\r
11199          * @param {Object} new The new item.\r
11200          */\r
11201         'replace',\r
11202         /**\r
11203          * @event remove\r
11204          * Fires when an item is removed from the collection.\r
11205          * @param {Object} o The item being removed.\r
11206          * @param {String} key (optional) The key associated with the removed item.\r
11207          */\r
11208         'remove',\r
11209         'sort'\r
11210     );\r
11211     this.allowFunctions = allowFunctions === true;\r
11212     if(keyFn){\r
11213         this.getKey = keyFn;\r
11214     }\r
11215     Ext.util.MixedCollection.superclass.constructor.call(this);\r
11216 };\r
11217 \r
11218 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {\r
11219 \r
11220     /**\r
11221      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}\r
11222      * function should add function references to the collection. Defaults to\r
11223      * <tt>false</tt>.\r
11224      */\r
11225     allowFunctions : false,\r
11226 \r
11227     /**\r
11228      * Adds an item to the collection. Fires the {@link #add} event when complete.\r
11229      * @param {String} key <p>The key to associate with the item, or the new item.</p>\r
11230      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,\r
11231      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,\r
11232      * the MixedCollection will be able to <i>derive</i> the key for the new item.\r
11233      * In this case just pass the new item in this parameter.</p>\r
11234      * @param {Object} o The item to add.\r
11235      * @return {Object} The item added.\r
11236      */\r
11237     add : function(key, o){\r
11238         if(arguments.length == 1){\r
11239             o = arguments[0];\r
11240             key = this.getKey(o);\r
11241         }\r
11242         if(typeof key != 'undefined' && key !== null){\r
11243             var old = this.map[key];\r
11244             if(typeof old != 'undefined'){\r
11245                 return this.replace(key, o);\r
11246             }\r
11247             this.map[key] = o;\r
11248         }\r
11249         this.length++;\r
11250         this.items.push(o);\r
11251         this.keys.push(key);\r
11252         this.fireEvent('add', this.length-1, o, key);\r
11253         return o;\r
11254     },\r
11255 \r
11256     /**\r
11257       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation\r
11258       * simply returns <b><code>item.id</code></b> but you can provide your own implementation\r
11259       * to return a different value as in the following examples:<pre><code>\r
11260 // normal way\r
11261 var mc = new Ext.util.MixedCollection();\r
11262 mc.add(someEl.dom.id, someEl);\r
11263 mc.add(otherEl.dom.id, otherEl);\r
11264 //and so on\r
11265 \r
11266 // using getKey\r
11267 var mc = new Ext.util.MixedCollection();\r
11268 mc.getKey = function(el){\r
11269    return el.dom.id;\r
11270 };\r
11271 mc.add(someEl);\r
11272 mc.add(otherEl);\r
11273 \r
11274 // or via the constructor\r
11275 var mc = new Ext.util.MixedCollection(false, function(el){\r
11276    return el.dom.id;\r
11277 });\r
11278 mc.add(someEl);\r
11279 mc.add(otherEl);\r
11280      * </code></pre>\r
11281      * @param {Object} item The item for which to find the key.\r
11282      * @return {Object} The key for the passed item.\r
11283      */\r
11284     getKey : function(o){\r
11285          return o.id;\r
11286     },\r
11287 \r
11288     /**\r
11289      * Replaces an item in the collection. Fires the {@link #replace} event when complete.\r
11290      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>\r
11291      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key\r
11292      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection\r
11293      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item\r
11294      * with one having the same key value, then just pass the replacement item in this parameter.</p>\r
11295      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate\r
11296      * with that key.\r
11297      * @return {Object}  The new item.\r
11298      */\r
11299     replace : function(key, o){\r
11300         if(arguments.length == 1){\r
11301             o = arguments[0];\r
11302             key = this.getKey(o);\r
11303         }\r
11304         var old = this.map[key];\r
11305         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){\r
11306              return this.add(key, o);\r
11307         }\r
11308         var index = this.indexOfKey(key);\r
11309         this.items[index] = o;\r
11310         this.map[key] = o;\r
11311         this.fireEvent('replace', key, old, o);\r
11312         return o;\r
11313     },\r
11314 \r
11315     /**\r
11316      * Adds all elements of an Array or an Object to the collection.\r
11317      * @param {Object/Array} objs An Object containing properties which will be added\r
11318      * to the collection, or an Array of values, each of which are added to the collection.\r
11319      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>\r
11320      * has been set to <tt>true</tt>.\r
11321      */\r
11322     addAll : function(objs){\r
11323         if(arguments.length > 1 || Ext.isArray(objs)){\r
11324             var args = arguments.length > 1 ? arguments : objs;\r
11325             for(var i = 0, len = args.length; i < len; i++){\r
11326                 this.add(args[i]);\r
11327             }\r
11328         }else{\r
11329             for(var key in objs){\r
11330                 if(this.allowFunctions || typeof objs[key] != 'function'){\r
11331                     this.add(key, objs[key]);\r
11332                 }\r
11333             }\r
11334         }\r
11335     },\r
11336 \r
11337     /**\r
11338      * Executes the specified function once for every item in the collection, passing the following arguments:\r
11339      * <div class="mdetail-params"><ul>\r
11340      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>\r
11341      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>\r
11342      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>\r
11343      * </ul></div>\r
11344      * The function should return a boolean value. Returning false from the function will stop the iteration.\r
11345      * @param {Function} fn The function to execute for each item.\r
11346      * @param {Object} scope (optional) The scope in which to execute the function.\r
11347      */\r
11348     each : function(fn, scope){\r
11349         var items = [].concat(this.items); // each safe for removal\r
11350         for(var i = 0, len = items.length; i < len; i++){\r
11351             if(fn.call(scope || items[i], items[i], i, len) === false){\r
11352                 break;\r
11353             }\r
11354         }\r
11355     },\r
11356 \r
11357     /**\r
11358      * Executes the specified function once for every key in the collection, passing each\r
11359      * key, and its associated item as the first two parameters.\r
11360      * @param {Function} fn The function to execute for each item.\r
11361      * @param {Object} scope (optional) The scope in which to execute the function.\r
11362      */\r
11363     eachKey : function(fn, scope){\r
11364         for(var i = 0, len = this.keys.length; i < len; i++){\r
11365             fn.call(scope || window, this.keys[i], this.items[i], i, len);\r
11366         }\r
11367     },\r
11368 \r
11369     /**\r
11370      * Returns the first item in the collection which elicits a true return value from the\r
11371      * passed selection function.\r
11372      * @param {Function} fn The selection function to execute for each item.\r
11373      * @param {Object} scope (optional) The scope in which to execute the function.\r
11374      * @return {Object} The first item in the collection which returned true from the selection function.\r
11375      */\r
11376     find : function(fn, scope){\r
11377         for(var i = 0, len = this.items.length; i < len; i++){\r
11378             if(fn.call(scope || window, this.items[i], this.keys[i])){\r
11379                 return this.items[i];\r
11380             }\r
11381         }\r
11382         return null;\r
11383     },\r
11384 \r
11385     /**\r
11386      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.\r
11387      * @param {Number} index The index to insert the item at.\r
11388      * @param {String} key The key to associate with the new item, or the item itself.\r
11389      * @param {Object} o (optional) If the second parameter was a key, the new item.\r
11390      * @return {Object} The item inserted.\r
11391      */\r
11392     insert : function(index, key, o){\r
11393         if(arguments.length == 2){\r
11394             o = arguments[1];\r
11395             key = this.getKey(o);\r
11396         }\r
11397         if(this.containsKey(key)){\r
11398             this.suspendEvents();\r
11399             this.removeKey(key);\r
11400             this.resumeEvents();\r
11401         }\r
11402         if(index >= this.length){\r
11403             return this.add(key, o);\r
11404         }\r
11405         this.length++;\r
11406         this.items.splice(index, 0, o);\r
11407         if(typeof key != 'undefined' && key !== null){\r
11408             this.map[key] = o;\r
11409         }\r
11410         this.keys.splice(index, 0, key);\r
11411         this.fireEvent('add', index, o, key);\r
11412         return o;\r
11413     },\r
11414 \r
11415     /**\r
11416      * Remove an item from the collection.\r
11417      * @param {Object} o The item to remove.\r
11418      * @return {Object} The item removed or false if no item was removed.\r
11419      */\r
11420     remove : function(o){\r
11421         return this.removeAt(this.indexOf(o));\r
11422     },\r
11423 \r
11424     /**\r
11425      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.\r
11426      * @param {Number} index The index within the collection of the item to remove.\r
11427      * @return {Object} The item removed or false if no item was removed.\r
11428      */\r
11429     removeAt : function(index){\r
11430         if(index < this.length && index >= 0){\r
11431             this.length--;\r
11432             var o = this.items[index];\r
11433             this.items.splice(index, 1);\r
11434             var key = this.keys[index];\r
11435             if(typeof key != 'undefined'){\r
11436                 delete this.map[key];\r
11437             }\r
11438             this.keys.splice(index, 1);\r
11439             this.fireEvent('remove', o, key);\r
11440             return o;\r
11441         }\r
11442         return false;\r
11443     },\r
11444 \r
11445     /**\r
11446      * Removed an item associated with the passed key fom the collection.\r
11447      * @param {String} key The key of the item to remove.\r
11448      * @return {Object} The item removed or false if no item was removed.\r
11449      */\r
11450     removeKey : function(key){\r
11451         return this.removeAt(this.indexOfKey(key));\r
11452     },\r
11453 \r
11454     /**\r
11455      * Returns the number of items in the collection.\r
11456      * @return {Number} the number of items in the collection.\r
11457      */\r
11458     getCount : function(){\r
11459         return this.length;\r
11460     },\r
11461     \r
11462     /**\r
11463      * Returns index within the collection of the passed Object.\r
11464      * @param {Object} o The item to find the index of.\r
11465      * @return {Number} index of the item. Returns -1 if not found.\r
11466      */\r
11467     indexOf : function(o){\r
11468         return this.items.indexOf(o);\r
11469     },\r
11470 \r
11471     /**\r
11472      * Returns index within the collection of the passed key.\r
11473      * @param {String} key The key to find the index of.\r
11474      * @return {Number} index of the key.\r
11475      */\r
11476     indexOfKey : function(key){\r
11477         return this.keys.indexOf(key);\r
11478     },\r
11479 \r
11480     /**\r
11481      * Returns the item associated with the passed key OR index.\r
11482      * Key has priority over index.  This is the equivalent\r
11483      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.\r
11484      * @param {String/Number} key The key or index of the item.\r
11485      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.\r
11486      * If an item was found, but is a Class, returns <tt>null</tt>.\r
11487      */\r
11488     item : function(key){\r
11489         var mk = this.map[key],\r
11490             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;\r
11491         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!\r
11492     },\r
11493 \r
11494     /**\r
11495      * Returns the item at the specified index.\r
11496      * @param {Number} index The index of the item.\r
11497      * @return {Object} The item at the specified index.\r
11498      */\r
11499     itemAt : function(index){\r
11500         return this.items[index];\r
11501     },\r
11502 \r
11503     /**\r
11504      * Returns the item associated with the passed key.\r
11505      * @param {String/Number} key The key of the item.\r
11506      * @return {Object} The item associated with the passed key.\r
11507      */\r
11508     key : function(key){\r
11509         return this.map[key];\r
11510     },\r
11511 \r
11512     /**\r
11513      * Returns true if the collection contains the passed Object as an item.\r
11514      * @param {Object} o  The Object to look for in the collection.\r
11515      * @return {Boolean} True if the collection contains the Object as an item.\r
11516      */\r
11517     contains : function(o){\r
11518         return this.indexOf(o) != -1;\r
11519     },\r
11520 \r
11521     /**\r
11522      * Returns true if the collection contains the passed Object as a key.\r
11523      * @param {String} key The key to look for in the collection.\r
11524      * @return {Boolean} True if the collection contains the Object as a key.\r
11525      */\r
11526     containsKey : function(key){\r
11527         return typeof this.map[key] != 'undefined';\r
11528     },\r
11529 \r
11530     /**\r
11531      * Removes all items from the collection.  Fires the {@link #clear} event when complete.\r
11532      */\r
11533     clear : function(){\r
11534         this.length = 0;\r
11535         this.items = [];\r
11536         this.keys = [];\r
11537         this.map = {};\r
11538         this.fireEvent('clear');\r
11539     },\r
11540 \r
11541     /**\r
11542      * Returns the first item in the collection.\r
11543      * @return {Object} the first item in the collection..\r
11544      */\r
11545     first : function(){\r
11546         return this.items[0];\r
11547     },\r
11548 \r
11549     /**\r
11550      * Returns the last item in the collection.\r
11551      * @return {Object} the last item in the collection..\r
11552      */\r
11553     last : function(){\r
11554         return this.items[this.length-1];\r
11555     },\r
11556 \r
11557     /**\r
11558      * @private\r
11559      * @param {String} property Property to sort by ('key', 'value', or 'index')\r
11560      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.\r
11561      * @param {Function} fn (optional) Comparison function that defines the sort order.\r
11562      * Defaults to sorting by numeric value.  \r
11563      */\r
11564     _sort : function(property, dir, fn){\r
11565         var i,\r
11566             len,\r
11567             dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,\r
11568             c = [], k = this.keys, items = this.items;\r
11569             \r
11570         fn = fn || function(a, b){\r
11571             return a-b;\r
11572         };\r
11573         for(i = 0, len = items.length; i < len; i++){\r
11574             c[c.length] = {key: k[i], value: items[i], index: i};\r
11575         }\r
11576         c.sort(function(a, b){\r
11577             var v = fn(a[property], b[property]) * dsc;\r
11578             if(v === 0){\r
11579                 v = (a.index < b.index ? -1 : 1);\r
11580             }\r
11581             return v;\r
11582         });\r
11583         for(i = 0, len = c.length; i < len; i++){\r
11584             items[i] = c[i].value;\r
11585             k[i] = c[i].key;\r
11586         }\r
11587         this.fireEvent('sort', this);\r
11588     },\r
11589 \r
11590     /**\r
11591      * Sorts this collection by <b>item</b> value with the passed comparison function.\r
11592      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.\r
11593      * @param {Function} fn (optional) Comparison function that defines the sort order.\r
11594      * Defaults to sorting by numeric value.  \r
11595      */\r
11596     sort : function(dir, fn){\r
11597         this._sort('value', dir, fn);\r
11598     },\r
11599 \r
11600     /**\r
11601      * Sorts this collection by <b>key</b>s.\r
11602      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.\r
11603      * @param {Function} fn (optional) Comparison function that defines the sort order.\r
11604      * Defaults to sorting by case insensitive string.  \r
11605      */\r
11606     keySort : function(dir, fn){\r
11607         this._sort('key', dir, fn || function(a, b){\r
11608             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();\r
11609             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);\r
11610         });\r
11611     },\r
11612 \r
11613     /**\r
11614      * Returns a range of items in this collection\r
11615      * @param {Number} startIndex (optional) The starting index. Defaults to 0.\r
11616      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.\r
11617      * @return {Array} An array of items\r
11618      */\r
11619     getRange : function(start, end){\r
11620         var items = this.items;\r
11621         if(items.length < 1){\r
11622             return [];\r
11623         }\r
11624         start = start || 0;\r
11625         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);\r
11626         var i, r = [];\r
11627         if(start <= end){\r
11628             for(i = start; i <= end; i++) {\r
11629                 r[r.length] = items[i];\r
11630             }\r
11631         }else{\r
11632             for(i = start; i >= end; i--) {\r
11633                 r[r.length] = items[i];\r
11634             }\r
11635         }\r
11636         return r;\r
11637     },\r
11638 \r
11639     /**\r
11640      * Filter the <i>objects</i> in this collection by a specific property.\r
11641      * Returns a new collection that has been filtered.\r
11642      * @param {String} property A property on your objects\r
11643      * @param {String/RegExp} value Either string that the property values\r
11644      * should start with or a RegExp to test against the property\r
11645      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning\r
11646      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).\r
11647      * @return {MixedCollection} The new filtered collection\r
11648      */\r
11649     filter : function(property, value, anyMatch, caseSensitive){\r
11650         if(Ext.isEmpty(value, false)){\r
11651             return this.clone();\r
11652         }\r
11653         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
11654         return this.filterBy(function(o){\r
11655             return o && value.test(o[property]);\r
11656         });\r
11657     },\r
11658 \r
11659     /**\r
11660      * Filter by a function. Returns a <i>new</i> collection that has been filtered.\r
11661      * The passed function will be called with each object in the collection.\r
11662      * If the function returns true, the value is included otherwise it is filtered.\r
11663      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)\r
11664      * @param {Object} scope (optional) The scope of the function (defaults to this)\r
11665      * @return {MixedCollection} The new filtered collection\r
11666      */\r
11667     filterBy : function(fn, scope){\r
11668         var r = new Ext.util.MixedCollection();\r
11669         r.getKey = this.getKey;\r
11670         var k = this.keys, it = this.items;\r
11671         for(var i = 0, len = it.length; i < len; i++){\r
11672             if(fn.call(scope||this, it[i], k[i])){\r
11673                 r.add(k[i], it[i]);\r
11674             }\r
11675         }\r
11676         return r;\r
11677     },\r
11678 \r
11679     /**\r
11680      * Finds the index of the first matching object in this collection by a specific property/value.\r
11681      * @param {String} property The name of a property on your objects.\r
11682      * @param {String/RegExp} value A string that the property values\r
11683      * should start with or a RegExp to test against the property.\r
11684      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
11685      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.\r
11686      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.\r
11687      * @return {Number} The matched index or -1\r
11688      */\r
11689     findIndex : function(property, value, start, anyMatch, caseSensitive){\r
11690         if(Ext.isEmpty(value, false)){\r
11691             return -1;\r
11692         }\r
11693         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
11694         return this.findIndexBy(function(o){\r
11695             return o && value.test(o[property]);\r
11696         }, null, start);\r
11697     },\r
11698 \r
11699     /**\r
11700      * Find the index of the first matching object in this collection by a function.\r
11701      * If the function returns <i>true</i> it is considered a match.\r
11702      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).\r
11703      * @param {Object} scope (optional) The scope of the function (defaults to this).\r
11704      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
11705      * @return {Number} The matched index or -1\r
11706      */\r
11707     findIndexBy : function(fn, scope, start){\r
11708         var k = this.keys, it = this.items;\r
11709         for(var i = (start||0), len = it.length; i < len; i++){\r
11710             if(fn.call(scope||this, it[i], k[i])){\r
11711                 return i;\r
11712             }\r
11713         }\r
11714         return -1;\r
11715     },\r
11716 \r
11717     // private\r
11718     createValueMatcher : function(value, anyMatch, caseSensitive){\r
11719         if(!value.exec){ // not a regex\r
11720             value = String(value);\r
11721             value = new RegExp((anyMatch === true ? '' : '^') + Ext.escapeRe(value), caseSensitive ? '' : 'i');\r
11722         }\r
11723         return value;\r
11724     },\r
11725 \r
11726     /**\r
11727      * Creates a shallow copy of this collection\r
11728      * @return {MixedCollection}\r
11729      */\r
11730     clone : function(){\r
11731         var r = new Ext.util.MixedCollection();\r
11732         var k = this.keys, it = this.items;\r
11733         for(var i = 0, len = it.length; i < len; i++){\r
11734             r.add(k[i], it[i]);\r
11735         }\r
11736         r.getKey = this.getKey;\r
11737         return r;\r
11738     }\r
11739 });\r
11740 /**\r
11741  * This method calls {@link #item item()}.\r
11742  * Returns the item associated with the passed key OR index. Key has priority\r
11743  * over index.  This is the equivalent of calling {@link #key} first, then if\r
11744  * nothing matched calling {@link #itemAt}.\r
11745  * @param {String/Number} key The key or index of the item.\r
11746  * @return {Object} If the item is found, returns the item.  If the item was\r
11747  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,\r
11748  * returns <tt>null</tt>.\r
11749  */\r
11750 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
11751  * @class Ext.util.JSON
11752  * Modified version of Douglas Crockford"s json.js that doesn"t
11753  * mess with the Object prototype
11754  * http://www.json.org/js.html
11755  * @singleton
11756  */
11757 Ext.util.JSON = new (function(){
11758     var useHasOwn = !!{}.hasOwnProperty,
11759         isNative = function() {
11760             var useNative = null;
11761
11762             return function() {
11763                 if (useNative === null) {
11764                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
11765                 }
11766         
11767                 return useNative;
11768             };
11769         }(),
11770         pad = function(n) {
11771             return n < 10 ? "0" + n : n;
11772         },
11773         doDecode = function(json){
11774             return eval("(" + json + ')');    
11775         },
11776         doEncode = function(o){
11777             if(!Ext.isDefined(o) || o === null){
11778                 return "null";
11779             }else if(Ext.isArray(o)){
11780                 return encodeArray(o);
11781             }else if(Ext.isDate(o)){
11782                 return Ext.util.JSON.encodeDate(o);
11783             }else if(Ext.isString(o)){
11784                 return encodeString(o);
11785             }else if(typeof o == "number"){
11786                 //don't use isNumber here, since finite checks happen inside isNumber
11787                 return isFinite(o) ? String(o) : "null";
11788             }else if(Ext.isBoolean(o)){
11789                 return String(o);
11790             }else {
11791                 var a = ["{"], b, i, v;
11792                 for (i in o) {
11793                     // don't encode DOM objects
11794                     if(!o.getElementsByTagName){
11795                         if(!useHasOwn || o.hasOwnProperty(i)) {
11796                             v = o[i];
11797                             switch (typeof v) {
11798                             case "undefined":
11799                             case "function":
11800                             case "unknown":
11801                                 break;
11802                             default:
11803                                 if(b){
11804                                     a.push(',');
11805                                 }
11806                                 a.push(doEncode(i), ":",
11807                                         v === null ? "null" : doEncode(v));
11808                                 b = true;
11809                             }
11810                         }
11811                     }
11812                 }
11813                 a.push("}");
11814                 return a.join("");
11815             }    
11816         },
11817         m = {
11818             "\b": '\\b',
11819             "\t": '\\t',
11820             "\n": '\\n',
11821             "\f": '\\f',
11822             "\r": '\\r',
11823             '"' : '\\"',
11824             "\\": '\\\\'
11825         },
11826         encodeString = function(s){
11827             if (/["\\\x00-\x1f]/.test(s)) {
11828                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
11829                     var c = m[b];
11830                     if(c){
11831                         return c;
11832                     }
11833                     c = b.charCodeAt();
11834                     return "\\u00" +
11835                         Math.floor(c / 16).toString(16) +
11836                         (c % 16).toString(16);
11837                 }) + '"';
11838             }
11839             return '"' + s + '"';
11840         },
11841         encodeArray = function(o){
11842             var a = ["["], b, i, l = o.length, v;
11843                 for (i = 0; i < l; i += 1) {
11844                     v = o[i];
11845                     switch (typeof v) {
11846                         case "undefined":
11847                         case "function":
11848                         case "unknown":
11849                             break;
11850                         default:
11851                             if (b) {
11852                                 a.push(',');
11853                             }
11854                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
11855                             b = true;
11856                     }
11857                 }
11858                 a.push("]");
11859                 return a.join("");
11860         };
11861
11862     this.encodeDate = function(o){
11863         return '"' + o.getFullYear() + "-" +
11864                 pad(o.getMonth() + 1) + "-" +
11865                 pad(o.getDate()) + "T" +
11866                 pad(o.getHours()) + ":" +
11867                 pad(o.getMinutes()) + ":" +
11868                 pad(o.getSeconds()) + '"';
11869     };
11870
11871     /**
11872      * Encodes an Object, Array or other value
11873      * @param {Mixed} o The variable to encode
11874      * @return {String} The JSON string
11875      */
11876     this.encode = function() {
11877         var ec;
11878         return function(o) {
11879             if (!ec) {
11880                 // setup encoding function on first access
11881                 ec = isNative() ? JSON.stringify : doEncode;
11882             }
11883             return ec(o);
11884         };
11885     }();
11886
11887
11888     /**
11889      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
11890      * @param {String} json The JSON string
11891      * @return {Object} The resulting object
11892      */
11893     this.decode = function() {
11894         var dc;
11895         return function(json) {
11896             if (!dc) {
11897                 // setup decoding function on first access
11898                 dc = isNative() ? JSON.parse : doDecode;
11899             }
11900             return dc(json);
11901         };
11902     }();
11903
11904 })();
11905 /**
11906  * Shorthand for {@link Ext.util.JSON#encode}
11907  * @param {Mixed} o The variable to encode
11908  * @return {String} The JSON string
11909  * @member Ext
11910  * @method encode
11911  */
11912 Ext.encode = Ext.util.JSON.encode;
11913 /**
11914  * Shorthand for {@link Ext.util.JSON#decode}
11915  * @param {String} json The JSON string
11916  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
11917  * @return {Object} The resulting object
11918  * @member Ext
11919  * @method decode
11920  */
11921 Ext.decode = Ext.util.JSON.decode;
11922 /**\r
11923  * @class Ext.util.Format\r
11924  * Reusable data formatting functions\r
11925  * @singleton\r
11926  */\r
11927 Ext.util.Format = function(){\r
11928     var trimRe = /^\s+|\s+$/g;\r
11929     return {\r
11930         /**\r
11931          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length\r
11932          * @param {String} value The string to truncate\r
11933          * @param {Number} length The maximum length to allow before truncating\r
11934          * @param {Boolean} word True to try to find a common work break\r
11935          * @return {String} The converted text\r
11936          */\r
11937         ellipsis : function(value, len, word){\r
11938             if(value && value.length > len){\r
11939                 if(word){\r
11940                     var vs = value.substr(0, len - 2);\r
11941                     var index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));\r
11942                     if(index == -1 || index < (len - 15)){\r
11943                         return value.substr(0, len - 3) + "...";\r
11944                     }else{\r
11945                         return vs.substr(0, index) + "...";\r
11946                     }\r
11947                 } else{\r
11948                     return value.substr(0, len - 3) + "...";\r
11949                 }\r
11950             }\r
11951             return value;\r
11952         },\r
11953 \r
11954         /**\r
11955          * Checks a reference and converts it to empty string if it is undefined\r
11956          * @param {Mixed} value Reference to check\r
11957          * @return {Mixed} Empty string if converted, otherwise the original value\r
11958          */\r
11959         undef : function(value){\r
11960             return value !== undefined ? value : "";\r
11961         },\r
11962 \r
11963         /**\r
11964          * Checks a reference and converts it to the default value if it's empty\r
11965          * @param {Mixed} value Reference to check\r
11966          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")\r
11967          * @return {String}\r
11968          */\r
11969         defaultValue : function(value, defaultValue){\r
11970             return value !== undefined && value !== '' ? value : defaultValue;\r
11971         },\r
11972 \r
11973         /**\r
11974          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.\r
11975          * @param {String} value The string to encode\r
11976          * @return {String} The encoded text\r
11977          */\r
11978         htmlEncode : function(value){\r
11979             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");\r
11980         },\r
11981 \r
11982         /**\r
11983          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.\r
11984          * @param {String} value The string to decode\r
11985          * @return {String} The decoded text\r
11986          */\r
11987         htmlDecode : function(value){\r
11988             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");\r
11989         },\r
11990 \r
11991         /**\r
11992          * Trims any whitespace from either side of a string\r
11993          * @param {String} value The text to trim\r
11994          * @return {String} The trimmed text\r
11995          */\r
11996         trim : function(value){\r
11997             return String(value).replace(trimRe, "");\r
11998         },\r
11999 \r
12000         /**\r
12001          * Returns a substring from within an original string\r
12002          * @param {String} value The original text\r
12003          * @param {Number} start The start index of the substring\r
12004          * @param {Number} length The length of the substring\r
12005          * @return {String} The substring\r
12006          */\r
12007         substr : function(value, start, length){\r
12008             return String(value).substr(start, length);\r
12009         },\r
12010 \r
12011         /**\r
12012          * Converts a string to all lower case letters\r
12013          * @param {String} value The text to convert\r
12014          * @return {String} The converted text\r
12015          */\r
12016         lowercase : function(value){\r
12017             return String(value).toLowerCase();\r
12018         },\r
12019 \r
12020         /**\r
12021          * Converts a string to all upper case letters\r
12022          * @param {String} value The text to convert\r
12023          * @return {String} The converted text\r
12024          */\r
12025         uppercase : function(value){\r
12026             return String(value).toUpperCase();\r
12027         },\r
12028 \r
12029         /**\r
12030          * Converts the first character only of a string to upper case\r
12031          * @param {String} value The text to convert\r
12032          * @return {String} The converted text\r
12033          */\r
12034         capitalize : function(value){\r
12035             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();\r
12036         },\r
12037 \r
12038         // private\r
12039         call : function(value, fn){\r
12040             if(arguments.length > 2){\r
12041                 var args = Array.prototype.slice.call(arguments, 2);\r
12042                 args.unshift(value);\r
12043                 return eval(fn).apply(window, args);\r
12044             }else{\r
12045                 return eval(fn).call(window, value);\r
12046             }\r
12047         },\r
12048 \r
12049         /**\r
12050          * Format a number as US currency\r
12051          * @param {Number/String} value The numeric value to format\r
12052          * @return {String} The formatted currency string\r
12053          */\r
12054         usMoney : function(v){\r
12055             v = (Math.round((v-0)*100))/100;\r
12056             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);\r
12057             v = String(v);\r
12058             var ps = v.split('.');\r
12059             var whole = ps[0];\r
12060             var sub = ps[1] ? '.'+ ps[1] : '.00';\r
12061             var r = /(\d+)(\d{3})/;\r
12062             while (r.test(whole)) {\r
12063                 whole = whole.replace(r, '$1' + ',' + '$2');\r
12064             }\r
12065             v = whole + sub;\r
12066             if(v.charAt(0) == '-'){\r
12067                 return '-$' + v.substr(1);\r
12068             }\r
12069             return "$" +  v;\r
12070         },\r
12071 \r
12072         /**\r
12073          * Parse a value into a formatted date using the specified format pattern.\r
12074          * @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
12075          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')\r
12076          * @return {String} The formatted date string\r
12077          */\r
12078         date : function(v, format){\r
12079             if(!v){\r
12080                 return "";\r
12081             }\r
12082             if(!Ext.isDate(v)){\r
12083                 v = new Date(Date.parse(v));\r
12084             }\r
12085             return v.dateFormat(format || "m/d/Y");\r
12086         },\r
12087 \r
12088         /**\r
12089          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently\r
12090          * @param {String} format Any valid date format string\r
12091          * @return {Function} The date formatting function\r
12092          */\r
12093         dateRenderer : function(format){\r
12094             return function(v){\r
12095                 return Ext.util.Format.date(v, format);\r
12096             };\r
12097         },\r
12098 \r
12099         // private\r
12100         stripTagsRE : /<\/?[^>]+>/gi,\r
12101         \r
12102         /**\r
12103          * Strips all HTML tags\r
12104          * @param {Mixed} value The text from which to strip tags\r
12105          * @return {String} The stripped text\r
12106          */\r
12107         stripTags : function(v){\r
12108             return !v ? v : String(v).replace(this.stripTagsRE, "");\r
12109         },\r
12110 \r
12111         stripScriptsRe : /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
12112 \r
12113         /**\r
12114          * Strips all script tags\r
12115          * @param {Mixed} value The text from which to strip script tags\r
12116          * @return {String} The stripped text\r
12117          */\r
12118         stripScripts : function(v){\r
12119             return !v ? v : String(v).replace(this.stripScriptsRe, "");\r
12120         },\r
12121 \r
12122         /**\r
12123          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)\r
12124          * @param {Number/String} size The numeric value to format\r
12125          * @return {String} The formatted file size\r
12126          */\r
12127         fileSize : function(size){\r
12128             if(size < 1024) {\r
12129                 return size + " bytes";\r
12130             } else if(size < 1048576) {\r
12131                 return (Math.round(((size*10) / 1024))/10) + " KB";\r
12132             } else {\r
12133                 return (Math.round(((size*10) / 1048576))/10) + " MB";\r
12134             }\r
12135         },\r
12136 \r
12137         /**\r
12138          * It does simple math for use in a template, for example:<pre><code>\r
12139          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');\r
12140          * </code></pre>\r
12141          * @return {Function} A function that operates on the passed value.\r
12142          */\r
12143         math : function(){\r
12144             var fns = {};\r
12145             return function(v, a){\r
12146                 if(!fns[a]){\r
12147                     fns[a] = new Function('v', 'return v ' + a + ';');\r
12148                 }\r
12149                 return fns[a](v);\r
12150             }\r
12151         }(),\r
12152 \r
12153         /**\r
12154          * Rounds the passed number to the required decimal precision.\r
12155          * @param {Number/String} value The numeric value to round.\r
12156          * @param {Number} precision The number of decimal places to which to round the first parameter's value.\r
12157          * @return {Number} The rounded value.\r
12158          */\r
12159         round : function(value, precision) {\r
12160             var result = Number(value);\r
12161             if (typeof precision == 'number') {\r
12162                 precision = Math.pow(10, precision);\r
12163                 result = Math.round(value * precision) / precision;\r
12164             }\r
12165             return result;\r
12166         },\r
12167 \r
12168         /**\r
12169          * Formats the number according to the format string.\r
12170          * <div style="margin-left:40px">examples (123456.789):\r
12171          * <div style="margin-left:10px">\r
12172          * 0 - (123456) show only digits, no precision<br>\r
12173          * 0.00 - (123456.78) show only digits, 2 precision<br>\r
12174          * 0.0000 - (123456.7890) show only digits, 4 precision<br>\r
12175          * 0,000 - (123,456) show comma and digits, no precision<br>\r
12176          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>\r
12177          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>\r
12178          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.\r
12179          * For example: 0.000,00/i\r
12180          * </div></div>\r
12181          * @param {Number} v The number to format.\r
12182          * @param {String} format The way you would like to format this text.\r
12183          * @return {String} The formatted number.\r
12184          */\r
12185         number: function(v, format) {\r
12186             if(!format){\r
12187                         return v;\r
12188                     }\r
12189                     v = Ext.num(v, NaN);\r
12190             if (isNaN(v)){\r
12191                 return '';\r
12192             }\r
12193                     var comma = ',',\r
12194                         dec = '.',\r
12195                         i18n = false,\r
12196                         neg = v < 0;\r
12197                 \r
12198                     v = Math.abs(v);\r
12199                     if(format.substr(format.length - 2) == '/i'){\r
12200                         format = format.substr(0, format.length - 2);\r
12201                         i18n = true;\r
12202                         comma = '.';\r
12203                         dec = ',';\r
12204                     }\r
12205                 \r
12206                     var hasComma = format.indexOf(comma) != -1, \r
12207                         psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);\r
12208                 \r
12209                     if(1 < psplit.length){\r
12210                         v = v.toFixed(psplit[1].length);\r
12211                     }else if(2 < psplit.length){\r
12212                         throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);\r
12213                     }else{\r
12214                         v = v.toFixed(0);\r
12215                     }\r
12216                 \r
12217                     var fnum = v.toString();\r
12218                     if(hasComma){\r
12219                         psplit = fnum.split('.');\r
12220                 \r
12221                         var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;\r
12222                 \r
12223                         for(var i = 0; i < j; i += n){\r
12224                             if(i != 0){\r
12225                                 n = 3;\r
12226                             }\r
12227                             parr[parr.length] = cnum.substr(i, n);\r
12228                             m -= 1;\r
12229                         }\r
12230                         fnum = parr.join(comma);\r
12231                         if(psplit[1]){\r
12232                             fnum += dec + psplit[1];\r
12233                         }\r
12234                     }\r
12235                 \r
12236                     return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);\r
12237         },\r
12238 \r
12239         /**\r
12240          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently\r
12241          * @param {String} format Any valid number format string for {@link #number}\r
12242          * @return {Function} The number formatting function\r
12243          */\r
12244         numberRenderer : function(format){\r
12245             return function(v){\r
12246                 return Ext.util.Format.number(v, format);\r
12247             };\r
12248         },\r
12249 \r
12250         /**\r
12251          * Selectively do a plural form of a word based on a numeric value. For example, in a template,\r
12252          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"\r
12253          * if the value is 0 or greater than 1.\r
12254          * @param {Number} value The value to compare against\r
12255          * @param {String} singular The singular form of the word\r
12256          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")\r
12257          */\r
12258         plural : function(v, s, p){\r
12259             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));\r
12260         },\r
12261         \r
12262         /**\r
12263          * Converts newline characters to the HTML tag &lt;br/>\r
12264          * @param {String} The string value to format.\r
12265          * @return {String} The string with embedded &lt;br/> tags in place of newlines.\r
12266          */\r
12267         nl2br : function(v){\r
12268             return v === undefined || v === null ? '' : v.replace(/\n/g, '<br/>');\r
12269         }\r
12270     }\r
12271 }();/**
12272  * @class Ext.XTemplate
12273  * @extends Ext.Template
12274  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12275  * <li>Autofilling arrays using templates and sub-templates</li>
12276  * <li>Conditional processing with basic comparison operators</li>
12277  * <li>Basic math function support</li>
12278  * <li>Execute arbitrary inline code with special built-in template variables</li>
12279  * <li>Custom member functions</li>
12280  * <li>Many special tags and built-in operators that aren't defined as part of
12281  * the API, but are supported in the templates that can be created</li>
12282  * </ul></div></p>
12283  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12284  * <li>{@link Ext.DataView}</li>
12285  * <li>{@link Ext.ListView}</li>
12286  * <li>{@link Ext.form.ComboBox}</li>
12287  * <li>{@link Ext.grid.TemplateColumn}</li>
12288  * <li>{@link Ext.grid.GroupingView}</li>
12289  * <li>{@link Ext.menu.Item}</li>
12290  * <li>{@link Ext.layout.MenuLayout}</li>
12291  * <li>{@link Ext.ColorPalette}</li>
12292  * </ul></div></p>
12293  * 
12294  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
12295  *   
12296  * @constructor
12297  * The {@link Ext.Template#Template Ext.Template constructor} describes
12298  * the acceptable parameters to pass to the constructor. The following
12299  * examples demonstrate all of the supported features.</p>
12300  * 
12301  * <div class="mdetail-params"><ul>
12302  * 
12303  * <li><b><u>Sample Data</u></b> 
12304  * <div class="sub-desc">
12305  * <p>This is the data object used for reference in each code example:</p>
12306  * <pre><code>
12307 var data = {
12308     name: 'Jack Slocum',
12309     title: 'Lead Developer',
12310     company: 'Ext JS, LLC',
12311     email: 'jack@extjs.com',
12312     address: '4 Red Bulls Drive',
12313     city: 'Cleveland',
12314     state: 'Ohio',
12315     zip: '44102',
12316     drinks: ['Red Bull', 'Coffee', 'Water'],
12317     kids: [{
12318         name: 'Sara Grace',
12319         age:3
12320     },{
12321         name: 'Zachary',
12322         age:2
12323     },{
12324         name: 'John James',
12325         age:0
12326     }]
12327 };
12328  * </code></pre>
12329  * </div>
12330  * </li>
12331  * 
12332  * 
12333  * <li><b><u>Auto filling of arrays</u></b> 
12334  * <div class="sub-desc">
12335  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
12336  * to process the provided data object:
12337  * <ul>
12338  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
12339  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
12340  * array.</li>
12341  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
12342  * <li>While processing an array, the special variable <tt>{#}</tt>
12343  * will provide the current array index + 1 (starts at 1, not 0).</li>
12344  * </ul>
12345  * </p>
12346  * <pre><code>
12347 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
12348 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
12349 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
12350  * </code></pre>
12351  * Using the sample data above:
12352  * <pre><code>
12353 var tpl = new Ext.XTemplate(
12354     '&lt;p>Kids: ',
12355     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
12356         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
12357     '&lt;/tpl>&lt;/p>'
12358 );
12359 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12360  * </code></pre>
12361  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
12362  * to access specified members of the provided data object to populate the template:</p>
12363  * <pre><code>
12364 var tpl = new Ext.XTemplate(
12365     '&lt;p>Name: {name}&lt;/p>',
12366     '&lt;p>Title: {title}&lt;/p>',
12367     '&lt;p>Company: {company}&lt;/p>',
12368     '&lt;p>Kids: ',
12369     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
12370         '&lt;p>{name}&lt;/p>',
12371     '&lt;/tpl>&lt;/p>'
12372 );
12373 tpl.overwrite(panel.body, data);  // pass the root node of the data object
12374  * </code></pre>
12375  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
12376  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
12377  * will represent the value of the array at the current index:</p>
12378  * <pre><code>
12379 var tpl = new Ext.XTemplate(
12380     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
12381     '&lt;tpl for="drinks">',
12382        '&lt;div> - {.}&lt;/div>',
12383     '&lt;/tpl>'
12384 );
12385 tpl.overwrite(panel.body, data);
12386  * </code></pre>
12387  * <p>When processing a sub-template, for example while looping through a child array,
12388  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
12389  * <pre><code>
12390 var tpl = new Ext.XTemplate(
12391     '&lt;p>Name: {name}&lt;/p>',
12392     '&lt;p>Kids: ',
12393     '&lt;tpl for="kids">',
12394         '&lt;tpl if="age > 1">',
12395             '&lt;p>{name}&lt;/p>',
12396             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
12397         '&lt;/tpl>',
12398     '&lt;/tpl>&lt;/p>'
12399 );
12400 tpl.overwrite(panel.body, data);
12401  * </code></pre>
12402  * </div>
12403  * </li>
12404  * 
12405  * 
12406  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
12407  * <div class="sub-desc">
12408  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
12409  * to provide conditional checks for deciding whether or not to render specific
12410  * parts of the template. Notes:<div class="sub-desc"><ul>
12411  * <li>Double quotes must be encoded if used within the conditional</li>
12412  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
12413  * <tt>if</tt> statements should be used.</li>
12414  * </ul></div>
12415  * <pre><code>
12416 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
12417 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
12418 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
12419 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
12420 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
12421 // no good:
12422 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
12423 // encode &#34; if it is part of the condition, e.g.
12424 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
12425  * </code></pre>
12426  * Using the sample data above:
12427  * <pre><code>
12428 var tpl = new Ext.XTemplate(
12429     '&lt;p>Name: {name}&lt;/p>',
12430     '&lt;p>Kids: ',
12431     '&lt;tpl for="kids">',
12432         '&lt;tpl if="age > 1">',
12433             '&lt;p>{name}&lt;/p>',
12434         '&lt;/tpl>',
12435     '&lt;/tpl>&lt;/p>'
12436 );
12437 tpl.overwrite(panel.body, data);
12438  * </code></pre>
12439  * </div>
12440  * </li>
12441  * 
12442  * 
12443  * <li><b><u>Basic math support</u></b> 
12444  * <div class="sub-desc">
12445  * <p>The following basic math operators may be applied directly on numeric
12446  * data values:</p><pre>
12447  * + - * /
12448  * </pre>
12449  * For example:
12450  * <pre><code>
12451 var tpl = new Ext.XTemplate(
12452     '&lt;p>Name: {name}&lt;/p>',
12453     '&lt;p>Kids: ',
12454     '&lt;tpl for="kids">',
12455         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12456             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
12457             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
12458             '&lt;p>Dad: {parent.name}&lt;/p>',
12459         '&lt;/tpl>',
12460     '&lt;/tpl>&lt;/p>'
12461 );
12462 tpl.overwrite(panel.body, data);
12463 </code></pre>
12464  * </div>
12465  * </li>
12466  *
12467  * 
12468  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
12469  * <div class="sub-desc">
12470  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
12471  * in the scope of the template. There are some special variables available in that code:
12472  * <ul>
12473  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
12474  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
12475  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12476  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
12477  * loop you are in (1-based).</li>
12478  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
12479  * of the array you are looping.</li>
12480  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12481  * </ul>
12482  * This example demonstrates basic row striping using an inline code block and the
12483  * <tt>xindex</tt> variable:</p>
12484  * <pre><code>
12485 var tpl = new Ext.XTemplate(
12486     '&lt;p>Name: {name}&lt;/p>',
12487     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
12488     '&lt;p>Kids: ',
12489     '&lt;tpl for="kids">',
12490        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12491         '{name}',
12492         '&lt;/div>',
12493     '&lt;/tpl>&lt;/p>'
12494 );
12495 tpl.overwrite(panel.body, data);
12496  * </code></pre>
12497  * </div>
12498  * </li>
12499  * 
12500  * <li><b><u>Template member functions</u></b> 
12501  * <div class="sub-desc">
12502  * <p>One or more member functions can be specified in a configuration
12503  * object passed into the XTemplate constructor for more complex processing:</p>
12504  * <pre><code>
12505 var tpl = new Ext.XTemplate(
12506     '&lt;p>Name: {name}&lt;/p>',
12507     '&lt;p>Kids: ',
12508     '&lt;tpl for="kids">',
12509         '&lt;tpl if="this.isGirl(name)">',
12510             '&lt;p>Girl: {name} - {age}&lt;/p>',
12511         '&lt;/tpl>',
12512         // use opposite if statement to simulate 'else' processing:
12513         '&lt;tpl if="this.isGirl(name) == false">',
12514             '&lt;p>Boy: {name} - {age}&lt;/p>',
12515         '&lt;/tpl>',
12516         '&lt;tpl if="this.isBaby(age)">',
12517             '&lt;p>{name} is a baby!&lt;/p>',
12518         '&lt;/tpl>',
12519     '&lt;/tpl>&lt;/p>',
12520     {
12521         // XTemplate configuration:
12522         compiled: true,
12523         disableFormats: true,
12524         // member functions:
12525         isGirl: function(name){
12526             return name == 'Sara Grace';
12527         },
12528         isBaby: function(age){
12529             return age < 1;
12530         }
12531     }
12532 );
12533 tpl.overwrite(panel.body, data);
12534  * </code></pre>
12535  * </div>
12536  * </li>
12537  * 
12538  * </ul></div>
12539  * 
12540  * @param {Mixed} config
12541  */
12542 Ext.XTemplate = function(){
12543     Ext.XTemplate.superclass.constructor.apply(this, arguments);
12544
12545     var me = this,
12546         s = me.html,
12547         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
12548         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
12549         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
12550         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
12551         m,
12552         id = 0,
12553         tpls = [],
12554         VALUES = 'values',
12555         PARENT = 'parent',
12556         XINDEX = 'xindex',
12557         XCOUNT = 'xcount',
12558         RETURN = 'return ',
12559         WITHVALUES = 'with(values){ ';
12560
12561     s = ['<tpl>', s, '</tpl>'].join('');
12562
12563     while((m = s.match(re))){
12564         var m2 = m[0].match(nameRe),
12565                         m3 = m[0].match(ifRe),
12566                 m4 = m[0].match(execRe),
12567                 exp = null,
12568                 fn = null,
12569                 exec = null,
12570                 name = m2 && m2[1] ? m2[1] : '';
12571
12572        if (m3) {
12573            exp = m3 && m3[1] ? m3[1] : null;
12574            if(exp){
12575                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
12576            }
12577        }
12578        if (m4) {
12579            exp = m4 && m4[1] ? m4[1] : null;
12580            if(exp){
12581                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
12582            }
12583        }
12584        if(name){
12585            switch(name){
12586                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
12587                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
12588                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
12589            }
12590        }
12591        tpls.push({
12592             id: id,
12593             target: name,
12594             exec: exec,
12595             test: fn,
12596             body: m[1]||''
12597         });
12598        s = s.replace(m[0], '{xtpl'+ id + '}');
12599        ++id;
12600     }
12601         Ext.each(tpls, function(t) {
12602         me.compileTpl(t);
12603     });
12604     me.master = tpls[tpls.length-1];
12605     me.tpls = tpls;
12606 };
12607 Ext.extend(Ext.XTemplate, Ext.Template, {
12608     // private
12609     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
12610     // private
12611     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
12612
12613     // private
12614     applySubTemplate : function(id, values, parent, xindex, xcount){
12615         var me = this,
12616                 len,
12617                 t = me.tpls[id],
12618                 vs,
12619                 buf = [];
12620         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
12621             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
12622             return '';
12623         }
12624         vs = t.target ? t.target.call(me, values, parent) : values;
12625         len = vs.length;
12626         parent = t.target ? values : parent;
12627         if(t.target && Ext.isArray(vs)){
12628                 Ext.each(vs, function(v, i) {
12629                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
12630             });
12631             return buf.join('');
12632         }
12633         return t.compiled.call(me, vs, parent, xindex, xcount);
12634     },
12635
12636     // private
12637     compileTpl : function(tpl){
12638         var fm = Ext.util.Format,
12639                 useF = this.disableFormats !== true,
12640             sep = Ext.isGecko ? "+" : ",",
12641             body;
12642
12643         function fn(m, name, format, args, math){
12644             if(name.substr(0, 4) == 'xtpl'){
12645                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
12646             }
12647             var v;
12648             if(name === '.'){
12649                 v = 'values';
12650             }else if(name === '#'){
12651                 v = 'xindex';
12652             }else if(name.indexOf('.') != -1){
12653                 v = name;
12654             }else{
12655                 v = "values['" + name + "']";
12656             }
12657             if(math){
12658                 v = '(' + v + math + ')';
12659             }
12660             if (format && useF) {
12661                 args = args ? ',' + args : "";
12662                 if(format.substr(0, 5) != "this."){
12663                     format = "fm." + format + '(';
12664                 }else{
12665                     format = 'this.call("'+ format.substr(5) + '", ';
12666                     args = ", values";
12667                 }
12668             } else {
12669                 args= ''; format = "("+v+" === undefined ? '' : ";
12670             }
12671             return "'"+ sep + format + v + args + ")"+sep+"'";
12672         }
12673
12674         function codeFn(m, code){
12675             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
12676             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
12677         }
12678
12679         // branched to use + in gecko and [].join() in others
12680         if(Ext.isGecko){
12681             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
12682                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
12683                     "';};";
12684         }else{
12685             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
12686             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
12687             body.push("'].join('');};");
12688             body = body.join('');
12689         }
12690         eval(body);
12691         return this;
12692     },
12693
12694     /**
12695      * Returns an HTML fragment of this template with the specified values applied.
12696      * @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'})
12697      * @return {String} The HTML fragment
12698      */
12699     applyTemplate : function(values){
12700         return this.master.compiled.call(this, values, {}, 1, 1);
12701     },
12702
12703     /**
12704      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
12705      * @return {Function} The compiled function
12706      */
12707     compile : function(){return this;}
12708
12709     /**
12710      * @property re
12711      * @hide
12712      */
12713     /**
12714      * @property disableFormats
12715      * @hide
12716      */
12717     /**
12718      * @method set
12719      * @hide
12720      */
12721
12722 });
12723 /**
12724  * Alias for {@link #applyTemplate}
12725  * Returns an HTML fragment of this template with the specified values applied.
12726  * @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'})
12727  * @return {String} The HTML fragment
12728  * @member Ext.XTemplate
12729  * @method apply
12730  */
12731 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
12732
12733 /**
12734  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
12735  * @param {String/HTMLElement} el A DOM element or its id
12736  * @return {Ext.Template} The created template
12737  * @static
12738  */
12739 Ext.XTemplate.from = function(el){
12740     el = Ext.getDom(el);
12741     return new Ext.XTemplate(el.value || el.innerHTML);
12742 };/**\r
12743  * @class Ext.util.CSS\r
12744  * Utility class for manipulating CSS rules\r
12745  * @singleton\r
12746  */\r
12747 Ext.util.CSS = function(){\r
12748         var rules = null;\r
12749         var doc = document;\r
12750 \r
12751     var camelRe = /(-[a-z])/gi;\r
12752     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
12753 \r
12754    return {\r
12755    /**\r
12756     * Creates a stylesheet from a text blob of rules.\r
12757     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.\r
12758     * @param {String} cssText The text containing the css rules\r
12759     * @param {String} id An id to add to the stylesheet for later removal\r
12760     * @return {StyleSheet}\r
12761     */\r
12762    createStyleSheet : function(cssText, id){\r
12763        var ss;\r
12764        var head = doc.getElementsByTagName("head")[0];\r
12765        var rules = doc.createElement("style");\r
12766        rules.setAttribute("type", "text/css");\r
12767        if(id){\r
12768            rules.setAttribute("id", id);\r
12769        }\r
12770        if(Ext.isIE){\r
12771            head.appendChild(rules);\r
12772            ss = rules.styleSheet;\r
12773            ss.cssText = cssText;\r
12774        }else{\r
12775            try{\r
12776                 rules.appendChild(doc.createTextNode(cssText));\r
12777            }catch(e){\r
12778                rules.cssText = cssText;\r
12779            }\r
12780            head.appendChild(rules);\r
12781            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);\r
12782        }\r
12783        this.cacheStyleSheet(ss);\r
12784        return ss;\r
12785    },\r
12786 \r
12787    /**\r
12788     * Removes a style or link tag by id\r
12789     * @param {String} id The id of the tag\r
12790     */\r
12791    removeStyleSheet : function(id){\r
12792        var existing = doc.getElementById(id);\r
12793        if(existing){\r
12794            existing.parentNode.removeChild(existing);\r
12795        }\r
12796    },\r
12797 \r
12798    /**\r
12799     * Dynamically swaps an existing stylesheet reference for a new one\r
12800     * @param {String} id The id of an existing link tag to remove\r
12801     * @param {String} url The href of the new stylesheet to include\r
12802     */\r
12803    swapStyleSheet : function(id, url){\r
12804        this.removeStyleSheet(id);\r
12805        var ss = doc.createElement("link");\r
12806        ss.setAttribute("rel", "stylesheet");\r
12807        ss.setAttribute("type", "text/css");\r
12808        ss.setAttribute("id", id);\r
12809        ss.setAttribute("href", url);\r
12810        doc.getElementsByTagName("head")[0].appendChild(ss);\r
12811    },\r
12812    \r
12813    /**\r
12814     * Refresh the rule cache if you have dynamically added stylesheets\r
12815     * @return {Object} An object (hash) of rules indexed by selector\r
12816     */\r
12817    refreshCache : function(){\r
12818        return this.getRules(true);\r
12819    },\r
12820 \r
12821    // private\r
12822    cacheStyleSheet : function(ss){\r
12823        if(!rules){\r
12824            rules = {};\r
12825        }\r
12826        try{// try catch for cross domain access issue\r
12827            var ssRules = ss.cssRules || ss.rules;\r
12828            for(var j = ssRules.length-1; j >= 0; --j){\r
12829                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];\r
12830            }\r
12831        }catch(e){}\r
12832    },\r
12833    \r
12834    /**\r
12835     * Gets all css rules for the document\r
12836     * @param {Boolean} refreshCache true to refresh the internal cache\r
12837     * @return {Object} An object (hash) of rules indexed by selector\r
12838     */\r
12839    getRules : function(refreshCache){\r
12840                 if(rules === null || refreshCache){\r
12841                         rules = {};\r
12842                         var ds = doc.styleSheets;\r
12843                         for(var i =0, len = ds.length; i < len; i++){\r
12844                             try{\r
12845                         this.cacheStyleSheet(ds[i]);\r
12846                     }catch(e){} \r
12847                 }\r
12848                 }\r
12849                 return rules;\r
12850         },\r
12851         \r
12852         /**\r
12853     * Gets an an individual CSS rule by selector(s)\r
12854     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.\r
12855     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically\r
12856     * @return {CSSRule} The CSS rule or null if one is not found\r
12857     */\r
12858    getRule : function(selector, refreshCache){\r
12859                 var rs = this.getRules(refreshCache);\r
12860                 if(!Ext.isArray(selector)){\r
12861                     return rs[selector.toLowerCase()];\r
12862                 }\r
12863                 for(var i = 0; i < selector.length; i++){\r
12864                         if(rs[selector[i]]){\r
12865                                 return rs[selector[i].toLowerCase()];\r
12866                         }\r
12867                 }\r
12868                 return null;\r
12869         },\r
12870         \r
12871         \r
12872         /**\r
12873     * Updates a rule property\r
12874     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.\r
12875     * @param {String} property The css property\r
12876     * @param {String} value The new value for the property\r
12877     * @return {Boolean} true If a rule was found and updated\r
12878     */\r
12879    updateRule : function(selector, property, value){\r
12880                 if(!Ext.isArray(selector)){\r
12881                         var rule = this.getRule(selector);\r
12882                         if(rule){\r
12883                                 rule.style[property.replace(camelRe, camelFn)] = value;\r
12884                                 return true;\r
12885                         }\r
12886                 }else{\r
12887                         for(var i = 0; i < selector.length; i++){\r
12888                                 if(this.updateRule(selector[i], property, value)){\r
12889                                         return true;\r
12890                                 }\r
12891                         }\r
12892                 }\r
12893                 return false;\r
12894         }\r
12895    };   \r
12896 }();/**
12897  @class Ext.util.ClickRepeater
12898  @extends Ext.util.Observable
12899
12900  A wrapper class which can be applied to any element. Fires a "click" event while the
12901  mouse is pressed. The interval between firings may be specified in the config but
12902  defaults to 20 milliseconds.
12903
12904  Optionally, a CSS class may be applied to the element during the time it is pressed.
12905
12906  @cfg {Mixed} el The element to act as a button.
12907  @cfg {Number} delay The initial delay before the repeating event begins firing.
12908  Similar to an autorepeat key delay.
12909  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
12910  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
12911  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
12912            "interval" and "delay" are ignored.
12913  @cfg {Boolean} preventDefault True to prevent the default click event
12914  @cfg {Boolean} stopDefault True to stop the default click event
12915
12916  @history
12917     2007-02-02 jvs Original code contributed by Nige "Animal" White
12918     2007-02-02 jvs Renamed to ClickRepeater
12919     2007-02-03 jvs Modifications for FF Mac and Safari
12920
12921  @constructor
12922  @param {Mixed} el The element to listen on
12923  @param {Object} config
12924  */
12925 Ext.util.ClickRepeater = function(el, config)
12926 {
12927     this.el = Ext.get(el);
12928     this.el.unselectable();
12929
12930     Ext.apply(this, config);
12931
12932     this.addEvents(
12933     /**
12934      * @event mousedown
12935      * Fires when the mouse button is depressed.
12936      * @param {Ext.util.ClickRepeater} this
12937      */
12938         "mousedown",
12939     /**
12940      * @event click
12941      * Fires on a specified interval during the time the element is pressed.
12942      * @param {Ext.util.ClickRepeater} this
12943      */
12944         "click",
12945     /**
12946      * @event mouseup
12947      * Fires when the mouse key is released.
12948      * @param {Ext.util.ClickRepeater} this
12949      */
12950         "mouseup"
12951     );
12952
12953     if(!this.disabled){
12954         this.disabled = true;
12955         this.enable();
12956     }
12957
12958     // allow inline handler
12959     if(this.handler){
12960         this.on("click", this.handler,  this.scope || this);
12961     }
12962
12963     Ext.util.ClickRepeater.superclass.constructor.call(this);
12964 };
12965
12966 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
12967     interval : 20,
12968     delay: 250,
12969     preventDefault : true,
12970     stopDefault : false,
12971     timer : 0,
12972
12973     /**
12974      * Enables the repeater and allows events to fire.
12975      */
12976     enable: function(){
12977         if(this.disabled){
12978             this.el.on('mousedown', this.handleMouseDown, this);
12979             if(this.preventDefault || this.stopDefault){
12980                 this.el.on('click', this.eventOptions, this);
12981             }
12982         }
12983         this.disabled = false;
12984     },
12985     
12986     /**
12987      * Disables the repeater and stops events from firing.
12988      */
12989     disable: function(/* private */ force){
12990         if(force || !this.disabled){
12991             clearTimeout(this.timer);
12992             if(this.pressClass){
12993                 this.el.removeClass(this.pressClass);
12994             }
12995             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
12996             this.el.removeAllListeners();
12997         }
12998         this.disabled = true;
12999     },
13000     
13001     /**
13002      * Convenience function for setting disabled/enabled by boolean.
13003      * @param {Boolean} disabled
13004      */
13005     setDisabled: function(disabled){
13006         this[disabled ? 'disable' : 'enable']();    
13007     },
13008     
13009     eventOptions: function(e){
13010         if(this.preventDefault){
13011             e.preventDefault();
13012         }
13013         if(this.stopDefault){
13014             e.stopEvent();
13015         }       
13016     },
13017     
13018     // private
13019     destroy : function() {
13020         this.disable(true);
13021         Ext.destroy(this.el);
13022         this.purgeListeners();
13023     },
13024     
13025     // private
13026     handleMouseDown : function(){
13027         clearTimeout(this.timer);
13028         this.el.blur();
13029         if(this.pressClass){
13030             this.el.addClass(this.pressClass);
13031         }
13032         this.mousedownTime = new Date();
13033
13034         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13035         this.el.on("mouseout", this.handleMouseOut, this);
13036
13037         this.fireEvent("mousedown", this);
13038         this.fireEvent("click", this);
13039
13040 //      Do not honor delay or interval if acceleration wanted.
13041         if (this.accelerate) {
13042             this.delay = 400;
13043             }
13044         this.timer = this.click.defer(this.delay || this.interval, this);
13045     },
13046
13047     // private
13048     click : function(){
13049         this.fireEvent("click", this);
13050         this.timer = this.click.defer(this.accelerate ?
13051             this.easeOutExpo(this.mousedownTime.getElapsed(),
13052                 400,
13053                 -390,
13054                 12000) :
13055             this.interval, this);
13056     },
13057
13058     easeOutExpo : function (t, b, c, d) {
13059         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13060     },
13061
13062     // private
13063     handleMouseOut : function(){
13064         clearTimeout(this.timer);
13065         if(this.pressClass){
13066             this.el.removeClass(this.pressClass);
13067         }
13068         this.el.on("mouseover", this.handleMouseReturn, this);
13069     },
13070
13071     // private
13072     handleMouseReturn : function(){
13073         this.el.un("mouseover", this.handleMouseReturn, this);
13074         if(this.pressClass){
13075             this.el.addClass(this.pressClass);
13076         }
13077         this.click();
13078     },
13079
13080     // private
13081     handleMouseUp : function(){
13082         clearTimeout(this.timer);
13083         this.el.un("mouseover", this.handleMouseReturn, this);
13084         this.el.un("mouseout", this.handleMouseOut, this);
13085         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13086         this.el.removeClass(this.pressClass);
13087         this.fireEvent("mouseup", this);
13088     }
13089 });/**
13090  * @class Ext.KeyNav
13091  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13092  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13093  * way to implement custom navigation schemes for any UI component.</p>
13094  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13095  * pageUp, pageDown, del, home, end.  Usage:</p>
13096  <pre><code>
13097 var nav = new Ext.KeyNav("my-element", {
13098     "left" : function(e){
13099         this.moveLeft(e.ctrlKey);
13100     },
13101     "right" : function(e){
13102         this.moveRight(e.ctrlKey);
13103     },
13104     "enter" : function(e){
13105         this.save();
13106     },
13107     scope : this
13108 });
13109 </code></pre>
13110  * @constructor
13111  * @param {Mixed} el The element to bind to
13112  * @param {Object} config The config
13113  */
13114 Ext.KeyNav = function(el, config){
13115     this.el = Ext.get(el);
13116     Ext.apply(this, config);
13117     if(!this.disabled){
13118         this.disabled = true;
13119         this.enable();
13120     }
13121 };
13122
13123 Ext.KeyNav.prototype = {
13124     /**
13125      * @cfg {Boolean} disabled
13126      * True to disable this KeyNav instance (defaults to false)
13127      */
13128     disabled : false,
13129     /**
13130      * @cfg {String} defaultEventAction
13131      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13132      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13133      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13134      */
13135     defaultEventAction: "stopEvent",
13136     /**
13137      * @cfg {Boolean} forceKeyDown
13138      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13139      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13140      * handle keydown instead of keypress.
13141      */
13142     forceKeyDown : false,
13143
13144     // private
13145     relay : function(e){
13146         var k = e.getKey();
13147         var h = this.keyToHandler[k];
13148         if(h && this[h]){
13149             if(this.doRelay(e, this[h], h) !== true){
13150                 e[this.defaultEventAction]();
13151             }
13152         }
13153     },
13154
13155     // private
13156     doRelay : function(e, h, hname){
13157         return h.call(this.scope || this, e);
13158     },
13159
13160     // possible handlers
13161     enter : false,
13162     left : false,
13163     right : false,
13164     up : false,
13165     down : false,
13166     tab : false,
13167     esc : false,
13168     pageUp : false,
13169     pageDown : false,
13170     del : false,
13171     home : false,
13172     end : false,
13173
13174     // quick lookup hash
13175     keyToHandler : {
13176         37 : "left",
13177         39 : "right",
13178         38 : "up",
13179         40 : "down",
13180         33 : "pageUp",
13181         34 : "pageDown",
13182         46 : "del",
13183         36 : "home",
13184         35 : "end",
13185         13 : "enter",
13186         27 : "esc",
13187         9  : "tab"
13188     },
13189     
13190     stopKeyUp: function(e) {
13191         var k = e.getKey();
13192
13193         if (k >= 37 && k <= 40) {
13194             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13195             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13196             e.stopEvent();
13197         }
13198     },
13199
13200         /**
13201          * Enable this KeyNav
13202          */
13203         enable: function() {
13204         if (this.disabled) {
13205             if (Ext.isSafari2) {
13206                 // call stopKeyUp() on "keyup" event
13207                 this.el.on('keyup', this.stopKeyUp, this);
13208             }
13209
13210             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13211             this.disabled = false;
13212         }
13213     },
13214
13215         /**
13216          * Disable this KeyNav
13217          */
13218         disable: function() {
13219         if (!this.disabled) {
13220             if (Ext.isSafari2) {
13221                 // remove "keyup" event handler
13222                 this.el.un('keyup', this.stopKeyUp, this);
13223             }
13224
13225             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13226             this.disabled = true;
13227         }
13228     },
13229     
13230     /**
13231      * Convenience function for setting disabled/enabled by boolean.
13232      * @param {Boolean} disabled
13233      */
13234     setDisabled : function(disabled){
13235         this[disabled ? "disable" : "enable"]();
13236     },
13237     
13238     // private
13239     isKeydown: function(){
13240         return this.forceKeyDown || Ext.EventManager.useKeydown;
13241     }
13242 };
13243 /**\r
13244  * @class Ext.KeyMap\r
13245  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.\r
13246  * The constructor accepts the same config object as defined by {@link #addBinding}.\r
13247  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key\r
13248  * combination it will call the function with this signature (if the match is a multi-key\r
13249  * combination the callback will still be called only once): (String key, Ext.EventObject e)\r
13250  * A KeyMap can also handle a string representation of keys.<br />\r
13251  * Usage:\r
13252  <pre><code>\r
13253 // map one key by key code\r
13254 var map = new Ext.KeyMap("my-element", {\r
13255     key: 13, // or Ext.EventObject.ENTER\r
13256     fn: myHandler,\r
13257     scope: myObject\r
13258 });\r
13259 \r
13260 // map multiple keys to one action by string\r
13261 var map = new Ext.KeyMap("my-element", {\r
13262     key: "a\r\n\t",\r
13263     fn: myHandler,\r
13264     scope: myObject\r
13265 });\r
13266 \r
13267 // map multiple keys to multiple actions by strings and array of codes\r
13268 var map = new Ext.KeyMap("my-element", [\r
13269     {\r
13270         key: [10,13],\r
13271         fn: function(){ alert("Return was pressed"); }\r
13272     }, {\r
13273         key: "abc",\r
13274         fn: function(){ alert('a, b or c was pressed'); }\r
13275     }, {\r
13276         key: "\t",\r
13277         ctrl:true,\r
13278         shift:true,\r
13279         fn: function(){ alert('Control + shift + tab was pressed.'); }\r
13280     }\r
13281 ]);\r
13282 </code></pre>\r
13283  * <b>Note: A KeyMap starts enabled</b>\r
13284  * @constructor\r
13285  * @param {Mixed} el The element to bind to\r
13286  * @param {Object} config The config (see {@link #addBinding})\r
13287  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")\r
13288  */\r
13289 Ext.KeyMap = function(el, config, eventName){\r
13290     this.el  = Ext.get(el);\r
13291     this.eventName = eventName || "keydown";\r
13292     this.bindings = [];\r
13293     if(config){\r
13294         this.addBinding(config);\r
13295     }\r
13296     this.enable();\r
13297 };\r
13298 \r
13299 Ext.KeyMap.prototype = {\r
13300     /**\r
13301      * True to stop the event from bubbling and prevent the default browser action if the\r
13302      * key was handled by the KeyMap (defaults to false)\r
13303      * @type Boolean\r
13304      */\r
13305     stopEvent : false,\r
13306 \r
13307     /**\r
13308      * Add a new binding to this KeyMap. The following config object properties are supported:\r
13309      * <pre>\r
13310 Property    Type             Description\r
13311 ----------  ---------------  ----------------------------------------------------------------------\r
13312 key         String/Array     A single keycode or an array of keycodes to handle\r
13313 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
13314 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
13315 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
13316 handler     Function         The function to call when KeyMap finds the expected key combination\r
13317 fn          Function         Alias of handler (for backwards-compatibility)\r
13318 scope       Object           The scope of the callback function\r
13319 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
13320 </pre>\r
13321      *\r
13322      * Usage:\r
13323      * <pre><code>\r
13324 // Create a KeyMap\r
13325 var map = new Ext.KeyMap(document, {\r
13326     key: Ext.EventObject.ENTER,\r
13327     fn: handleKey,\r
13328     scope: this\r
13329 });\r
13330 \r
13331 //Add a new binding to the existing KeyMap later\r
13332 map.addBinding({\r
13333     key: 'abc',\r
13334     shift: true,\r
13335     fn: handleKey,\r
13336     scope: this\r
13337 });\r
13338 </code></pre>\r
13339      * @param {Object/Array} config A single KeyMap config or an array of configs\r
13340      */\r
13341         addBinding : function(config){\r
13342         if(Ext.isArray(config)){\r
13343             Ext.each(config, function(c){\r
13344                 this.addBinding(c);\r
13345             }, this);\r
13346             return;\r
13347         }\r
13348         var keyCode = config.key,\r
13349             fn = config.fn || config.handler,\r
13350             scope = config.scope;\r
13351 \r
13352         if (config.stopEvent) {\r
13353             this.stopEvent = config.stopEvent;    \r
13354         }       \r
13355 \r
13356         if(typeof keyCode == "string"){\r
13357             var ks = [];\r
13358             var keyString = keyCode.toUpperCase();\r
13359             for(var j = 0, len = keyString.length; j < len; j++){\r
13360                 ks.push(keyString.charCodeAt(j));\r
13361             }\r
13362             keyCode = ks;\r
13363         }\r
13364         var keyArray = Ext.isArray(keyCode);\r
13365         \r
13366         var handler = function(e){\r
13367             if(this.checkModifiers(config, e)){\r
13368                 var k = e.getKey();\r
13369                 if(keyArray){\r
13370                     for(var i = 0, len = keyCode.length; i < len; i++){\r
13371                         if(keyCode[i] == k){\r
13372                           if(this.stopEvent){\r
13373                               e.stopEvent();\r
13374                           }\r
13375                           fn.call(scope || window, k, e);\r
13376                           return;\r
13377                         }\r
13378                     }\r
13379                 }else{\r
13380                     if(k == keyCode){\r
13381                         if(this.stopEvent){\r
13382                            e.stopEvent();\r
13383                         }\r
13384                         fn.call(scope || window, k, e);\r
13385                     }\r
13386                 }\r
13387             }\r
13388         };\r
13389         this.bindings.push(handler);\r
13390         },\r
13391     \r
13392     // private\r
13393     checkModifiers: function(config, e){\r
13394         var val, key, keys = ['shift', 'ctrl', 'alt'];\r
13395         for (var i = 0, len = keys.length; i < len; ++i){\r
13396             key = keys[i];\r
13397             val = config[key];\r
13398             if(!(val === undefined || (val === e[key + 'Key']))){\r
13399                 return false;\r
13400             }\r
13401         }\r
13402         return true;\r
13403     },\r
13404 \r
13405     /**\r
13406      * Shorthand for adding a single key listener\r
13407      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the\r
13408      * following options:\r
13409      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
13410      * @param {Function} fn The function to call\r
13411      * @param {Object} scope (optional) The scope of the function\r
13412      */\r
13413     on : function(key, fn, scope){\r
13414         var keyCode, shift, ctrl, alt;\r
13415         if(typeof key == "object" && !Ext.isArray(key)){\r
13416             keyCode = key.key;\r
13417             shift = key.shift;\r
13418             ctrl = key.ctrl;\r
13419             alt = key.alt;\r
13420         }else{\r
13421             keyCode = key;\r
13422         }\r
13423         this.addBinding({\r
13424             key: keyCode,\r
13425             shift: shift,\r
13426             ctrl: ctrl,\r
13427             alt: alt,\r
13428             fn: fn,\r
13429             scope: scope\r
13430         });\r
13431     },\r
13432 \r
13433     // private\r
13434     handleKeyDown : function(e){\r
13435             if(this.enabled){ //just in case\r
13436             var b = this.bindings;\r
13437             for(var i = 0, len = b.length; i < len; i++){\r
13438                 b[i].call(this, e);\r
13439             }\r
13440             }\r
13441         },\r
13442 \r
13443         /**\r
13444          * Returns true if this KeyMap is enabled\r
13445          * @return {Boolean}\r
13446          */\r
13447         isEnabled : function(){\r
13448             return this.enabled;\r
13449         },\r
13450 \r
13451         /**\r
13452          * Enables this KeyMap\r
13453          */\r
13454         enable: function(){\r
13455                 if(!this.enabled){\r
13456                     this.el.on(this.eventName, this.handleKeyDown, this);\r
13457                     this.enabled = true;\r
13458                 }\r
13459         },\r
13460 \r
13461         /**\r
13462          * Disable this KeyMap\r
13463          */\r
13464         disable: function(){\r
13465                 if(this.enabled){\r
13466                     this.el.removeListener(this.eventName, this.handleKeyDown, this);\r
13467                     this.enabled = false;\r
13468                 }\r
13469         },\r
13470     \r
13471     /**\r
13472      * Convenience function for setting disabled/enabled by boolean.\r
13473      * @param {Boolean} disabled\r
13474      */\r
13475     setDisabled : function(disabled){\r
13476         this[disabled ? "disable" : "enable"]();\r
13477     }\r
13478 };/**
13479  * @class Ext.util.TextMetrics
13480  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13481  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13482  * should not contain any HTML, otherwise it may not be measured correctly.
13483  * @singleton
13484  */
13485 Ext.util.TextMetrics = function(){
13486     var shared;
13487     return {
13488         /**
13489          * Measures the size of the specified text
13490          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13491          * that can affect the size of the rendered text
13492          * @param {String} text The text to measure
13493          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13494          * in order to accurately measure the text height
13495          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13496          */
13497         measure : function(el, text, fixedWidth){
13498             if(!shared){
13499                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13500             }
13501             shared.bind(el);
13502             shared.setFixedWidth(fixedWidth || 'auto');
13503             return shared.getSize(text);
13504         },
13505
13506         /**
13507          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13508          * the overhead of multiple calls to initialize the style properties on each measurement.
13509          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13510          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13511          * in order to accurately measure the text height
13512          * @return {Ext.util.TextMetrics.Instance} instance The new instance
13513          */
13514         createInstance : function(el, fixedWidth){
13515             return Ext.util.TextMetrics.Instance(el, fixedWidth);
13516         }
13517     };
13518 }();
13519
13520 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13521     var ml = new Ext.Element(document.createElement('div'));
13522     document.body.appendChild(ml.dom);
13523     ml.position('absolute');
13524     ml.setLeftTop(-1000, -1000);
13525     ml.hide();
13526
13527     if(fixedWidth){
13528         ml.setWidth(fixedWidth);
13529     }
13530
13531     var instance = {
13532         /**
13533          * Returns the size of the specified text based on the internal element's style and width properties
13534          * @param {String} text The text to measure
13535          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13536          */
13537         getSize : function(text){
13538             ml.update(text);
13539             var s = ml.getSize();
13540             ml.update('');
13541             return s;
13542         },
13543
13544         /**
13545          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13546          * that can affect the size of the rendered text
13547          * @param {String/HTMLElement} el The element, dom node or id
13548          */
13549         bind : function(el){
13550             ml.setStyle(
13551                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
13552             );
13553         },
13554
13555         /**
13556          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13557          * to set a fixed width in order to accurately measure the text height.
13558          * @param {Number} width The width to set on the element
13559          */
13560         setFixedWidth : function(width){
13561             ml.setWidth(width);
13562         },
13563
13564         /**
13565          * Returns the measured width of the specified text
13566          * @param {String} text The text to measure
13567          * @return {Number} width The width in pixels
13568          */
13569         getWidth : function(text){
13570             ml.dom.style.width = 'auto';
13571             return this.getSize(text).width;
13572         },
13573
13574         /**
13575          * Returns the measured height of the specified text.  For multiline text, be sure to call
13576          * {@link #setFixedWidth} if necessary.
13577          * @param {String} text The text to measure
13578          * @return {Number} height The height in pixels
13579          */
13580         getHeight : function(text){
13581             return this.getSize(text).height;
13582         }
13583     };
13584
13585     instance.bind(bindTo);
13586
13587     return instance;
13588 };
13589
13590 Ext.Element.addMethods({
13591     /**
13592      * Returns the width in pixels of the passed text, or the width of the text in this Element.
13593      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
13594      * @param {Number} min (Optional) The minumum value to return.
13595      * @param {Number} max (Optional) The maximum value to return.
13596      * @return {Number} The text width in pixels.
13597      * @member Ext.Element getTextWidth
13598      */
13599     getTextWidth : function(text, min, max){
13600         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
13601     }
13602 });
13603 /**\r
13604  * @class Ext.util.Cookies\r
13605  * Utility class for managing and interacting with cookies.\r
13606  * @singleton\r
13607  */\r
13608 Ext.util.Cookies = {\r
13609     /**\r
13610      * Create a cookie with the specified name and value. Additional settings\r
13611      * for the cookie may be optionally specified (for example: expiration,\r
13612      * access restriction, SSL).\r
13613      * @param {String} name The name of the cookie to set. \r
13614      * @param {Mixed} value The value to set for the cookie.\r
13615      * @param {Object} expires (Optional) Specify an expiration date the\r
13616      * cookie is to persist until.  Note that the specified Date object will\r
13617      * be converted to Greenwich Mean Time (GMT). \r
13618      * @param {String} path (Optional) Setting a path on the cookie restricts\r
13619      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). \r
13620      * @param {String} domain (Optional) Setting a domain restricts access to\r
13621      * pages on a given domain (typically used to allow cookie access across\r
13622      * subdomains). For example, "extjs.com" will create a cookie that can be\r
13623      * accessed from any subdomain of extjs.com, including www.extjs.com,\r
13624      * support.extjs.com, etc.\r
13625      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie\r
13626      * should only be accessible via SSL on a page using the HTTPS protocol.\r
13627      * Defaults to <tt>false</tt>. Note that this will only work if the page\r
13628      * calling this code uses the HTTPS protocol, otherwise the cookie will be\r
13629      * created with default options.\r
13630      */\r
13631     set : function(name, value){\r
13632         var argv = arguments;\r
13633         var argc = arguments.length;\r
13634         var expires = (argc > 2) ? argv[2] : null;\r
13635         var path = (argc > 3) ? argv[3] : '/';\r
13636         var domain = (argc > 4) ? argv[4] : null;\r
13637         var secure = (argc > 5) ? argv[5] : false;\r
13638         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");\r
13639     },\r
13640 \r
13641     /**\r
13642      * Retrieves cookies that are accessible by the current page. If a cookie\r
13643      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following\r
13644      * example retrieves the cookie called "valid" and stores the String value\r
13645      * in the variable <tt>validStatus</tt>.\r
13646      * <pre><code>\r
13647      * var validStatus = Ext.util.Cookies.get("valid");\r
13648      * </code></pre>\r
13649      * @param {String} name The name of the cookie to get\r
13650      * @return {Mixed} Returns the cookie value for the specified name;\r
13651      * null if the cookie name does not exist.\r
13652      */\r
13653     get : function(name){\r
13654         var arg = name + "=";\r
13655         var alen = arg.length;\r
13656         var clen = document.cookie.length;\r
13657         var i = 0;\r
13658         var j = 0;\r
13659         while(i < clen){\r
13660             j = i + alen;\r
13661             if(document.cookie.substring(i, j) == arg){\r
13662                 return Ext.util.Cookies.getCookieVal(j);\r
13663             }\r
13664             i = document.cookie.indexOf(" ", i) + 1;\r
13665             if(i === 0){\r
13666                 break;\r
13667             }\r
13668         }\r
13669         return null;\r
13670     },\r
13671 \r
13672     /**\r
13673      * Removes a cookie with the provided name from the browser\r
13674      * if found by setting its expiration date to sometime in the past. \r
13675      * @param {String} name The name of the cookie to remove\r
13676      */\r
13677     clear : function(name){\r
13678         if(Ext.util.Cookies.get(name)){\r
13679             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";\r
13680         }\r
13681     },\r
13682     /**\r
13683      * @private\r
13684      */\r
13685     getCookieVal : function(offset){\r
13686         var endstr = document.cookie.indexOf(";", offset);\r
13687         if(endstr == -1){\r
13688             endstr = document.cookie.length;\r
13689         }\r
13690         return unescape(document.cookie.substring(offset, endstr));\r
13691     }\r
13692 };/**
13693  * Framework-wide error-handler.  Developers can override this method to provide
13694  * custom exception-handling.  Framework errors will often extend from the base
13695  * Ext.Error class.
13696  * @param {Object/Error} e The thrown exception object.
13697  */
13698 Ext.handleError = function(e) {
13699     throw e;
13700 };
13701
13702 /**
13703  * @class Ext.Error
13704  * @extends Error
13705  * <p>A base error class. Future implementations are intended to provide more
13706  * robust error handling throughout the framework (<b>in the debug build only</b>)
13707  * to check for common errors and problems. The messages issued by this class
13708  * will aid error checking. Error checks will be automatically removed in the
13709  * production build so that performance is not negatively impacted.</p>
13710  * <p>Some sample messages currently implemented:</p><pre>
13711 "DataProxy attempted to execute an API-action but found an undefined
13712 url / function. Please review your Proxy url/api-configuration."
13713  * </pre><pre>
13714 "Could not locate your "root" property in your server response.
13715 Please review your JsonReader config to ensure the config-property
13716 "root" matches the property your server-response.  See the JsonReader
13717 docs for additional assistance."
13718  * </pre>
13719  * <p>An example of the code used for generating error messages:</p><pre><code>
13720 try {
13721     generateError({
13722         foo: 'bar'
13723     });
13724 }
13725 catch (e) {
13726     console.error(e);
13727 }
13728 function generateError(data) {
13729     throw new Ext.Error('foo-error', data);
13730 }
13731  * </code></pre>
13732  * @param {String} message
13733  */
13734 Ext.Error = function(message) {
13735     // Try to read the message from Ext.Error.lang
13736     this.message = (this.lang[message]) ? this.lang[message] : message;
13737 }
13738 Ext.Error.prototype = new Error();
13739 Ext.apply(Ext.Error.prototype, {
13740     // protected.  Extensions place their error-strings here.
13741     lang: {},
13742
13743     name: 'Ext.Error',
13744     /**
13745      * getName
13746      * @return {String}
13747      */
13748     getName : function() {
13749         return this.name;
13750     },
13751     /**
13752      * getMessage
13753      * @return {String}
13754      */
13755     getMessage : function() {
13756         return this.message;
13757     },
13758     /**
13759      * toJson
13760      * @return {String}
13761      */
13762     toJson : function() {
13763         return Ext.encode(this);
13764     }
13765 });
13766
13767 /**
13768  * @class Ext.ComponentMgr
13769  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
13770  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
13771  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
13772  * <p>This object also provides a registry of available Component <i>classes</i>
13773  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
13774  * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
13775  * when creating a full, nested config object for a complete Ext page.</p>
13776  * <p>A child Component may be specified simply as a <i>config object</i>
13777  * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
13778  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
13779  * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
13780  * @singleton
13781  */
13782 Ext.ComponentMgr = function(){
13783     var all = new Ext.util.MixedCollection();
13784     var types = {};
13785     var ptypes = {};
13786
13787     return {
13788         /**
13789          * Registers a component.
13790          * @param {Ext.Component} c The component
13791          */
13792         register : function(c){
13793             all.add(c);
13794         },
13795
13796         /**
13797          * Unregisters a component.
13798          * @param {Ext.Component} c The component
13799          */
13800         unregister : function(c){
13801             all.remove(c);
13802         },
13803
13804         /**
13805          * Returns a component by {@link Ext.Component#id id}.
13806          * For additional details see {@link Ext.util.MixedCollection#get}.
13807          * @param {String} id The component {@link Ext.Component#id id}
13808          * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
13809          * Class was found.
13810          */
13811         get : function(id){
13812             return all.get(id);
13813         },
13814
13815         /**
13816          * Registers a function that will be called when a specified component is added to ComponentMgr
13817          * @param {String} id The component {@link Ext.Component#id id}
13818          * @param {Function} fn The callback function
13819          * @param {Object} scope The scope of the callback
13820          */
13821         onAvailable : function(id, fn, scope){
13822             all.on("add", function(index, o){
13823                 if(o.id == id){
13824                     fn.call(scope || o, o);
13825                     all.un("add", fn, scope);
13826                 }
13827             });
13828         },
13829
13830         /**
13831          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
13832          * events on the MixedCollection to monitor addition or removal.  Read-only.
13833          * @type {MixedCollection}
13834          */
13835         all : all,
13836         
13837         /**
13838          * Checks if a Component type is registered.
13839          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
13840          * @return {Boolean} Whether the type is registered.
13841          */
13842         isRegistered : function(xtype){
13843             return types[xtype] !== undefined;    
13844         },
13845
13846         /**
13847          * <p>Registers a new Component constructor, keyed by a new
13848          * {@link Ext.Component#xtype}.</p>
13849          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
13850          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
13851          * child Components.
13852          * see {@link Ext.Container#items}</p>
13853          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
13854          * @param {Constructor} cls The new Component class.
13855          */
13856         registerType : function(xtype, cls){
13857             types[xtype] = cls;
13858             cls.xtype = xtype;
13859         },
13860
13861         /**
13862          * Creates a new Component from the specified config object using the
13863          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
13864          * @param {Object} config A configuration object for the Component you wish to create.
13865          * @param {Constructor} defaultType The constructor to provide the default Component type if
13866          * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
13867          * @return {Ext.Component} The newly instantiated Component.
13868          */
13869         create : function(config, defaultType){
13870             return config.render ? config : new types[config.xtype || defaultType](config);
13871         },
13872
13873         /**
13874          * <p>Registers a new Plugin constructor, keyed by a new
13875          * {@link Ext.Component#ptype}.</p>
13876          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
13877          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
13878          * Plugins.</p>
13879          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
13880          * @param {Constructor} cls The new Plugin class.
13881          */
13882         registerPlugin : function(ptype, cls){
13883             ptypes[ptype] = cls;
13884             cls.ptype = ptype;
13885         },
13886
13887         /**
13888          * Creates a new Plugin from the specified config object using the
13889          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
13890          * @param {Object} config A configuration object for the Plugin you wish to create.
13891          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
13892          * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
13893          * @return {Ext.Component} The newly instantiated Plugin.
13894          */
13895         createPlugin : function(config, defaultType){
13896             return new ptypes[config.ptype || defaultType](config);
13897         }
13898     };
13899 }();
13900
13901 /**
13902  * Shorthand for {@link Ext.ComponentMgr#registerType}
13903  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
13904  * may be looked up.
13905  * @param {Constructor} cls The new Component class.
13906  * @member Ext
13907  * @method reg
13908  */
13909 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
13910 /**
13911  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
13912  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
13913  * may be looked up.
13914  * @param {Constructor} cls The new Plugin class.
13915  * @member Ext
13916  * @method preg
13917  */
13918 Ext.preg = Ext.ComponentMgr.registerPlugin;
13919 /**
13920  * Shorthand for {@link Ext.ComponentMgr#create}
13921  * Creates a new Component from the specified config object using the
13922  * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
13923  * @param {Object} config A configuration object for the Component you wish to create.
13924  * @param {Constructor} defaultType The constructor to provide the default Component type if
13925  * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
13926  * @return {Ext.Component} The newly instantiated Component.
13927  * @member Ext
13928  * @method create
13929  */
13930 Ext.create = Ext.ComponentMgr.create;/**
13931  * @class Ext.Component
13932  * @extends Ext.util.Observable
13933  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
13934  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
13935  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
13936  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
13937  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
13938  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
13939  * {@link Ext#getCmp}, passing the {@link #id}.</p>
13940  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
13941  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
13942  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
13943  * and to either extend or augment ExtJs base classes to create custom Components.</p>
13944  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
13945  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
13946  * <pre>
13947 xtype            Class
13948 -------------    ------------------
13949 box              {@link Ext.BoxComponent}
13950 button           {@link Ext.Button}
13951 buttongroup      {@link Ext.ButtonGroup}
13952 colorpalette     {@link Ext.ColorPalette}
13953 component        {@link Ext.Component}
13954 container        {@link Ext.Container}
13955 cycle            {@link Ext.CycleButton}
13956 dataview         {@link Ext.DataView}
13957 datepicker       {@link Ext.DatePicker}
13958 editor           {@link Ext.Editor}
13959 editorgrid       {@link Ext.grid.EditorGridPanel}
13960 flash            {@link Ext.FlashComponent}
13961 grid             {@link Ext.grid.GridPanel}
13962 listview         {@link Ext.ListView}
13963 panel            {@link Ext.Panel}
13964 progress         {@link Ext.ProgressBar}
13965 propertygrid     {@link Ext.grid.PropertyGrid}
13966 slider           {@link Ext.Slider}
13967 spacer           {@link Ext.Spacer}
13968 splitbutton      {@link Ext.SplitButton}
13969 tabpanel         {@link Ext.TabPanel}
13970 treepanel        {@link Ext.tree.TreePanel}
13971 viewport         {@link Ext.ViewPort}
13972 window           {@link Ext.Window}
13973
13974 Toolbar components
13975 ---------------------------------------
13976 paging           {@link Ext.PagingToolbar}
13977 toolbar          {@link Ext.Toolbar}
13978 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
13979 tbfill           {@link Ext.Toolbar.Fill}
13980 tbitem           {@link Ext.Toolbar.Item}
13981 tbseparator      {@link Ext.Toolbar.Separator}
13982 tbspacer         {@link Ext.Toolbar.Spacer}
13983 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
13984 tbtext           {@link Ext.Toolbar.TextItem}
13985
13986 Menu components
13987 ---------------------------------------
13988 menu             {@link Ext.menu.Menu}
13989 colormenu        {@link Ext.menu.ColorMenu}
13990 datemenu         {@link Ext.menu.DateMenu}
13991 menubaseitem     {@link Ext.menu.BaseItem}
13992 menucheckitem    {@link Ext.menu.CheckItem}
13993 menuitem         {@link Ext.menu.Item}
13994 menuseparator    {@link Ext.menu.Separator}
13995 menutextitem     {@link Ext.menu.TextItem}
13996
13997 Form components
13998 ---------------------------------------
13999 form             {@link Ext.FormPanel}
14000 checkbox         {@link Ext.form.Checkbox}
14001 checkboxgroup    {@link Ext.form.CheckboxGroup}
14002 combo            {@link Ext.form.ComboBox}
14003 datefield        {@link Ext.form.DateField}
14004 displayfield     {@link Ext.form.DisplayField}
14005 field            {@link Ext.form.Field}
14006 fieldset         {@link Ext.form.FieldSet}
14007 hidden           {@link Ext.form.Hidden}
14008 htmleditor       {@link Ext.form.HtmlEditor}
14009 label            {@link Ext.form.Label}
14010 numberfield      {@link Ext.form.NumberField}
14011 radio            {@link Ext.form.Radio}
14012 radiogroup       {@link Ext.form.RadioGroup}
14013 textarea         {@link Ext.form.TextArea}
14014 textfield        {@link Ext.form.TextField}
14015 timefield        {@link Ext.form.TimeField}
14016 trigger          {@link Ext.form.TriggerField}
14017
14018 Chart components
14019 ---------------------------------------
14020 chart            {@link Ext.chart.Chart}
14021 barchart         {@link Ext.chart.BarChart}
14022 cartesianchart   {@link Ext.chart.CartesianChart}
14023 columnchart      {@link Ext.chart.ColumnChart}
14024 linechart        {@link Ext.chart.LineChart}
14025 piechart         {@link Ext.chart.PieChart}
14026
14027 Store xtypes
14028 ---------------------------------------
14029 arraystore       {@link Ext.data.ArrayStore}
14030 directstore      {@link Ext.data.DirectStore}
14031 groupingstore    {@link Ext.data.GroupingStore}
14032 jsonstore        {@link Ext.data.JsonStore}
14033 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
14034 store            {@link Ext.data.Store}
14035 xmlstore         {@link Ext.data.XmlStore}
14036 </pre>
14037  * @constructor
14038  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
14039  * <div class="mdetail-params"><ul>
14040  * <li><b>an element</b> :
14041  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
14042  * <li><b>a string</b> :
14043  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
14044  * <li><b>anything else</b> :
14045  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
14046  * </ul></div>
14047  */
14048 Ext.Component = function(config){
14049     config = config || {};
14050     if(config.initialConfig){
14051         if(config.isAction){           // actions
14052             this.baseAction = config;
14053         }
14054         config = config.initialConfig; // component cloning / action set up
14055     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
14056         config = {applyTo: config, id: config.id || config};
14057     }
14058
14059     /**
14060      * This Component's initial configuration specification. Read-only.
14061      * @type Object
14062      * @property initialConfig
14063      */
14064     this.initialConfig = config;
14065
14066     Ext.apply(this, config);
14067     this.addEvents(
14068         /**
14069          * @event disable
14070          * Fires after the component is disabled.
14071          * @param {Ext.Component} this
14072          */
14073         'disable',
14074         /**
14075          * @event enable
14076          * Fires after the component is enabled.
14077          * @param {Ext.Component} this
14078          */
14079         'enable',
14080         /**
14081          * @event beforeshow
14082          * Fires before the component is shown by calling the {@link #show} method.
14083          * Return false from an event handler to stop the show.
14084          * @param {Ext.Component} this
14085          */
14086         'beforeshow',
14087         /**
14088          * @event show
14089          * Fires after the component is shown when calling the {@link #show} method.
14090          * @param {Ext.Component} this
14091          */
14092         'show',
14093         /**
14094          * @event beforehide
14095          * Fires before the component is hidden by calling the {@link #hide} method.
14096          * Return false from an event handler to stop the hide.
14097          * @param {Ext.Component} this
14098          */
14099         'beforehide',
14100         /**
14101          * @event hide
14102          * Fires after the component is hidden.
14103          * Fires after the component is hidden when calling the {@link #hide} method.
14104          * @param {Ext.Component} this
14105          */
14106         'hide',
14107         /**
14108          * @event beforerender
14109          * Fires before the component is {@link #rendered}. Return false from an
14110          * event handler to stop the {@link #render}.
14111          * @param {Ext.Component} this
14112          */
14113         'beforerender',
14114         /**
14115          * @event render
14116          * Fires after the component markup is {@link #rendered}.
14117          * @param {Ext.Component} this
14118          */
14119         'render',
14120         /**
14121          * @event afterrender
14122          * <p>Fires after the component rendering is finished.</p>
14123          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
14124          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
14125          * has been restored.</p>
14126          * @param {Ext.Component} this
14127          */
14128         'afterrender',
14129         /**
14130          * @event beforedestroy
14131          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
14132          * @param {Ext.Component} this
14133          */
14134         'beforedestroy',
14135         /**
14136          * @event destroy
14137          * Fires after the component is {@link #destroy}ed.
14138          * @param {Ext.Component} this
14139          */
14140         'destroy',
14141         /**
14142          * @event beforestaterestore
14143          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
14144          * @param {Ext.Component} this
14145          * @param {Object} state The hash of state values returned from the StateProvider. If this
14146          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
14147          * that simply copies property values into this Component. The method maybe overriden to
14148          * provide custom state restoration.
14149          */
14150         'beforestaterestore',
14151         /**
14152          * @event staterestore
14153          * Fires after the state of the component is restored.
14154          * @param {Ext.Component} this
14155          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
14156          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
14157          * Component. The method maybe overriden to provide custom state restoration.
14158          */
14159         'staterestore',
14160         /**
14161          * @event beforestatesave
14162          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
14163          * @param {Ext.Component} this
14164          * @param {Object} state The hash of state values. This is determined by calling
14165          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14166          * developer to return whetever representation of state is required, by default, Ext.Component
14167          * has a null implementation.
14168          */
14169         'beforestatesave',
14170         /**
14171          * @event statesave
14172          * Fires after the state of the component is saved to the configured state provider.
14173          * @param {Ext.Component} this
14174          * @param {Object} state The hash of state values. This is determined by calling
14175          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14176          * developer to return whetever representation of state is required, by default, Ext.Component
14177          * has a null implementation.
14178          */
14179         'statesave'
14180     );
14181     this.getId();
14182     Ext.ComponentMgr.register(this);
14183     Ext.Component.superclass.constructor.call(this);
14184
14185     if(this.baseAction){
14186         this.baseAction.addComponent(this);
14187     }
14188
14189     this.initComponent();
14190
14191     if(this.plugins){
14192         if(Ext.isArray(this.plugins)){
14193             for(var i = 0, len = this.plugins.length; i < len; i++){
14194                 this.plugins[i] = this.initPlugin(this.plugins[i]);
14195             }
14196         }else{
14197             this.plugins = this.initPlugin(this.plugins);
14198         }
14199     }
14200
14201     if(this.stateful !== false){
14202         this.initState(config);
14203     }
14204
14205     if(this.applyTo){
14206         this.applyToMarkup(this.applyTo);
14207         delete this.applyTo;
14208     }else if(this.renderTo){
14209         this.render(this.renderTo);
14210         delete this.renderTo;
14211     }
14212 };
14213
14214 // private
14215 Ext.Component.AUTO_ID = 1000;
14216
14217 Ext.extend(Ext.Component, Ext.util.Observable, {
14218     // Configs below are used for all Components when rendered by FormLayout.
14219     /**
14220      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
14221      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
14222      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
14223      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
14224      * <p>Also see <tt>{@link #hideLabel}</tt> and
14225      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
14226      * Example use:<pre><code>
14227 new Ext.FormPanel({
14228     height: 100,
14229     renderTo: Ext.getBody(),
14230     items: [{
14231         xtype: 'textfield',
14232         fieldLabel: 'Name'
14233     }]
14234 });
14235 </code></pre>
14236      */
14237     /**
14238      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
14239      * label.  Defaults to the container's labelStyle value if set (e.g.,
14240      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
14241      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
14242      * <p>Also see <code>{@link #hideLabel}</code> and
14243      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
14244      * Example use:<pre><code>
14245 new Ext.FormPanel({
14246     height: 100,
14247     renderTo: Ext.getBody(),
14248     items: [{
14249         xtype: 'textfield',
14250         fieldLabel: 'Name',
14251         labelStyle: 'font-weight:bold;'
14252     }]
14253 });
14254 </code></pre>
14255      */
14256     /**
14257      * @cfg {String} labelSeparator <p>The separator to display after the text of each
14258      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
14259      * The order of precedence is:
14260      * <div class="mdetail-params"><ul>
14261      * <li>field / component level</li>
14262      * <li>container level</li>
14263      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
14264      * </ul></div>
14265      * To display no separator for this field's label specify empty string ''.</p>
14266      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
14267      * <p>Also see <tt>{@link #hideLabel}</tt> and
14268      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
14269      * Example use:<pre><code>
14270 new Ext.FormPanel({
14271     height: 100,
14272     renderTo: Ext.getBody(),
14273     layoutConfig: {
14274         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
14275     },
14276     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
14277     items: [{
14278         xtype: 'textfield',
14279         fieldLabel: 'Field 1',
14280         labelSeparator: '...' // field/component level config supersedes others
14281     },{
14282         xtype: 'textfield',
14283         fieldLabel: 'Field 2' // labelSeparator will be '='
14284     }]
14285 });
14286 </code></pre>
14287      */
14288     /**
14289      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
14290      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
14291      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
14292      * reserved so that the field will line up with other fields that do have labels.
14293      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
14294      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
14295      * Example use:<pre><code>
14296 new Ext.FormPanel({
14297     height: 100,
14298     renderTo: Ext.getBody(),
14299     items: [{
14300         xtype: 'textfield'
14301         hideLabel: true
14302     }]
14303 });
14304 </code></pre>
14305      */
14306     /**
14307      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
14308      * directly after each form field wrapper to provide field clearing (defaults to
14309      * <tt>'x-form-clear-left'</tt>).</p>
14310      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
14311      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
14312      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
14313      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
14314      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
14315      */
14316     /**
14317      * @cfg {String} itemCls
14318      * <p><b>Note</b>: this config is only used when this Component is rendered by a Container which
14319      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
14320      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
14321      * <p>An additional CSS class to apply to the div wrapping the form item
14322      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
14323      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
14324      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
14325      * <p>Since it is applied to the item wrapper (see
14326      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
14327      * you to write standard CSS rules that can apply to the field, the label (if specified), or
14328      * any other element within the markup for the field.</p>
14329      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
14330      * Example use:<pre><code>
14331 // Apply a style to the field&#39;s label:
14332 &lt;style>
14333     .required .x-form-item-label {font-weight:bold;color:red;}
14334 &lt;/style>
14335
14336 new Ext.FormPanel({
14337     height: 100,
14338     renderTo: Ext.getBody(),
14339     items: [{
14340         xtype: 'textfield',
14341         fieldLabel: 'Name',
14342         itemCls: 'required' //this label will be styled
14343     },{
14344         xtype: 'textfield',
14345         fieldLabel: 'Favorite Color'
14346     }]
14347 });
14348 </code></pre>
14349      */
14350
14351     // Configs below are used for all Components when rendered by AnchorLayout.
14352     /**
14353      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
14354      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout}</b>
14355      * based layout manager, for example:<div class="mdetail-params"><ul>
14356      * <li>{@link Ext.form.FormPanel}</li>
14357      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
14358      * </ul></div></p>
14359      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
14360      */
14361
14362     /**
14363      * @cfg {String} id
14364      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
14365      * You should assign an id if you need to be able to access the component later and you do
14366      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
14367      * <p>Note that this id will also be used as the element id for the containing HTML element
14368      * that is rendered to the page for this component. This allows you to write id-based CSS
14369      * rules to style the specific instance of this component uniquely, and also to select
14370      * sub-elements using this component's id as the parent.</p>
14371      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
14372      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
14373      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
14374      */
14375     /**
14376      * @cfg {String} itemId
14377      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
14378      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
14379      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
14380      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
14381      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
14382      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
14383      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
14384      * <code>{@link #id}</code>.</p>
14385      * <pre><code>
14386 var c = new Ext.Panel({ //
14387     {@link Ext.BoxComponent#height height}: 300,
14388     {@link #renderTo}: document.body,
14389     {@link Ext.Container#layout layout}: 'auto',
14390     {@link Ext.Container#items items}: [
14391         {
14392             itemId: 'p1',
14393             {@link Ext.Panel#title title}: 'Panel 1',
14394             {@link Ext.BoxComponent#height height}: 150
14395         },
14396         {
14397             itemId: 'p2',
14398             {@link Ext.Panel#title title}: 'Panel 2',
14399             {@link Ext.BoxComponent#height height}: 150
14400         }
14401     ]
14402 })
14403 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
14404 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
14405      * </code></pre>
14406      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
14407      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
14408      */
14409     /**
14410      * @cfg {String} xtype
14411      * The registered <tt>xtype</tt> to create. This config option is not used when passing
14412      * a config object into a constructor. This config option is used only when
14413      * lazy instantiation is being used, and a child item of a Container is being
14414      * specified not as a fully instantiated Component, but as a <i>Component config
14415      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
14416      * type of child Component to create.<br><br>
14417      * The predefined xtypes are listed {@link Ext.Component here}.
14418      * <br><br>
14419      * If you subclass Components to create your own Components, you may register
14420      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
14421      * take advantage of lazy instantiation and rendering.
14422      */
14423     /**
14424      * @cfg {String} ptype
14425      * The registered <tt>ptype</tt> to create. This config option is not used when passing
14426      * a config object into a constructor. This config option is used only when
14427      * lazy instantiation is being used, and a Plugin is being
14428      * specified not as a fully instantiated Component, but as a <i>Component config
14429      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
14430      * type of Plugin to create.<br><br>
14431      * If you create your own Plugins, you may register them using
14432      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
14433      * take advantage of lazy instantiation and rendering.
14434      */
14435     /**
14436      * @cfg {String} cls
14437      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
14438      * useful for adding customized styles to the component or any of its children using standard CSS rules.
14439      */
14440     /**
14441      * @cfg {String} overCls
14442      * An optional extra CSS class that will be added to this component's Element when the mouse moves
14443      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
14444      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
14445      */
14446     /**
14447      * @cfg {String} style
14448      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
14449      * {@link Ext.Element#applyStyles}.
14450      * <pre><code>
14451 new Ext.Panel({
14452     title: 'Some Title',
14453     renderTo: Ext.getBody(),
14454     width: 400, height: 300,
14455     layout: 'form',
14456     items: [{
14457         xtype: 'textarea',
14458         style: {
14459             width: '95%',
14460             marginBottom: '10px'
14461         }
14462     },
14463         new Ext.Button({
14464             text: 'Send',
14465             minWidth: '100',
14466             style: {
14467                 marginBottom: '10px'
14468             }
14469         })
14470     ]
14471 });
14472      * </code></pre>
14473      */
14474     /**
14475      * @cfg {String} ctCls
14476      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
14477      * adding customized styles to the container or any of its children using standard CSS rules.  See
14478      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
14479      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
14480      * which assigns a value by default:
14481      * <div class="mdetail-params"><ul>
14482      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
14483      * </ul></div>
14484      * To configure the above Class with an extra CSS class append to the default.  For example,
14485      * for BoxLayout (Hbox and Vbox):<pre><code>
14486      * ctCls: 'x-box-layout-ct custom-class'
14487      * </code></pre>
14488      * </p>
14489      */
14490     /**
14491      * @cfg {Boolean} disabled
14492      * Render this component disabled (default is false).
14493      */
14494     disabled : false,
14495     /**
14496      * @cfg {Boolean} hidden
14497      * Render this component hidden (default is false). If <tt>true</tt>, the
14498      * {@link #hide} method will be called internally.
14499      */
14500     hidden : false,
14501     /**
14502      * @cfg {Object/Array} plugins
14503      * An object or array of objects that will provide custom functionality for this component.  The only
14504      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
14505      * When a component is created, if any plugins are available, the component will call the init method on each
14506      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
14507      * component as needed to provide its functionality.
14508      */
14509     /**
14510      * @cfg {Mixed} applyTo
14511      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
14512      * that is already present in the document that specifies some structural markup for this
14513      * component.</p><div><ul>
14514      * <li><b>Description</b> : <ul>
14515      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
14516      * by id or CSS class name within the main element, and the component being created may attempt
14517      * to create its subcomponents from that markup if applicable.</div>
14518      * </ul></li>
14519      * <li><b>Notes</b> : <ul>
14520      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
14521      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
14522      * element's parent node will automatically be used as the component's container.</div>
14523      * </ul></li>
14524      * </ul></div>
14525      */
14526     /**
14527      * @cfg {Mixed} renderTo
14528      * <p>Specify the id of the element, a DOM element or an existing Element that this component
14529      * will be rendered into.</p><div><ul>
14530      * <li><b>Notes</b> : <ul>
14531      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
14532      * a {@link Ext.Container Container}. It is the responsibility of the
14533      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
14534      * to render and manage its child items.</div>
14535      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
14536      * </ul></li>
14537      * </ul></div>
14538      * <p>See <tt>{@link #render}</tt> also.</p>
14539      */
14540     /**
14541      * @cfg {Boolean} stateful
14542      * <p>A flag which causes the Component to attempt to restore the state of
14543      * internal properties from a saved state on startup. The component must have
14544      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
14545      * for state to be managed. Auto-generated ids are not guaranteed to be stable
14546      * across page loads and cannot be relied upon to save and restore the same
14547      * state for a component.<p>
14548      * <p>For state saving to work, the state manager's provider must have been
14549      * set to an implementation of {@link Ext.state.Provider} which overrides the
14550      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
14551      * methods to save and recall name/value pairs. A built-in implementation,
14552      * {@link Ext.state.CookieProvider} is available.</p>
14553      * <p>To set the state provider for the current page:</p>
14554      * <pre><code>
14555 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
14556     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
14557 }));
14558      * </code></pre>
14559      * <p>A stateful Component attempts to save state when one of the events
14560      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
14561      * <p>To save state, a stateful Component first serializes its state by
14562      * calling <b><code>getState</code></b>. By default, this function does
14563      * nothing. The developer must provide an implementation which returns an
14564      * object hash which represents the Component's restorable state.</p>
14565      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
14566      * which uses the configured {@link Ext.state.Provider} to save the object
14567      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
14568      * specified, its <code>{@link #id}</code>.</p>
14569      * <p>During construction, a stateful Component attempts to <i>restore</i>
14570      * its state by calling {@link Ext.state.Manager#get} passing the
14571      * <code>{@link #stateId}</code>, or, if that is not specified, the
14572      * <code>{@link #id}</code>.</p>
14573      * <p>The resulting object is passed to <b><code>applyState</code></b>.
14574      * The default implementation of <code>applyState</code> simply copies
14575      * properties into the object, but a developer may override this to support
14576      * more behaviour.</p>
14577      * <p>You can perform extra processing on state save and restore by attaching
14578      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
14579      * {@link #beforestatesave} and {@link #statesave} events.</p>
14580      */
14581     /**
14582      * @cfg {String} stateId
14583      * The unique id for this component to use for state management purposes
14584      * (defaults to the component id if one was set, otherwise null if the
14585      * component is using a generated id).
14586      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
14587      * restoring Component state.</p>
14588      */
14589     /**
14590      * @cfg {Array} stateEvents
14591      * <p>An array of events that, when fired, should trigger this component to
14592      * save its state (defaults to none). <code>stateEvents</code> may be any type
14593      * of event supported by this component, including browser or custom events
14594      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
14595      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
14596      * restoring Component state.</p>
14597      */
14598     /**
14599      * @cfg {Mixed} autoEl
14600      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
14601      * encapsulate this Component.</p>
14602      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
14603      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
14604      * DOM structure created by their own onRender methods.</p>
14605      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
14606      * different DOM elements. Example usage:</p><pre><code>
14607 {
14608     xtype: 'box',
14609     autoEl: {
14610         tag: 'img',
14611         src: 'http://www.example.com/example.jpg'
14612     }
14613 }, {
14614     xtype: 'box',
14615     autoEl: {
14616         tag: 'blockquote',
14617         html: 'autoEl is cool!'
14618     }
14619 }, {
14620     xtype: 'container',
14621     autoEl: 'ul',
14622     cls: 'ux-unordered-list',
14623     items: {
14624         xtype: 'box',
14625         autoEl: 'li',
14626         html: 'First list item'
14627     }
14628 }
14629 </code></pre>
14630      */
14631     autoEl : 'div',
14632
14633     /**
14634      * @cfg {String} disabledClass
14635      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
14636      */
14637     disabledClass : 'x-item-disabled',
14638     /**
14639      * @cfg {Boolean} allowDomMove
14640      * Whether the component can move the Dom node when rendering (defaults to true).
14641      */
14642     allowDomMove : true,
14643     /**
14644      * @cfg {Boolean} autoShow
14645      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
14646      * them on render (defaults to false).
14647      */
14648     autoShow : false,
14649     /**
14650      * @cfg {String} hideMode
14651      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
14652      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
14653      * (css display).</p>
14654      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
14655      * since items are automatically laid out when they are first shown (no sizing
14656      * is done while hidden).</p>
14657      */
14658     hideMode : 'display',
14659     /**
14660      * @cfg {Boolean} hideParent
14661      * True to hide and show the component's container when hide/show is called on the component, false to hide
14662      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
14663      * button on a window by setting hide:true on the button when adding it to its parent container.
14664      */
14665     hideParent : false,
14666     /**
14667      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
14668      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
14669      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
14670      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
14671      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
14672      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
14673      * config for a suggestion, or use a render listener directly:</p><pre><code>
14674 new Ext.Panel({
14675     title: 'The Clickable Panel',
14676     listeners: {
14677         render: function(p) {
14678             // Append the Panel to the click handler&#39;s argument list.
14679             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
14680         },
14681         single: true  // Remove the listener after first invocation
14682     }
14683 });
14684 </code></pre>
14685      * <p>See also <tt>{@link #getEl getEl}</p>
14686      * @type Ext.Element
14687      * @property el
14688      */
14689     /**
14690      * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when
14691      * this Component is added to a Container).  Read-only.
14692      * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
14693      * @type Ext.Container
14694      * @property ownerCt
14695      */
14696     /**
14697      * True if this component is hidden. Read-only.
14698      * @type Boolean
14699      * @property hidden
14700      */
14701     /**
14702      * True if this component is disabled. Read-only.
14703      * @type Boolean
14704      * @property disabled
14705      */
14706     /**
14707      * True if this component has been rendered. Read-only.
14708      * @type Boolean
14709      * @property rendered
14710      */
14711     rendered : false,
14712
14713     // private
14714     ctype : 'Ext.Component',
14715
14716     // private
14717     actionMode : 'el',
14718
14719     // private
14720     getActionEl : function(){
14721         return this[this.actionMode];
14722     },
14723
14724     initPlugin : function(p){
14725         if(p.ptype && !Ext.isFunction(p.init)){
14726             p = Ext.ComponentMgr.createPlugin(p);
14727         }else if(Ext.isString(p)){
14728             p = Ext.ComponentMgr.createPlugin({
14729                 ptype: p
14730             });
14731         }
14732         p.init(this);
14733         return p;
14734     },
14735
14736     /* // protected
14737      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
14738      * <pre><code>
14739 // Traditional constructor:
14740 Ext.Foo = function(config){
14741     // call superclass constructor:
14742     Ext.Foo.superclass.constructor.call(this, config);
14743
14744     this.addEvents({
14745         // add events
14746     });
14747 };
14748 Ext.extend(Ext.Foo, Ext.Bar, {
14749    // class body
14750 }
14751
14752 // initComponent replaces the constructor:
14753 Ext.Foo = Ext.extend(Ext.Bar, {
14754     initComponent : function(){
14755         // call superclass initComponent
14756         Ext.Container.superclass.initComponent.call(this);
14757
14758         this.addEvents({
14759             // add events
14760         });
14761     }
14762 }
14763 </code></pre>
14764      */
14765     initComponent : Ext.emptyFn,
14766
14767     /**
14768      * <p>Render this Component into the passed HTML element.</p>
14769      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
14770      * do not use the render method.</b></p>
14771      * <p>A Container's child Components are rendered by that Container's
14772      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
14773      * <p>Certain layout managers allow dynamic addition of child components. Those that do
14774      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
14775      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
14776      * <p>If the Container is already rendered when a new child Component is added, you may need to call
14777      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
14778      * unrendered child Components to be rendered. This is required so that you can add multiple
14779      * child components if needed while only refreshing the layout once.</p>
14780      * <p>When creating complex UIs, it is important to remember that sizing and positioning
14781      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
14782      * If you expect child items to be sized in response to user interactions, you must
14783      * configure the Container with a layout manager which creates and manages the type of layout you
14784      * have in mind.</p>
14785      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
14786      * layout manager is used which does nothing but render child components sequentially into the
14787      * Container. No sizing or positioning will be performed in this situation.</b></p>
14788      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
14789      * rendered into. If it is being created from existing markup, this should be omitted.
14790      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
14791      * which this component will be inserted (defaults to appending to the end of the container)
14792      */
14793     render : function(container, position){
14794         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
14795             if(!container && this.el){
14796                 this.el = Ext.get(this.el);
14797                 container = this.el.dom.parentNode;
14798                 this.allowDomMove = false;
14799             }
14800             this.container = Ext.get(container);
14801             if(this.ctCls){
14802                 this.container.addClass(this.ctCls);
14803             }
14804             this.rendered = true;
14805             if(position !== undefined){
14806                 if(Ext.isNumber(position)){
14807                     position = this.container.dom.childNodes[position];
14808                 }else{
14809                     position = Ext.getDom(position);
14810                 }
14811             }
14812             this.onRender(this.container, position || null);
14813             if(this.autoShow){
14814                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
14815             }
14816             if(this.cls){
14817                 this.el.addClass(this.cls);
14818                 delete this.cls;
14819             }
14820             if(this.style){
14821                 this.el.applyStyles(this.style);
14822                 delete this.style;
14823             }
14824             if(this.overCls){
14825                 this.el.addClassOnOver(this.overCls);
14826             }
14827             this.fireEvent('render', this);
14828             this.afterRender(this.container);
14829             if(this.hidden){
14830                 // call this so we don't fire initial hide events.
14831                 this.doHide();
14832             }
14833             if(this.disabled){
14834                 // pass silent so the event doesn't fire the first time.
14835                 this.disable(true);
14836             }
14837
14838             if(this.stateful !== false){
14839                 this.initStateEvents();
14840             }
14841             this.initRef();
14842             this.fireEvent('afterrender', this);
14843         }
14844         return this;
14845     },
14846
14847     initRef : function(){
14848         /**
14849          * @cfg {String} ref
14850          * <p>A path specification, relative to the Component's {@link #ownerCt} specifying into which
14851          * ancestor Container to place a named reference to this Component.</p>
14852          * <p>The ancestor axis can be traversed by using '/' characters in the path.
14853          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
14854 var myGrid = new Ext.grid.EditorGridPanel({
14855     title: 'My EditorGridPanel',
14856     store: myStore,
14857     colModel: myColModel,
14858     tbar: [{
14859         text: 'Save',
14860         handler: saveChanges,
14861         disabled: true,
14862         ref: '../saveButton'
14863     }],
14864     listeners: {
14865         afteredit: function() {
14866 //          The button reference is in the GridPanel
14867             myGrid.saveButton.enable();
14868         }
14869     }
14870 });
14871 </code></pre>
14872          * <p>In the code above, if the ref had been <code>'saveButton'</code> the reference would
14873          * have been placed into the Toolbar. Each '/' in the ref moves up one level from the
14874          * Component's {@link #ownerCt}.</p>
14875          */
14876         if(this.ref){
14877             var levels = this.ref.split('/');
14878             var last = levels.length, i = 0;
14879             var t = this;
14880             while(i < last){
14881                 if(t.ownerCt){
14882                     t = t.ownerCt;
14883                 }
14884                 i++;
14885             }
14886             t[levels[--i]] = this;
14887         }
14888     },
14889
14890     // private
14891     initState : function(config){
14892         if(Ext.state.Manager){
14893             var id = this.getStateId();
14894             if(id){
14895                 var state = Ext.state.Manager.get(id);
14896                 if(state){
14897                     if(this.fireEvent('beforestaterestore', this, state) !== false){
14898                         this.applyState(state);
14899                         this.fireEvent('staterestore', this, state);
14900                     }
14901                 }
14902             }
14903         }
14904     },
14905
14906     // private
14907     getStateId : function(){
14908         return this.stateId || ((this.id.indexOf('ext-comp-') == 0 || this.id.indexOf('ext-gen') == 0) ? null : this.id);
14909     },
14910
14911     // private
14912     initStateEvents : function(){
14913         if(this.stateEvents){
14914             for(var i = 0, e; e = this.stateEvents[i]; i++){
14915                 this.on(e, this.saveState, this, {delay:100});
14916             }
14917         }
14918     },
14919
14920     // private
14921     applyState : function(state){
14922         if(state){
14923             Ext.apply(this, state);
14924         }
14925     },
14926
14927     // private
14928     getState : function(){
14929         return null;
14930     },
14931
14932     // private
14933     saveState : function(){
14934         if(Ext.state.Manager && this.stateful !== false){
14935             var id = this.getStateId();
14936             if(id){
14937                 var state = this.getState();
14938                 if(this.fireEvent('beforestatesave', this, state) !== false){
14939                     Ext.state.Manager.set(id, state);
14940                     this.fireEvent('statesave', this, state);
14941                 }
14942             }
14943         }
14944     },
14945
14946     /**
14947      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
14948      * @param {String/HTMLElement} el
14949      */
14950     applyToMarkup : function(el){
14951         this.allowDomMove = false;
14952         this.el = Ext.get(el);
14953         this.render(this.el.dom.parentNode);
14954     },
14955
14956     /**
14957      * Adds a CSS class to the component's underlying element.
14958      * @param {string} cls The CSS class name to add
14959      * @return {Ext.Component} this
14960      */
14961     addClass : function(cls){
14962         if(this.el){
14963             this.el.addClass(cls);
14964         }else{
14965             this.cls = this.cls ? this.cls + ' ' + cls : cls;
14966         }
14967         return this;
14968     },
14969
14970     /**
14971      * Removes a CSS class from the component's underlying element.
14972      * @param {string} cls The CSS class name to remove
14973      * @return {Ext.Component} this
14974      */
14975     removeClass : function(cls){
14976         if(this.el){
14977             this.el.removeClass(cls);
14978         }else if(this.cls){
14979             this.cls = this.cls.split(' ').remove(cls).join(' ');
14980         }
14981         return this;
14982     },
14983
14984     // private
14985     // default function is not really useful
14986     onRender : function(ct, position){
14987         if(!this.el && this.autoEl){
14988             if(Ext.isString(this.autoEl)){
14989                 this.el = document.createElement(this.autoEl);
14990             }else{
14991                 var div = document.createElement('div');
14992                 Ext.DomHelper.overwrite(div, this.autoEl);
14993                 this.el = div.firstChild;
14994             }
14995             if (!this.el.id) {
14996                 this.el.id = this.getId();
14997             }
14998         }
14999         if(this.el){
15000             this.el = Ext.get(this.el);
15001             if(this.allowDomMove !== false){
15002                 ct.dom.insertBefore(this.el.dom, position);
15003             }
15004         }
15005     },
15006
15007     // private
15008     getAutoCreate : function(){
15009         var cfg = Ext.isObject(this.autoCreate) ?
15010                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
15011         if(this.id && !cfg.id){
15012             cfg.id = this.id;
15013         }
15014         return cfg;
15015     },
15016
15017     // private
15018     afterRender : Ext.emptyFn,
15019
15020     /**
15021      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15022      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
15023      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
15024      * should usually not need to be called directly.
15025      *
15026      */
15027     destroy : function(){
15028         if(!this.isDestroyed){
15029             if(this.fireEvent('beforedestroy', this) !== false){
15030                 this.beforeDestroy();
15031                 if(this.rendered){
15032                     this.el.removeAllListeners();
15033                     this.el.remove();
15034                     if(this.actionMode == 'container' || this.removeMode == 'container'){
15035                         this.container.remove();
15036                     }
15037                 }
15038                 this.onDestroy();
15039                 Ext.ComponentMgr.unregister(this);
15040                 this.fireEvent('destroy', this);
15041                 this.purgeListeners();
15042                 this.isDestroyed = true;
15043             }
15044         }
15045     },
15046
15047     // private
15048     beforeDestroy : Ext.emptyFn,
15049
15050     // private
15051     onDestroy  : Ext.emptyFn,
15052
15053     /**
15054      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
15055      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15056      * that may be overridden using the {@link #autoEl} config.</p>
15057      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15058      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15059      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
15060      * or use a render listener directly:</p><pre><code>
15061 new Ext.Panel({
15062     title: 'The Clickable Panel',
15063     listeners: {
15064         render: function(p) {
15065             // Append the Panel to the click handler&#39;s argument list.
15066             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15067         },
15068         single: true  // Remove the listener after first invocation
15069     }
15070 });
15071 </code></pre>
15072      * @return {Ext.Element} The Element which encapsulates this Component.
15073      */
15074     getEl : function(){
15075         return this.el;
15076     },
15077
15078     /**
15079      * Returns the <code>id</code> of this component or automatically generates and
15080      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
15081      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
15082      * </code></pre>
15083      * @return {String} id
15084      */
15085     getId : function(){
15086         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
15087     },
15088
15089     /**
15090      * Returns the <code>{@link #itemId}</code> of this component.  If an
15091      * <code>{@link #itemId}</code> was not assigned through configuration the
15092      * <code>id</code> is returned using <code>{@link #getId}</code>.
15093      * @return {String}
15094      */
15095     getItemId : function(){
15096         return this.itemId || this.getId();
15097     },
15098
15099     /**
15100      * Try to focus this component.
15101      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
15102      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
15103      * @return {Ext.Component} this
15104      */
15105     focus : function(selectText, delay){
15106         if(delay){
15107             this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]);
15108             return;
15109         }
15110         if(this.rendered){
15111             this.el.focus();
15112             if(selectText === true){
15113                 this.el.dom.select();
15114             }
15115         }
15116         return this;
15117     },
15118
15119     // private
15120     blur : function(){
15121         if(this.rendered){
15122             this.el.blur();
15123         }
15124         return this;
15125     },
15126
15127     /**
15128      * Disable this component and fire the 'disable' event.
15129      * @return {Ext.Component} this
15130      */
15131     disable : function(/* private */ silent){
15132         if(this.rendered){
15133             this.onDisable();
15134         }
15135         this.disabled = true;
15136         if(silent !== true){
15137             this.fireEvent('disable', this);
15138         }
15139         return this;
15140     },
15141
15142     // private
15143     onDisable : function(){
15144         this.getActionEl().addClass(this.disabledClass);
15145         this.el.dom.disabled = true;
15146     },
15147
15148     /**
15149      * Enable this component and fire the 'enable' event.
15150      * @return {Ext.Component} this
15151      */
15152     enable : function(){
15153         if(this.rendered){
15154             this.onEnable();
15155         }
15156         this.disabled = false;
15157         this.fireEvent('enable', this);
15158         return this;
15159     },
15160
15161     // private
15162     onEnable : function(){
15163         this.getActionEl().removeClass(this.disabledClass);
15164         this.el.dom.disabled = false;
15165     },
15166
15167     /**
15168      * Convenience function for setting disabled/enabled by boolean.
15169      * @param {Boolean} disabled
15170      * @return {Ext.Component} this
15171      */
15172     setDisabled : function(disabled){
15173         return this[disabled ? 'disable' : 'enable']();
15174     },
15175
15176     /**
15177      * Show this component.  Listen to the '{@link #beforeshow}' event and return
15178      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
15179      * event after showing the component.
15180      * @return {Ext.Component} this
15181      */
15182     show : function(){
15183         if(this.fireEvent('beforeshow', this) !== false){
15184             this.hidden = false;
15185             if(this.autoRender){
15186                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
15187             }
15188             if(this.rendered){
15189                 this.onShow();
15190             }
15191             this.fireEvent('show', this);
15192         }
15193         return this;
15194     },
15195
15196     // private
15197     onShow : function(){
15198         this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
15199     },
15200
15201     /**
15202      * Hide this component.  Listen to the '{@link #beforehide}' event and return
15203      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
15204      * event after hiding the component. Note this method is called internally if
15205      * the component is configured to be <code>{@link #hidden}</code>.
15206      * @return {Ext.Component} this
15207      */
15208     hide : function(){
15209         if(this.fireEvent('beforehide', this) !== false){
15210             this.doHide();
15211             this.fireEvent('hide', this);
15212         }
15213         return this;
15214     },
15215
15216     // private
15217     doHide: function(){
15218         this.hidden = true;
15219         if(this.rendered){
15220             this.onHide();
15221         }
15222     },
15223
15224     // private
15225     onHide : function(){
15226         this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
15227     },
15228
15229     // private
15230     getVisibilityEl : function(){
15231         return this.hideParent ? this.container : this.getActionEl();
15232     },
15233
15234     /**
15235      * Convenience function to hide or show this component by boolean.
15236      * @param {Boolean} visible True to show, false to hide
15237      * @return {Ext.Component} this
15238      */
15239     setVisible : function(visible){
15240         return this[visible ? 'show' : 'hide']();
15241     },
15242
15243     /**
15244      * Returns true if this component is visible.
15245      * @return {Boolean} True if this component is visible, false otherwise.
15246      */
15247     isVisible : function(){
15248         return this.rendered && this.getVisibilityEl().isVisible();
15249     },
15250
15251     /**
15252      * Clone the current component using the original config values passed into this instance by default.
15253      * @param {Object} overrides A new config containing any properties to override in the cloned version.
15254      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
15255      * @return {Ext.Component} clone The cloned copy of this component
15256      */
15257     cloneConfig : function(overrides){
15258         overrides = overrides || {};
15259         var id = overrides.id || Ext.id();
15260         var cfg = Ext.applyIf(overrides, this.initialConfig);
15261         cfg.id = id; // prevent dup id
15262         return new this.constructor(cfg);
15263     },
15264
15265     /**
15266      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
15267      * available xtypes, see the {@link Ext.Component} header. Example usage:
15268      * <pre><code>
15269 var t = new Ext.form.TextField();
15270 alert(t.getXType());  // alerts 'textfield'
15271 </code></pre>
15272      * @return {String} The xtype
15273      */
15274     getXType : function(){
15275         return this.constructor.xtype;
15276     },
15277
15278     /**
15279      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
15280      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
15281      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
15282      * to participate in determination of inherited xtypes.</b></p>
15283      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
15284      * <p>Example usage:</p>
15285      * <pre><code>
15286 var t = new Ext.form.TextField();
15287 var isText = t.isXType('textfield');        // true
15288 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
15289 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
15290 </code></pre>
15291      * @param {String} xtype The xtype to check for this Component
15292      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
15293      * the default), or true to check whether this Component is directly of the specified xtype.
15294      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
15295      */
15296     isXType : function(xtype, shallow){
15297         //assume a string by default
15298         if (Ext.isFunction(xtype)){
15299             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
15300         }else if (Ext.isObject(xtype)){
15301             xtype = xtype.constructor.xtype; //handle being passed an instance
15302         }
15303
15304         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
15305     },
15306
15307     /**
15308      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
15309      * available xtypes, see the {@link Ext.Component} header.</p>
15310      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
15311      * to participate in determination of inherited xtypes.</b></p>
15312      * <p>Example usage:</p>
15313      * <pre><code>
15314 var t = new Ext.form.TextField();
15315 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
15316 </code></pre>
15317      * @return {String} The xtype hierarchy string
15318      */
15319     getXTypes : function(){
15320         var tc = this.constructor;
15321         if(!tc.xtypes){
15322             var c = [], sc = this;
15323             while(sc && sc.constructor.xtype){
15324                 c.unshift(sc.constructor.xtype);
15325                 sc = sc.constructor.superclass;
15326             }
15327             tc.xtypeChain = c;
15328             tc.xtypes = c.join('/');
15329         }
15330         return tc.xtypes;
15331     },
15332
15333     /**
15334      * Find a container above this component at any level by a custom function. If the passed function returns
15335      * true, the container will be returned.
15336      * @param {Function} fn The custom function to call with the arguments (container, this component).
15337      * @return {Ext.Container} The first Container for which the custom function returns true
15338      */
15339     findParentBy : function(fn) {
15340         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
15341         return p || null;
15342     },
15343
15344     /**
15345      * Find a container above this component at any level by xtype or class
15346      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
15347      * @return {Ext.Container} The first Container which matches the given xtype or class
15348      */
15349     findParentByType : function(xtype) {
15350         return Ext.isFunction(xtype) ?
15351             this.findParentBy(function(p){
15352                 return p.constructor === xtype;
15353             }) :
15354             this.findParentBy(function(p){
15355                 return p.constructor.xtype === xtype;
15356             });
15357     },
15358
15359     getDomPositionEl : function(){
15360         return this.getPositionEl ? this.getPositionEl() : this.getEl();
15361     },
15362
15363     // private
15364     purgeListeners : function(){
15365         Ext.Component.superclass.purgeListeners.call(this);
15366         if(this.mons){
15367             this.on('beforedestroy', this.clearMons, this, {single: true});
15368         }
15369     },
15370
15371     // private
15372     clearMons : function(){
15373         Ext.each(this.mons, function(m){
15374             m.item.un(m.ename, m.fn, m.scope);
15375         }, this);
15376         this.mons = [];
15377     },
15378     
15379     // private
15380     createMons: function(){
15381         if(!this.mons){
15382             this.mons = [];
15383             this.on('beforedestroy', this.clearMons, this, {single: true});
15384         }
15385     },
15386
15387     // internal function for auto removal of assigned event handlers on destruction
15388     mon : function(item, ename, fn, scope, opt){
15389         this.createMons();
15390         if(Ext.isObject(ename)){
15391             var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
15392
15393             var o = ename;
15394             for(var e in o){
15395                 if(propRe.test(e)){
15396                     continue;
15397                 }
15398                 if(Ext.isFunction(o[e])){
15399                     // shared options
15400                     this.mons.push({
15401                         item: item, ename: e, fn: o[e], scope: o.scope
15402                     });
15403                     item.on(e, o[e], o.scope, o);
15404                 }else{
15405                     // individual options
15406                     this.mons.push({
15407                         item: item, ename: e, fn: o[e], scope: o.scope
15408                     });
15409                     item.on(e, o[e]);
15410                 }
15411             }
15412             return;
15413         }
15414
15415         this.mons.push({
15416             item: item, ename: ename, fn: fn, scope: scope
15417         });
15418         item.on(ename, fn, scope, opt);
15419     },
15420
15421     // protected, opposite of mon
15422     mun : function(item, ename, fn, scope){
15423         var found, mon;
15424         this.createMons();
15425         for(var i = 0, len = this.mons.length; i < len; ++i){
15426             mon = this.mons[i];
15427             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
15428                 this.mons.splice(i, 1);
15429                 item.un(ename, fn, scope);
15430                 found = true;
15431                 break;
15432             }
15433         }
15434         return found;
15435     },
15436
15437     /**
15438      * Returns the next component in the owning container
15439      * @return Ext.Component
15440      */
15441     nextSibling : function(){
15442         if(this.ownerCt){
15443             var index = this.ownerCt.items.indexOf(this);
15444             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
15445                 return this.ownerCt.items.itemAt(index+1);
15446             }
15447         }
15448         return null;
15449     },
15450
15451     /**
15452      * Returns the previous component in the owning container
15453      * @return Ext.Component
15454      */
15455     previousSibling : function(){
15456         if(this.ownerCt){
15457             var index = this.ownerCt.items.indexOf(this);
15458             if(index > 0){
15459                 return this.ownerCt.items.itemAt(index-1);
15460             }
15461         }
15462         return null;
15463     },
15464
15465     /**
15466      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
15467      * @return {Ext.Container} the Container which owns this Component.
15468      */
15469     getBubbleTarget : function(){
15470         return this.ownerCt;
15471     }
15472 });
15473
15474 Ext.reg('component', Ext.Component);
15475 /**\r
15476  * @class Ext.Action\r
15477  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it\r
15478  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI\r
15479  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}\r
15480  * and {@link Ext.menu.Menu} components).</p>\r
15481  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support\r
15482  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),\r
15483  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>\r
15484  * Example usage:<br>\r
15485  * <pre><code>\r
15486 // Define the shared action.  Each component below will have the same\r
15487 // display text and icon, and will display the same message on click.\r
15488 var action = new Ext.Action({\r
15489     {@link #text}: 'Do something',\r
15490     {@link #handler}: function(){\r
15491         Ext.Msg.alert('Click', 'You did something.');\r
15492     },\r
15493     {@link #iconCls}: 'do-something',\r
15494     {@link #itemId}: 'myAction'\r
15495 });\r
15496 \r
15497 var panel = new Ext.Panel({\r
15498     title: 'Actions',\r
15499     width: 500,\r
15500     height: 300,\r
15501     tbar: [\r
15502         // Add the action directly to a toolbar as a menu button\r
15503         action,\r
15504         {\r
15505             text: 'Action Menu',\r
15506             // Add the action to a menu as a text item\r
15507             menu: [action]\r
15508         }\r
15509     ],\r
15510     items: [\r
15511         // Add the action to the panel body as a standard button\r
15512         new Ext.Button(action)\r
15513     ],\r
15514     renderTo: Ext.getBody()\r
15515 });\r
15516 \r
15517 // Change the text for all components using the action\r
15518 action.setText('Something else');\r
15519 \r
15520 // Reference an action through a container using the itemId\r
15521 var btn = panel.getComponent('myAction');\r
15522 var aRef = btn.baseAction;\r
15523 aRef.setText('New text');\r
15524 </code></pre>\r
15525  * @constructor\r
15526  * @param {Object} config The configuration options\r
15527  */\r
15528 Ext.Action = function(config){\r
15529     this.initialConfig = config;\r
15530     this.itemId = config.itemId = (config.itemId || config.id || Ext.id());\r
15531     this.items = [];\r
15532 }\r
15533 \r
15534 Ext.Action.prototype = {\r
15535     /**\r
15536      * @cfg {String} text The text to set for all components using this action (defaults to '').\r
15537      */\r
15538     /**\r
15539      * @cfg {String} iconCls\r
15540      * The CSS class selector that specifies a background image to be used as the header icon for\r
15541      * all components using this action (defaults to '').\r
15542      * <p>An example of specifying a custom icon class would be something like:\r
15543      * </p><pre><code>\r
15544 // specify the property in the config for the class:\r
15545      ...\r
15546      iconCls: 'do-something'\r
15547 \r
15548 // css class that specifies background image to be used as the icon image:\r
15549 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }\r
15550 </code></pre>\r
15551      */\r
15552     /**\r
15553      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).\r
15554      */\r
15555     /**\r
15556      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).\r
15557      */\r
15558     /**\r
15559      * @cfg {Function} handler The function that will be invoked by each component tied to this action\r
15560      * when the component's primary event is triggered (defaults to undefined).\r
15561      */\r
15562     /**\r
15563      * @cfg {String} itemId\r
15564      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.\r
15565      */\r
15566     /**\r
15567      * @cfg {Object} scope The scope in which the {@link #handler} function will execute.\r
15568      */\r
15569 \r
15570     // private\r
15571     isAction : true,\r
15572 \r
15573     /**\r
15574      * Sets the text to be displayed by all components using this action.\r
15575      * @param {String} text The text to display\r
15576      */\r
15577     setText : function(text){\r
15578         this.initialConfig.text = text;\r
15579         this.callEach('setText', [text]);\r
15580     },\r
15581 \r
15582     /**\r
15583      * Gets the text currently displayed by all components using this action.\r
15584      */\r
15585     getText : function(){\r
15586         return this.initialConfig.text;\r
15587     },\r
15588 \r
15589     /**\r
15590      * Sets the icon CSS class for all components using this action.  The class should supply\r
15591      * a background image that will be used as the icon image.\r
15592      * @param {String} cls The CSS class supplying the icon image\r
15593      */\r
15594     setIconClass : function(cls){\r
15595         this.initialConfig.iconCls = cls;\r
15596         this.callEach('setIconClass', [cls]);\r
15597     },\r
15598 \r
15599     /**\r
15600      * Gets the icon CSS class currently used by all components using this action.\r
15601      */\r
15602     getIconClass : function(){\r
15603         return this.initialConfig.iconCls;\r
15604     },\r
15605 \r
15606     /**\r
15607      * Sets the disabled state of all components using this action.  Shortcut method\r
15608      * for {@link #enable} and {@link #disable}.\r
15609      * @param {Boolean} disabled True to disable the component, false to enable it\r
15610      */\r
15611     setDisabled : function(v){\r
15612         this.initialConfig.disabled = v;\r
15613         this.callEach('setDisabled', [v]);\r
15614     },\r
15615 \r
15616     /**\r
15617      * Enables all components using this action.\r
15618      */\r
15619     enable : function(){\r
15620         this.setDisabled(false);\r
15621     },\r
15622 \r
15623     /**\r
15624      * Disables all components using this action.\r
15625      */\r
15626     disable : function(){\r
15627         this.setDisabled(true);\r
15628     },\r
15629 \r
15630     /**\r
15631      * Returns true if the components using this action are currently disabled, else returns false.  \r
15632      */\r
15633     isDisabled : function(){\r
15634         return this.initialConfig.disabled;\r
15635     },\r
15636 \r
15637     /**\r
15638      * Sets the hidden state of all components using this action.  Shortcut method\r
15639      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.\r
15640      * @param {Boolean} hidden True to hide the component, false to show it\r
15641      */\r
15642     setHidden : function(v){\r
15643         this.initialConfig.hidden = v;\r
15644         this.callEach('setVisible', [!v]);\r
15645     },\r
15646 \r
15647     /**\r
15648      * Shows all components using this action.\r
15649      */\r
15650     show : function(){\r
15651         this.setHidden(false);\r
15652     },\r
15653 \r
15654     /**\r
15655      * Hides all components using this action.\r
15656      */\r
15657     hide : function(){\r
15658         this.setHidden(true);\r
15659     },\r
15660 \r
15661     /**\r
15662      * Returns true if the components using this action are currently hidden, else returns false.  \r
15663      */\r
15664     isHidden : function(){\r
15665         return this.initialConfig.hidden;\r
15666     },\r
15667 \r
15668     /**\r
15669      * Sets the function that will be called by each component using this action when its primary event is triggered.\r
15670      * @param {Function} fn The function that will be invoked by the action's components.  The function\r
15671      * will be called with no arguments.\r
15672      * @param {Object} scope The scope in which the function will execute\r
15673      */\r
15674     setHandler : function(fn, scope){\r
15675         this.initialConfig.handler = fn;\r
15676         this.initialConfig.scope = scope;\r
15677         this.callEach('setHandler', [fn, scope]);\r
15678     },\r
15679 \r
15680     /**\r
15681      * Executes the specified function once for each component currently tied to this action.  The function passed\r
15682      * in should accept a single argument that will be an object that supports the basic Action config/method interface.\r
15683      * @param {Function} fn The function to execute for each component\r
15684      * @param {Object} scope The scope in which the function will execute\r
15685      */\r
15686     each : function(fn, scope){\r
15687         Ext.each(this.items, fn, scope);\r
15688     },\r
15689 \r
15690     // private\r
15691     callEach : function(fnName, args){\r
15692         var cs = this.items;\r
15693         for(var i = 0, len = cs.length; i < len; i++){\r
15694             cs[i][fnName].apply(cs[i], args);\r
15695         }\r
15696     },\r
15697 \r
15698     // private\r
15699     addComponent : function(comp){\r
15700         this.items.push(comp);\r
15701         comp.on('destroy', this.removeComponent, this);\r
15702     },\r
15703 \r
15704     // private\r
15705     removeComponent : function(comp){\r
15706         this.items.remove(comp);\r
15707     },\r
15708 \r
15709     /**\r
15710      * Executes this action manually using the handler function specified in the original config object\r
15711      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this\r
15712      * function will be passed on to the handler function.\r
15713      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function\r
15714      * @param {Mixed} arg2 (optional)\r
15715      * @param {Mixed} etc... (optional)\r
15716      */\r
15717     execute : function(){\r
15718         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);\r
15719     }\r
15720 };
15721 /**
15722  * @class Ext.Layer
15723  * @extends Ext.Element
15724  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
15725  * automatic maintaining of shadow/shim positions.
15726  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
15727  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
15728  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
15729  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
15730  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
15731  * @cfg {String} cls CSS class to add to the element
15732  * @cfg {Number} zindex Starting z-index (defaults to 11000)
15733  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
15734  * @cfg {Boolean} useDisplay
15735  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
15736  * to use css style <tt>'display:none;'</tt> to hide the Layer.
15737  * @constructor
15738  * @param {Object} config An object with config options.
15739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
15740  */
15741 (function(){
15742 Ext.Layer = function(config, existingEl){
15743     config = config || {};
15744     var dh = Ext.DomHelper;
15745     var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
15746     if(existingEl){
15747         this.dom = Ext.getDom(existingEl);
15748     }
15749     if(!this.dom){
15750         var o = config.dh || {tag: 'div', cls: 'x-layer'};
15751         this.dom = dh.append(pel, o);
15752     }
15753     if(config.cls){
15754         this.addClass(config.cls);
15755     }
15756     this.constrain = config.constrain !== false;
15757     this.setVisibilityMode(Ext.Element.VISIBILITY);
15758     if(config.id){
15759         this.id = this.dom.id = config.id;
15760     }else{
15761         this.id = Ext.id(this.dom);
15762     }
15763     this.zindex = config.zindex || this.getZIndex();
15764     this.position('absolute', this.zindex);
15765     if(config.shadow){
15766         this.shadowOffset = config.shadowOffset || 4;
15767         this.shadow = new Ext.Shadow({
15768             offset : this.shadowOffset,
15769             mode : config.shadow
15770         });
15771     }else{
15772         this.shadowOffset = 0;
15773     }
15774     this.useShim = config.shim !== false && Ext.useShims;
15775     this.useDisplay = config.useDisplay;
15776     this.hide();
15777 };
15778
15779 var supr = Ext.Element.prototype;
15780
15781 // shims are shared among layer to keep from having 100 iframes
15782 var shims = [];
15783
15784 Ext.extend(Ext.Layer, Ext.Element, {
15785
15786     getZIndex : function(){
15787         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
15788     },
15789
15790     getShim : function(){
15791         if(!this.useShim){
15792             return null;
15793         }
15794         if(this.shim){
15795             return this.shim;
15796         }
15797         var shim = shims.shift();
15798         if(!shim){
15799             shim = this.createShim();
15800             shim.enableDisplayMode('block');
15801             shim.dom.style.display = 'none';
15802             shim.dom.style.visibility = 'visible';
15803         }
15804         var pn = this.dom.parentNode;
15805         if(shim.dom.parentNode != pn){
15806             pn.insertBefore(shim.dom, this.dom);
15807         }
15808         shim.setStyle('z-index', this.getZIndex()-2);
15809         this.shim = shim;
15810         return shim;
15811     },
15812
15813     hideShim : function(){
15814         if(this.shim){
15815             this.shim.setDisplayed(false);
15816             shims.push(this.shim);
15817             delete this.shim;
15818         }
15819     },
15820
15821     disableShadow : function(){
15822         if(this.shadow){
15823             this.shadowDisabled = true;
15824             this.shadow.hide();
15825             this.lastShadowOffset = this.shadowOffset;
15826             this.shadowOffset = 0;
15827         }
15828     },
15829
15830     enableShadow : function(show){
15831         if(this.shadow){
15832             this.shadowDisabled = false;
15833             this.shadowOffset = this.lastShadowOffset;
15834             delete this.lastShadowOffset;
15835             if(show){
15836                 this.sync(true);
15837             }
15838         }
15839     },
15840
15841     // private
15842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
15843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
15844     sync : function(doShow){
15845         var sw = this.shadow;
15846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
15847             var sh = this.getShim();
15848
15849             var w = this.getWidth(),
15850                 h = this.getHeight();
15851
15852             var l = this.getLeft(true),
15853                 t = this.getTop(true);
15854
15855             if(sw && !this.shadowDisabled){
15856                 if(doShow && !sw.isVisible()){
15857                     sw.show(this);
15858                 }else{
15859                     sw.realign(l, t, w, h);
15860                 }
15861                 if(sh){
15862                     if(doShow){
15863                        sh.show();
15864                     }
15865                     // fit the shim behind the shadow, so it is shimmed too
15866                     var a = sw.adjusts, s = sh.dom.style;
15867                     s.left = (Math.min(l, l+a.l))+'px';
15868                     s.top = (Math.min(t, t+a.t))+'px';
15869                     s.width = (w+a.w)+'px';
15870                     s.height = (h+a.h)+'px';
15871                 }
15872             }else if(sh){
15873                 if(doShow){
15874                    sh.show();
15875                 }
15876                 sh.setSize(w, h);
15877                 sh.setLeftTop(l, t);
15878             }
15879
15880         }
15881     },
15882
15883     // private
15884     destroy : function(){
15885         this.hideShim();
15886         if(this.shadow){
15887             this.shadow.hide();
15888         }
15889         this.removeAllListeners();
15890         Ext.removeNode(this.dom);
15891         Ext.Element.uncache(this.id);
15892     },
15893
15894     remove : function(){
15895         this.destroy();
15896     },
15897
15898     // private
15899     beginUpdate : function(){
15900         this.updating = true;
15901     },
15902
15903     // private
15904     endUpdate : function(){
15905         this.updating = false;
15906         this.sync(true);
15907     },
15908
15909     // private
15910     hideUnders : function(negOffset){
15911         if(this.shadow){
15912             this.shadow.hide();
15913         }
15914         this.hideShim();
15915     },
15916
15917     // private
15918     constrainXY : function(){
15919         if(this.constrain){
15920             var vw = Ext.lib.Dom.getViewWidth(),
15921                 vh = Ext.lib.Dom.getViewHeight();
15922             var s = Ext.getDoc().getScroll();
15923
15924             var xy = this.getXY();
15925             var x = xy[0], y = xy[1];
15926             var so = this.shadowOffset;
15927             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
15928             // only move it if it needs it
15929             var moved = false;
15930             // first validate right/bottom
15931             if((x + w) > vw+s.left){
15932                 x = vw - w - so;
15933                 moved = true;
15934             }
15935             if((y + h) > vh+s.top){
15936                 y = vh - h - so;
15937                 moved = true;
15938             }
15939             // then make sure top/left isn't negative
15940             if(x < s.left){
15941                 x = s.left;
15942                 moved = true;
15943             }
15944             if(y < s.top){
15945                 y = s.top;
15946                 moved = true;
15947             }
15948             if(moved){
15949                 if(this.avoidY){
15950                     var ay = this.avoidY;
15951                     if(y <= ay && (y+h) >= ay){
15952                         y = ay-h-5;
15953                     }
15954                 }
15955                 xy = [x, y];
15956                 this.storeXY(xy);
15957                 supr.setXY.call(this, xy);
15958                 this.sync();
15959             }
15960         }
15961         return this;
15962     },
15963
15964     isVisible : function(){
15965         return this.visible;
15966     },
15967
15968     // private
15969     showAction : function(){
15970         this.visible = true; // track visibility to prevent getStyle calls
15971         if(this.useDisplay === true){
15972             this.setDisplayed('');
15973         }else if(this.lastXY){
15974             supr.setXY.call(this, this.lastXY);
15975         }else if(this.lastLT){
15976             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
15977         }
15978     },
15979
15980     // private
15981     hideAction : function(){
15982         this.visible = false;
15983         if(this.useDisplay === true){
15984             this.setDisplayed(false);
15985         }else{
15986             this.setLeftTop(-10000,-10000);
15987         }
15988     },
15989
15990     // overridden Element method
15991     setVisible : function(v, a, d, c, e){
15992         if(v){
15993             this.showAction();
15994         }
15995         if(a && v){
15996             var cb = function(){
15997                 this.sync(true);
15998                 if(c){
15999                     c();
16000                 }
16001             }.createDelegate(this);
16002             supr.setVisible.call(this, true, true, d, cb, e);
16003         }else{
16004             if(!v){
16005                 this.hideUnders(true);
16006             }
16007             var cb = c;
16008             if(a){
16009                 cb = function(){
16010                     this.hideAction();
16011                     if(c){
16012                         c();
16013                     }
16014                 }.createDelegate(this);
16015             }
16016             supr.setVisible.call(this, v, a, d, cb, e);
16017             if(v){
16018                 this.sync(true);
16019             }else if(!a){
16020                 this.hideAction();
16021             }
16022         }
16023         return this;
16024     },
16025
16026     storeXY : function(xy){
16027         delete this.lastLT;
16028         this.lastXY = xy;
16029     },
16030
16031     storeLeftTop : function(left, top){
16032         delete this.lastXY;
16033         this.lastLT = [left, top];
16034     },
16035
16036     // private
16037     beforeFx : function(){
16038         this.beforeAction();
16039         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
16040     },
16041
16042     // private
16043     afterFx : function(){
16044         Ext.Layer.superclass.afterFx.apply(this, arguments);
16045         this.sync(this.isVisible());
16046     },
16047
16048     // private
16049     beforeAction : function(){
16050         if(!this.updating && this.shadow){
16051             this.shadow.hide();
16052         }
16053     },
16054
16055     // overridden Element method
16056     setLeft : function(left){
16057         this.storeLeftTop(left, this.getTop(true));
16058         supr.setLeft.apply(this, arguments);
16059         this.sync();
16060         return this;
16061     },
16062
16063     setTop : function(top){
16064         this.storeLeftTop(this.getLeft(true), top);
16065         supr.setTop.apply(this, arguments);
16066         this.sync();
16067         return this;
16068     },
16069
16070     setLeftTop : function(left, top){
16071         this.storeLeftTop(left, top);
16072         supr.setLeftTop.apply(this, arguments);
16073         this.sync();
16074         return this;
16075     },
16076
16077     setXY : function(xy, a, d, c, e){
16078         this.fixDisplay();
16079         this.beforeAction();
16080         this.storeXY(xy);
16081         var cb = this.createCB(c);
16082         supr.setXY.call(this, xy, a, d, cb, e);
16083         if(!a){
16084             cb();
16085         }
16086         return this;
16087     },
16088
16089     // private
16090     createCB : function(c){
16091         var el = this;
16092         return function(){
16093             el.constrainXY();
16094             el.sync(true);
16095             if(c){
16096                 c();
16097             }
16098         };
16099     },
16100
16101     // overridden Element method
16102     setX : function(x, a, d, c, e){
16103         this.setXY([x, this.getY()], a, d, c, e);
16104         return this;
16105     },
16106
16107     // overridden Element method
16108     setY : function(y, a, d, c, e){
16109         this.setXY([this.getX(), y], a, d, c, e);
16110         return this;
16111     },
16112
16113     // overridden Element method
16114     setSize : function(w, h, a, d, c, e){
16115         this.beforeAction();
16116         var cb = this.createCB(c);
16117         supr.setSize.call(this, w, h, a, d, cb, e);
16118         if(!a){
16119             cb();
16120         }
16121         return this;
16122     },
16123
16124     // overridden Element method
16125     setWidth : function(w, a, d, c, e){
16126         this.beforeAction();
16127         var cb = this.createCB(c);
16128         supr.setWidth.call(this, w, a, d, cb, e);
16129         if(!a){
16130             cb();
16131         }
16132         return this;
16133     },
16134
16135     // overridden Element method
16136     setHeight : function(h, a, d, c, e){
16137         this.beforeAction();
16138         var cb = this.createCB(c);
16139         supr.setHeight.call(this, h, a, d, cb, e);
16140         if(!a){
16141             cb();
16142         }
16143         return this;
16144     },
16145
16146     // overridden Element method
16147     setBounds : function(x, y, w, h, a, d, c, e){
16148         this.beforeAction();
16149         var cb = this.createCB(c);
16150         if(!a){
16151             this.storeXY([x, y]);
16152             supr.setXY.call(this, [x, y]);
16153             supr.setSize.call(this, w, h, a, d, cb, e);
16154             cb();
16155         }else{
16156             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16157         }
16158         return this;
16159     },
16160
16161     /**
16162      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16163      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16164      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16165      * @param {Number} zindex The new z-index to set
16166      * @return {this} The Layer
16167      */
16168     setZIndex : function(zindex){
16169         this.zindex = zindex;
16170         this.setStyle('z-index', zindex + 2);
16171         if(this.shadow){
16172             this.shadow.setZIndex(zindex + 1);
16173         }
16174         if(this.shim){
16175             this.shim.setStyle('z-index', zindex);
16176         }
16177         return this;
16178     }
16179 });
16180 })();/**
16181  * @class Ext.Shadow
16182  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
16183  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
16184  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
16185  * @constructor
16186  * Create a new Shadow
16187  * @param {Object} config The config object
16188  */
16189 Ext.Shadow = function(config){
16190     Ext.apply(this, config);
16191     if(typeof this.mode != "string"){
16192         this.mode = this.defaultMode;
16193     }
16194     var o = this.offset, a = {h: 0};
16195     var rad = Math.floor(this.offset/2);
16196     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
16197         case "drop":
16198             a.w = 0;
16199             a.l = a.t = o;
16200             a.t -= 1;
16201             if(Ext.isIE){
16202                 a.l -= this.offset + rad;
16203                 a.t -= this.offset + rad;
16204                 a.w -= rad;
16205                 a.h -= rad;
16206                 a.t += 1;
16207             }
16208         break;
16209         case "sides":
16210             a.w = (o*2);
16211             a.l = -o;
16212             a.t = o-1;
16213             if(Ext.isIE){
16214                 a.l -= (this.offset - rad);
16215                 a.t -= this.offset + rad;
16216                 a.l += 1;
16217                 a.w -= (this.offset - rad)*2;
16218                 a.w -= rad + 1;
16219                 a.h -= 1;
16220             }
16221         break;
16222         case "frame":
16223             a.w = a.h = (o*2);
16224             a.l = a.t = -o;
16225             a.t += 1;
16226             a.h -= 2;
16227             if(Ext.isIE){
16228                 a.l -= (this.offset - rad);
16229                 a.t -= (this.offset - rad);
16230                 a.l += 1;
16231                 a.w -= (this.offset + rad + 1);
16232                 a.h -= (this.offset + rad);
16233                 a.h += 1;
16234             }
16235         break;
16236     };
16237
16238     this.adjusts = a;
16239 };
16240
16241 Ext.Shadow.prototype = {
16242     /**
16243      * @cfg {String} mode
16244      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
16245      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
16246      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
16247      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
16248      * </ul></div>
16249      */
16250     /**
16251      * @cfg {String} offset
16252      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
16253      */
16254     offset: 4,
16255
16256     // private
16257     defaultMode: "drop",
16258
16259     /**
16260      * Displays the shadow under the target element
16261      * @param {Mixed} targetEl The id or element under which the shadow should display
16262      */
16263     show : function(target){
16264         target = Ext.get(target);
16265         if(!this.el){
16266             this.el = Ext.Shadow.Pool.pull();
16267             if(this.el.dom.nextSibling != target.dom){
16268                 this.el.insertBefore(target);
16269             }
16270         }
16271         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
16272         if(Ext.isIE){
16273             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
16274         }
16275         this.realign(
16276             target.getLeft(true),
16277             target.getTop(true),
16278             target.getWidth(),
16279             target.getHeight()
16280         );
16281         this.el.dom.style.display = "block";
16282     },
16283
16284     /**
16285      * Returns true if the shadow is visible, else false
16286      */
16287     isVisible : function(){
16288         return this.el ? true : false;  
16289     },
16290
16291     /**
16292      * Direct alignment when values are already available. Show must be called at least once before
16293      * calling this method to ensure it is initialized.
16294      * @param {Number} left The target element left position
16295      * @param {Number} top The target element top position
16296      * @param {Number} width The target element width
16297      * @param {Number} height The target element height
16298      */
16299     realign : function(l, t, w, h){
16300         if(!this.el){
16301             return;
16302         }
16303         var a = this.adjusts, d = this.el.dom, s = d.style;
16304         var iea = 0;
16305         s.left = (l+a.l)+"px";
16306         s.top = (t+a.t)+"px";
16307         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
16308         if(s.width != sws || s.height != shs){
16309             s.width = sws;
16310             s.height = shs;
16311             if(!Ext.isIE){
16312                 var cn = d.childNodes;
16313                 var sww = Math.max(0, (sw-12))+"px";
16314                 cn[0].childNodes[1].style.width = sww;
16315                 cn[1].childNodes[1].style.width = sww;
16316                 cn[2].childNodes[1].style.width = sww;
16317                 cn[1].style.height = Math.max(0, (sh-12))+"px";
16318             }
16319         }
16320     },
16321
16322     /**
16323      * Hides this shadow
16324      */
16325     hide : function(){
16326         if(this.el){
16327             this.el.dom.style.display = "none";
16328             Ext.Shadow.Pool.push(this.el);
16329             delete this.el;
16330         }
16331     },
16332
16333     /**
16334      * Adjust the z-index of this shadow
16335      * @param {Number} zindex The new z-index
16336      */
16337     setZIndex : function(z){
16338         this.zIndex = z;
16339         if(this.el){
16340             this.el.setStyle("z-index", z);
16341         }
16342     }
16343 };
16344
16345 // Private utility class that manages the internal Shadow cache
16346 Ext.Shadow.Pool = function(){
16347     var p = [];
16348     var markup = Ext.isIE ?
16349                  '<div class="x-ie-shadow"></div>' :
16350                  '<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>';
16351     return {
16352         pull : function(){
16353             var sh = p.shift();
16354             if(!sh){
16355                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
16356                 sh.autoBoxAdjust = false;
16357             }
16358             return sh;
16359         },
16360
16361         push : function(sh){
16362             p.push(sh);
16363         }
16364     };
16365 }();/**
16366  * @class Ext.BoxComponent
16367  * @extends Ext.Component
16368  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
16369  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
16370  * within the Component rendering model.</p>
16371  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
16372  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
16373  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
16374  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
16375  * element to reference:<pre><code>
16376 var pageHeader = new Ext.BoxComponent({
16377     el: 'my-header-div'
16378 });</code></pre>
16379  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
16380  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
16381  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
16382  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
16383 var myImage = new Ext.BoxComponent({
16384     autoEl: {
16385         tag: 'img',
16386         src: '/images/my-image.jpg'
16387     }
16388 });</code></pre></p>
16389  * @constructor
16390  * @param {Ext.Element/String/Object} config The configuration options.
16391  * @xtype box
16392  */
16393 Ext.BoxComponent = Ext.extend(Ext.Component, {
16394
16395     // tabTip config is used when a BoxComponent is a child of a TabPanel
16396     /**
16397      * @cfg {String} tabTip
16398      * <p><b>Note</b>: this config is only used when this BoxComponent is a child item of a TabPanel.</p>
16399      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
16400      * the associated tab selector element. {@link Ext.QuickTips}.init()
16401      * must be called in order for the tips to render.
16402      */
16403     // Configs below are used for all Components when rendered by BorderLayout.
16404     /**
16405      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
16406      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
16407      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
16408      * <p>See {@link Ext.layout.BorderLayout} also.</p>
16409      */
16410     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
16411     /**
16412      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
16413      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
16414      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
16415      * <p>An object containing margins to apply to this BoxComponent in the
16416      * format:</p><pre><code>
16417 {
16418     top: (top margin),
16419     right: (right margin),
16420     bottom: (bottom margin),
16421     left: (left margin)
16422 }</code></pre>
16423      * <p>May also be a string containing space-separated, numeric margin values. The order of the
16424      * sides associated with each value matches the way CSS processes margin values:</p>
16425      * <p><div class="mdetail-params"><ul>
16426      * <li>If there is only one value, it applies to all sides.</li>
16427      * <li>If there are two values, the top and bottom borders are set to the first value and the
16428      * right and left are set to the second.</li>
16429      * <li>If there are three values, the top is set to the first value, the left and right are set
16430      * to the second, and the bottom is set to the third.</li>
16431      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
16432      * </ul></div></p>
16433      * <p>Defaults to:</p><pre><code>
16434      * {top:0, right:0, bottom:0, left:0}
16435      * </code></pre>
16436      */
16437     /**
16438      * @cfg {Number} x
16439      * The local x (left) coordinate for this component if contained within a positioning container.
16440      */
16441     /**
16442      * @cfg {Number} y
16443      * The local y (top) coordinate for this component if contained within a positioning container.
16444      */
16445     /**
16446      * @cfg {Number} pageX
16447      * The page level x coordinate for this component if contained within a positioning container.
16448      */
16449     /**
16450      * @cfg {Number} pageY
16451      * The page level y coordinate for this component if contained within a positioning container.
16452      */
16453     /**
16454      * @cfg {Number} height
16455      * The height of this component in pixels (defaults to auto).
16456      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
16457      */
16458     /**
16459      * @cfg {Number} width
16460      * The width of this component in pixels (defaults to auto).
16461      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
16462      */
16463     /**
16464      * @cfg {Boolean} autoHeight
16465      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
16466      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
16467      * <p><b>Note</b>: Although many components inherit this config option, not all will
16468      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
16469      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
16470      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
16471      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
16472      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
16473 var w = new Ext.Window({
16474     title: 'Window',
16475     width: 600,
16476     autoHeight: true,
16477     items: {
16478         title: 'Collapse Me',
16479         height: 400,
16480         collapsible: true,
16481         border: false,
16482         listeners: {
16483             beforecollapse: function() {
16484                 w.el.shadow.hide();
16485             },
16486             beforeexpand: function() {
16487                 w.el.shadow.hide();
16488             },
16489             collapse: function() {
16490                 w.syncShadow();
16491             },
16492             expand: function() {
16493                 w.syncShadow();
16494             }
16495         }
16496     }
16497 }).show();
16498 </code></pre>
16499      */
16500     /**
16501      * @cfg {Boolean} autoWidth
16502      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
16503      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
16504      * <p><b>Note</b>: Although many components  inherit this config option, not all will
16505      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
16506      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
16507      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
16508      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
16509      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
16510 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
16511 </code></pre>
16512      * A Panel rendered into that target element must listen for browser window resize in order to relay its
16513       * child items when the browser changes its width:<pre><code>
16514 var myPanel = new Ext.Panel({
16515     renderTo: 'grid-container',
16516     monitorResize: true, // relay on browser resize
16517     title: 'Panel',
16518     height: 400,
16519     autoWidth: true,
16520     layout: 'hbox',
16521     layoutConfig: {
16522         align: 'stretch'
16523     },
16524     defaults: {
16525         flex: 1
16526     },
16527     items: [{
16528         title: 'Box 1',
16529     }, {
16530         title: 'Box 2'
16531     }, {
16532         title: 'Box 3'
16533     }],
16534 });
16535 </code></pre>
16536      */
16537
16538     /* // private internal config
16539      * {Boolean} deferHeight
16540      * True to defer height calculations to an external component, false to allow this component to set its own
16541      * height (defaults to false).
16542      */
16543
16544     // private
16545     initComponent : function(){
16546         Ext.BoxComponent.superclass.initComponent.call(this);
16547         this.addEvents(
16548             /**
16549              * @event resize
16550              * Fires after the component is resized.
16551              * @param {Ext.Component} this
16552              * @param {Number} adjWidth The box-adjusted width that was set
16553              * @param {Number} adjHeight The box-adjusted height that was set
16554              * @param {Number} rawWidth The width that was originally specified
16555              * @param {Number} rawHeight The height that was originally specified
16556              */
16557             'resize',
16558             /**
16559              * @event move
16560              * Fires after the component is moved.
16561              * @param {Ext.Component} this
16562              * @param {Number} x The new x position
16563              * @param {Number} y The new y position
16564              */
16565             'move'
16566         );
16567     },
16568
16569     // private, set in afterRender to signify that the component has been rendered
16570     boxReady : false,
16571     // private, used to defer height settings to subclasses
16572     deferHeight: false,
16573
16574     /**
16575      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
16576      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
16577      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
16578      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16579      * <li>A String used to set the CSS width style.</li>
16580      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
16581      * <li><code>undefined</code> to leave the width unchanged.</li>
16582      * </ul></div>
16583      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
16584      * This may be one of:<div class="mdetail-params"><ul>
16585      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16586      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
16587      * <li><code>undefined</code> to leave the height unchanged.</li>
16588      * </ul></div>
16589      * @return {Ext.BoxComponent} this
16590      */
16591     setSize : function(w, h){
16592         // support for standard size objects
16593         if(typeof w == 'object'){
16594             h = w.height;
16595             w = w.width;
16596         }
16597         // not rendered
16598         if(!this.boxReady){
16599             this.width = w;
16600             this.height = h;
16601             return this;
16602         }
16603
16604         // prevent recalcs when not needed
16605         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16606             return this;
16607         }
16608         this.lastSize = {width: w, height: h};
16609         var adj = this.adjustSize(w, h);
16610         var aw = adj.width, ah = adj.height;
16611         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16612             var rz = this.getResizeEl();
16613             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16614                 rz.setSize(aw, ah);
16615             }else if(!this.deferHeight && ah !== undefined){
16616                 rz.setHeight(ah);
16617             }else if(aw !== undefined){
16618                 rz.setWidth(aw);
16619             }
16620             this.onResize(aw, ah, w, h);
16621             this.fireEvent('resize', this, aw, ah, w, h);
16622         }
16623         return this;
16624     },
16625
16626     /**
16627      * Sets the width of the component.  This method fires the {@link #resize} event.
16628      * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
16629      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16630      * <li>A String used to set the CSS width style.</li>
16631      * </ul></div>
16632      * @return {Ext.BoxComponent} this
16633      */
16634     setWidth : function(width){
16635         return this.setSize(width);
16636     },
16637
16638     /**
16639      * Sets the height of the component.  This method fires the {@link #resize} event.
16640      * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
16641      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
16642      * <li>A String used to set the CSS height style.</li>
16643      * <li><i>undefined</i> to leave the height unchanged.</li>
16644      * </ul></div>
16645      * @return {Ext.BoxComponent} this
16646      */
16647     setHeight : function(height){
16648         return this.setSize(undefined, height);
16649     },
16650
16651     /**
16652      * Gets the current size of the component's underlying element.
16653      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16654      */
16655     getSize : function(){
16656         return this.getResizeEl().getSize();
16657     },
16658
16659     /**
16660      * Gets the current width of the component's underlying element.
16661      * @return {Number}
16662      */
16663     getWidth : function(){
16664         return this.getResizeEl().getWidth();
16665     },
16666
16667     /**
16668      * Gets the current height of the component's underlying element.
16669      * @return {Number}
16670      */
16671     getHeight : function(){
16672         return this.getResizeEl().getHeight();
16673     },
16674
16675     /**
16676      * Gets the current size of the component's underlying element, including space taken by its margins.
16677      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
16678      */
16679     getOuterSize : function(){
16680         var el = this.getResizeEl();
16681         return {width: el.getWidth() + el.getMargins('lr'),
16682                 height: el.getHeight() + el.getMargins('tb')};
16683     },
16684
16685     /**
16686      * Gets the current XY position of the component's underlying element.
16687      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16688      * @return {Array} The XY position of the element (e.g., [100, 200])
16689      */
16690     getPosition : function(local){
16691         var el = this.getPositionEl();
16692         if(local === true){
16693             return [el.getLeft(true), el.getTop(true)];
16694         }
16695         return this.xy || el.getXY();
16696     },
16697
16698     /**
16699      * Gets the current box measurements of the component's underlying element.
16700      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16701      * @return {Object} box An object in the format {x, y, width, height}
16702      */
16703     getBox : function(local){
16704         var pos = this.getPosition(local);
16705         var s = this.getSize();
16706         s.x = pos[0];
16707         s.y = pos[1];
16708         return s;
16709     },
16710
16711     /**
16712      * Sets the current box measurements of the component's underlying element.
16713      * @param {Object} box An object in the format {x, y, width, height}
16714      * @return {Ext.BoxComponent} this
16715      */
16716     updateBox : function(box){
16717         this.setSize(box.width, box.height);
16718         this.setPagePosition(box.x, box.y);
16719         return this;
16720     },
16721
16722     /**
16723      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
16724      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
16725      * but in some cases, a Component may have some more wrapping Elements around its main
16726      * active Element.</p>
16727      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
16728      * contains both the <code>&lt;input></code> Element (which is what would be returned
16729      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
16730      * This Element is returned as the <code>resizeEl</code>.
16731      */
16732     getResizeEl : function(){
16733         return this.resizeEl || this.el;
16734     },
16735
16736     // protected
16737     getPositionEl : function(){
16738         return this.positionEl || this.el;
16739     },
16740
16741     /**
16742      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16743      * This method fires the {@link #move} event.
16744      * @param {Number} left The new left
16745      * @param {Number} top The new top
16746      * @return {Ext.BoxComponent} this
16747      */
16748     setPosition : function(x, y){
16749         if(x && typeof x[1] == 'number'){
16750             y = x[1];
16751             x = x[0];
16752         }
16753         this.x = x;
16754         this.y = y;
16755         if(!this.boxReady){
16756             return this;
16757         }
16758         var adj = this.adjustPosition(x, y);
16759         var ax = adj.x, ay = adj.y;
16760
16761         var el = this.getPositionEl();
16762         if(ax !== undefined || ay !== undefined){
16763             if(ax !== undefined && ay !== undefined){
16764                 el.setLeftTop(ax, ay);
16765             }else if(ax !== undefined){
16766                 el.setLeft(ax);
16767             }else if(ay !== undefined){
16768                 el.setTop(ay);
16769             }
16770             this.onPosition(ax, ay);
16771             this.fireEvent('move', this, ax, ay);
16772         }
16773         return this;
16774     },
16775
16776     /**
16777      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16778      * This method fires the {@link #move} event.
16779      * @param {Number} x The new x position
16780      * @param {Number} y The new y position
16781      * @return {Ext.BoxComponent} this
16782      */
16783     setPagePosition : function(x, y){
16784         if(x && typeof x[1] == 'number'){
16785             y = x[1];
16786             x = x[0];
16787         }
16788         this.pageX = x;
16789         this.pageY = y;
16790         if(!this.boxReady){
16791             return;
16792         }
16793         if(x === undefined || y === undefined){ // cannot translate undefined points
16794             return;
16795         }
16796         var p = this.getPositionEl().translatePoints(x, y);
16797         this.setPosition(p.left, p.top);
16798         return this;
16799     },
16800
16801     // private
16802     afterRender : function(){
16803         Ext.BoxComponent.superclass.afterRender.call(this);
16804         if(this.resizeEl){
16805             this.resizeEl = Ext.get(this.resizeEl);
16806         }
16807         if(this.positionEl){
16808             this.positionEl = Ext.get(this.positionEl);
16809         }
16810         this.boxReady = true;
16811         this.setSize(this.width, this.height);
16812         if(this.x || this.y){
16813             this.setPosition(this.x, this.y);
16814         }else if(this.pageX || this.pageY){
16815             this.setPagePosition(this.pageX, this.pageY);
16816         }
16817     },
16818
16819     /**
16820      * Force the component's size to recalculate based on the underlying element's current height and width.
16821      * @return {Ext.BoxComponent} this
16822      */
16823     syncSize : function(){
16824         delete this.lastSize;
16825         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
16826         return this;
16827     },
16828
16829     /* // protected
16830      * Called after the component is resized, this method is empty by default but can be implemented by any
16831      * subclass that needs to perform custom logic after a resize occurs.
16832      * @param {Number} adjWidth The box-adjusted width that was set
16833      * @param {Number} adjHeight The box-adjusted height that was set
16834      * @param {Number} rawWidth The width that was originally specified
16835      * @param {Number} rawHeight The height that was originally specified
16836      */
16837     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16838
16839     },
16840
16841     /* // protected
16842      * Called after the component is moved, this method is empty by default but can be implemented by any
16843      * subclass that needs to perform custom logic after a move occurs.
16844      * @param {Number} x The new x position
16845      * @param {Number} y The new y position
16846      */
16847     onPosition : function(x, y){
16848
16849     },
16850
16851     // private
16852     adjustSize : function(w, h){
16853         if(this.autoWidth){
16854             w = 'auto';
16855         }
16856         if(this.autoHeight){
16857             h = 'auto';
16858         }
16859         return {width : w, height: h};
16860     },
16861
16862     // private
16863     adjustPosition : function(x, y){
16864         return {x : x, y: y};
16865     }
16866 });
16867 Ext.reg('box', Ext.BoxComponent);
16868
16869
16870 /**
16871  * @class Ext.Spacer
16872  * @extends Ext.BoxComponent
16873  * <p>Used to provide a sizable space in a layout.</p>
16874  * @constructor
16875  * @param {Object} config
16876  */
16877 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
16878     autoEl:'div'
16879 });
16880 Ext.reg('spacer', Ext.Spacer);/**\r
16881  * @class Ext.SplitBar\r
16882  * @extends Ext.util.Observable\r
16883  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).\r
16884  * <br><br>\r
16885  * Usage:\r
16886  * <pre><code>\r
16887 var split = new Ext.SplitBar("elementToDrag", "elementToSize",\r
16888                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);\r
16889 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));\r
16890 split.minSize = 100;\r
16891 split.maxSize = 600;\r
16892 split.animate = true;\r
16893 split.on('moved', splitterMoved);\r
16894 </code></pre>\r
16895  * @constructor\r
16896  * Create a new SplitBar\r
16897  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.\r
16898  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged\r
16899  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
16900  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or  \r
16901                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial\r
16902                         position of the SplitBar).\r
16903  */\r
16904 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){\r
16905     \r
16906     /** @private */\r
16907     this.el = Ext.get(dragElement, true);\r
16908     this.el.dom.unselectable = "on";\r
16909     /** @private */\r
16910     this.resizingEl = Ext.get(resizingElement, true);\r
16911 \r
16912     /**\r
16913      * @private\r
16914      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
16915      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated\r
16916      * @type Number\r
16917      */\r
16918     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;\r
16919     \r
16920     /**\r
16921      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.\r
16922      * @type Number\r
16923      * @property tickSize\r
16924      */\r
16925     /**\r
16926      * The minimum size of the resizing element. (Defaults to 0)\r
16927      * @type Number\r
16928      */\r
16929     this.minSize = 0;\r
16930     \r
16931     /**\r
16932      * The maximum size of the resizing element. (Defaults to 2000)\r
16933      * @type Number\r
16934      */\r
16935     this.maxSize = 2000;\r
16936     \r
16937     /**\r
16938      * Whether to animate the transition to the new size\r
16939      * @type Boolean\r
16940      */\r
16941     this.animate = false;\r
16942     \r
16943     /**\r
16944      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.\r
16945      * @type Boolean\r
16946      */\r
16947     this.useShim = false;\r
16948     \r
16949     /** @private */\r
16950     this.shim = null;\r
16951     \r
16952     if(!existingProxy){\r
16953         /** @private */\r
16954         this.proxy = Ext.SplitBar.createProxy(this.orientation);\r
16955     }else{\r
16956         this.proxy = Ext.get(existingProxy).dom;\r
16957     }\r
16958     /** @private */\r
16959     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});\r
16960     \r
16961     /** @private */\r
16962     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);\r
16963     \r
16964     /** @private */\r
16965     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);\r
16966     \r
16967     /** @private */\r
16968     this.dragSpecs = {};\r
16969     \r
16970     /**\r
16971      * @private The adapter to use to positon and resize elements\r
16972      */\r
16973     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();\r
16974     this.adapter.init(this);\r
16975     \r
16976     if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
16977         /** @private */\r
16978         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);\r
16979         this.el.addClass("x-splitbar-h");\r
16980     }else{\r
16981         /** @private */\r
16982         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);\r
16983         this.el.addClass("x-splitbar-v");\r
16984     }\r
16985     \r
16986     this.addEvents(\r
16987         /**\r
16988          * @event resize\r
16989          * Fires when the splitter is moved (alias for {@link #moved})\r
16990          * @param {Ext.SplitBar} this\r
16991          * @param {Number} newSize the new width or height\r
16992          */\r
16993         "resize",\r
16994         /**\r
16995          * @event moved\r
16996          * Fires when the splitter is moved\r
16997          * @param {Ext.SplitBar} this\r
16998          * @param {Number} newSize the new width or height\r
16999          */\r
17000         "moved",\r
17001         /**\r
17002          * @event beforeresize\r
17003          * Fires before the splitter is dragged\r
17004          * @param {Ext.SplitBar} this\r
17005          */\r
17006         "beforeresize",\r
17007 \r
17008         "beforeapply"\r
17009     );\r
17010 \r
17011     Ext.SplitBar.superclass.constructor.call(this);\r
17012 };\r
17013 \r
17014 Ext.extend(Ext.SplitBar, Ext.util.Observable, {\r
17015     onStartProxyDrag : function(x, y){\r
17016         this.fireEvent("beforeresize", this);\r
17017         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);\r
17018         this.overlay.unselectable();\r
17019         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
17020         this.overlay.show();\r
17021         Ext.get(this.proxy).setDisplayed("block");\r
17022         var size = this.adapter.getElementSize(this);\r
17023         this.activeMinSize = this.getMinimumSize();\r
17024         this.activeMaxSize = this.getMaximumSize();\r
17025         var c1 = size - this.activeMinSize;\r
17026         var c2 = Math.max(this.activeMaxSize - size, 0);\r
17027         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
17028             this.dd.resetConstraints();\r
17029             this.dd.setXConstraint(\r
17030                 this.placement == Ext.SplitBar.LEFT ? c1 : c2, \r
17031                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,\r
17032                 this.tickSize\r
17033             );\r
17034             this.dd.setYConstraint(0, 0);\r
17035         }else{\r
17036             this.dd.resetConstraints();\r
17037             this.dd.setXConstraint(0, 0);\r
17038             this.dd.setYConstraint(\r
17039                 this.placement == Ext.SplitBar.TOP ? c1 : c2, \r
17040                 this.placement == Ext.SplitBar.TOP ? c2 : c1,\r
17041                 this.tickSize\r
17042             );\r
17043          }\r
17044         this.dragSpecs.startSize = size;\r
17045         this.dragSpecs.startPoint = [x, y];\r
17046         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);\r
17047     },\r
17048     \r
17049     /** \r
17050      * @private Called after the drag operation by the DDProxy\r
17051      */\r
17052     onEndProxyDrag : function(e){\r
17053         Ext.get(this.proxy).setDisplayed(false);\r
17054         var endPoint = Ext.lib.Event.getXY(e);\r
17055         if(this.overlay){\r
17056             Ext.destroy(this.overlay);\r
17057             delete this.overlay;\r
17058         }\r
17059         var newSize;\r
17060         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
17061             newSize = this.dragSpecs.startSize + \r
17062                 (this.placement == Ext.SplitBar.LEFT ?\r
17063                     endPoint[0] - this.dragSpecs.startPoint[0] :\r
17064                     this.dragSpecs.startPoint[0] - endPoint[0]\r
17065                 );\r
17066         }else{\r
17067             newSize = this.dragSpecs.startSize + \r
17068                 (this.placement == Ext.SplitBar.TOP ?\r
17069                     endPoint[1] - this.dragSpecs.startPoint[1] :\r
17070                     this.dragSpecs.startPoint[1] - endPoint[1]\r
17071                 );\r
17072         }\r
17073         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);\r
17074         if(newSize != this.dragSpecs.startSize){\r
17075             if(this.fireEvent('beforeapply', this, newSize) !== false){\r
17076                 this.adapter.setElementSize(this, newSize);\r
17077                 this.fireEvent("moved", this, newSize);\r
17078                 this.fireEvent("resize", this, newSize);\r
17079             }\r
17080         }\r
17081     },\r
17082     \r
17083     /**\r
17084      * Get the adapter this SplitBar uses\r
17085      * @return The adapter object\r
17086      */\r
17087     getAdapter : function(){\r
17088         return this.adapter;\r
17089     },\r
17090     \r
17091     /**\r
17092      * Set the adapter this SplitBar uses\r
17093      * @param {Object} adapter A SplitBar adapter object\r
17094      */\r
17095     setAdapter : function(adapter){\r
17096         this.adapter = adapter;\r
17097         this.adapter.init(this);\r
17098     },\r
17099     \r
17100     /**\r
17101      * Gets the minimum size for the resizing element\r
17102      * @return {Number} The minimum size\r
17103      */\r
17104     getMinimumSize : function(){\r
17105         return this.minSize;\r
17106     },\r
17107     \r
17108     /**\r
17109      * Sets the minimum size for the resizing element\r
17110      * @param {Number} minSize The minimum size\r
17111      */\r
17112     setMinimumSize : function(minSize){\r
17113         this.minSize = minSize;\r
17114     },\r
17115     \r
17116     /**\r
17117      * Gets the maximum size for the resizing element\r
17118      * @return {Number} The maximum size\r
17119      */\r
17120     getMaximumSize : function(){\r
17121         return this.maxSize;\r
17122     },\r
17123     \r
17124     /**\r
17125      * Sets the maximum size for the resizing element\r
17126      * @param {Number} maxSize The maximum size\r
17127      */\r
17128     setMaximumSize : function(maxSize){\r
17129         this.maxSize = maxSize;\r
17130     },\r
17131     \r
17132     /**\r
17133      * Sets the initialize size for the resizing element\r
17134      * @param {Number} size The initial size\r
17135      */\r
17136     setCurrentSize : function(size){\r
17137         var oldAnimate = this.animate;\r
17138         this.animate = false;\r
17139         this.adapter.setElementSize(this, size);\r
17140         this.animate = oldAnimate;\r
17141     },\r
17142     \r
17143     /**\r
17144      * Destroy this splitbar. \r
17145      * @param {Boolean} removeEl True to remove the element\r
17146      */\r
17147     destroy : function(removeEl){\r
17148                 Ext.destroy(this.shim, Ext.get(this.proxy));\r
17149         this.dd.unreg();\r
17150         if(removeEl){\r
17151             this.el.remove();\r
17152         }\r
17153                 this.purgeListeners();\r
17154     }\r
17155 });\r
17156 \r
17157 /**\r
17158  * @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
17159  */\r
17160 Ext.SplitBar.createProxy = function(dir){\r
17161     var proxy = new Ext.Element(document.createElement("div"));\r
17162     proxy.unselectable();\r
17163     var cls = 'x-splitbar-proxy';\r
17164     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));\r
17165     document.body.appendChild(proxy.dom);\r
17166     return proxy.dom;\r
17167 };\r
17168 \r
17169 /** \r
17170  * @class Ext.SplitBar.BasicLayoutAdapter\r
17171  * Default Adapter. It assumes the splitter and resizing element are not positioned\r
17172  * elements and only gets/sets the width of the element. Generally used for table based layouts.\r
17173  */\r
17174 Ext.SplitBar.BasicLayoutAdapter = function(){\r
17175 };\r
17176 \r
17177 Ext.SplitBar.BasicLayoutAdapter.prototype = {\r
17178     // do nothing for now\r
17179     init : function(s){\r
17180     \r
17181     },\r
17182     /**\r
17183      * Called before drag operations to get the current size of the resizing element. \r
17184      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
17185      */\r
17186      getElementSize : function(s){\r
17187         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
17188             return s.resizingEl.getWidth();\r
17189         }else{\r
17190             return s.resizingEl.getHeight();\r
17191         }\r
17192     },\r
17193     \r
17194     /**\r
17195      * Called after drag operations to set the size of the resizing element.\r
17196      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
17197      * @param {Number} newSize The new size to set\r
17198      * @param {Function} onComplete A function to be invoked when resizing is complete\r
17199      */\r
17200     setElementSize : function(s, newSize, onComplete){\r
17201         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
17202             if(!s.animate){\r
17203                 s.resizingEl.setWidth(newSize);\r
17204                 if(onComplete){\r
17205                     onComplete(s, newSize);\r
17206                 }\r
17207             }else{\r
17208                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');\r
17209             }\r
17210         }else{\r
17211             \r
17212             if(!s.animate){\r
17213                 s.resizingEl.setHeight(newSize);\r
17214                 if(onComplete){\r
17215                     onComplete(s, newSize);\r
17216                 }\r
17217             }else{\r
17218                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');\r
17219             }\r
17220         }\r
17221     }\r
17222 };\r
17223 \r
17224 /** \r
17225  *@class Ext.SplitBar.AbsoluteLayoutAdapter\r
17226  * @extends Ext.SplitBar.BasicLayoutAdapter\r
17227  * Adapter that  moves the splitter element to align with the resized sizing element. \r
17228  * Used with an absolute positioned SplitBar.\r
17229  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's\r
17230  * document.body, make sure you assign an id to the body element.\r
17231  */\r
17232 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){\r
17233     this.basic = new Ext.SplitBar.BasicLayoutAdapter();\r
17234     this.container = Ext.get(container);\r
17235 };\r
17236 \r
17237 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {\r
17238     init : function(s){\r
17239         this.basic.init(s);\r
17240     },\r
17241     \r
17242     getElementSize : function(s){\r
17243         return this.basic.getElementSize(s);\r
17244     },\r
17245     \r
17246     setElementSize : function(s, newSize, onComplete){\r
17247         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));\r
17248     },\r
17249     \r
17250     moveSplitter : function(s){\r
17251         var yes = Ext.SplitBar;\r
17252         switch(s.placement){\r
17253             case yes.LEFT:\r
17254                 s.el.setX(s.resizingEl.getRight());\r
17255                 break;\r
17256             case yes.RIGHT:\r
17257                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");\r
17258                 break;\r
17259             case yes.TOP:\r
17260                 s.el.setY(s.resizingEl.getBottom());\r
17261                 break;\r
17262             case yes.BOTTOM:\r
17263                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());\r
17264                 break;\r
17265         }\r
17266     }\r
17267 };\r
17268 \r
17269 /**\r
17270  * Orientation constant - Create a vertical SplitBar\r
17271  * @static\r
17272  * @type Number\r
17273  */\r
17274 Ext.SplitBar.VERTICAL = 1;\r
17275 \r
17276 /**\r
17277  * Orientation constant - Create a horizontal SplitBar\r
17278  * @static\r
17279  * @type Number\r
17280  */\r
17281 Ext.SplitBar.HORIZONTAL = 2;\r
17282 \r
17283 /**\r
17284  * Placement constant - The resizing element is to the left of the splitter element\r
17285  * @static\r
17286  * @type Number\r
17287  */\r
17288 Ext.SplitBar.LEFT = 1;\r
17289 \r
17290 /**\r
17291  * Placement constant - The resizing element is to the right of the splitter element\r
17292  * @static\r
17293  * @type Number\r
17294  */\r
17295 Ext.SplitBar.RIGHT = 2;\r
17296 \r
17297 /**\r
17298  * Placement constant - The resizing element is positioned above the splitter element\r
17299  * @static\r
17300  * @type Number\r
17301  */\r
17302 Ext.SplitBar.TOP = 3;\r
17303 \r
17304 /**\r
17305  * Placement constant - The resizing element is positioned under splitter element\r
17306  * @static\r
17307  * @type Number\r
17308  */\r
17309 Ext.SplitBar.BOTTOM = 4;\r
17310 /**\r
17311  * @class Ext.Container\r
17312  * @extends Ext.BoxComponent\r
17313  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the\r
17314  * basic behavior of containing items, namely adding, inserting and removing items.</p>\r
17315  *\r
17316  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.\r
17317  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight\r
17318  * Container to be encapsulated by an HTML element to your specifications by using the\r
17319  * <tt><b>{@link Ext.Component#autoEl autoEl}</b></tt> config option. This is a useful technique when creating\r
17320  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}\r
17321  * for example.</p>\r
17322  *\r
17323  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly\r
17324  * create one using the <b><tt>'container'</tt></b> xtype:<pre><code>\r
17325 // explicitly create a Container\r
17326 var embeddedColumns = new Ext.Container({\r
17327     autoEl: 'div',  // This is the default\r
17328     layout: 'column',\r
17329     defaults: {\r
17330         // implicitly create Container by specifying xtype\r
17331         xtype: 'container',\r
17332         autoEl: 'div', // This is the default.\r
17333         layout: 'form',\r
17334         columnWidth: 0.5,\r
17335         style: {\r
17336             padding: '10px'\r
17337         }\r
17338     },\r
17339 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.\r
17340     items: [{\r
17341         items: {\r
17342             xtype: 'datefield',\r
17343             name: 'startDate',\r
17344             fieldLabel: 'Start date'\r
17345         }\r
17346     }, {\r
17347         items: {\r
17348             xtype: 'datefield',\r
17349             name: 'endDate',\r
17350             fieldLabel: 'End date'\r
17351         }\r
17352     }]\r
17353 });</code></pre></p>\r
17354  *\r
17355  * <p><u><b>Layout</b></u></p>\r
17356  * <p>Container classes delegate the rendering of child Components to a layout\r
17357  * manager class which must be configured into the Container using the\r
17358  * <code><b>{@link #layout}</b></code> configuration property.</p>\r
17359  * <p>When either specifying child <code>{@link #items}</code> of a Container,\r
17360  * or dynamically {@link #add adding} Components to a Container, remember to\r
17361  * consider how you wish the Container to arrange those child elements, and\r
17362  * whether those child elements need to be sized using one of Ext's built-in\r
17363  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the\r
17364  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only\r
17365  * renders child components, appending them one after the other inside the\r
17366  * Container, and <b>does not apply any sizing</b> at all.</p>\r
17367  * <p>A common mistake is when a developer neglects to specify a\r
17368  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or\r
17369  * TreePanels are added to Containers for which no <tt><b>{@link #layout}</b></tt>\r
17370  * has been specified). If a Container is left to use the default\r
17371  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its\r
17372  * child components will be resized, or changed in any way when the Container\r
17373  * is resized.</p>\r
17374  * <p>Certain layout managers allow dynamic addition of child components.\r
17375  * Those that do include {@link Ext.layout.CardLayout},\r
17376  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and\r
17377  * {@link Ext.layout.TableLayout}. For example:<pre><code>\r
17378 //  Create the GridPanel.\r
17379 var myNewGrid = new Ext.grid.GridPanel({\r
17380     store: myStore,\r
17381     columns: myColumnModel,\r
17382     title: 'Results', // the title becomes the title of the tab\r
17383 });\r
17384 \r
17385 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}\r
17386 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);\r
17387  * </code></pre></p>\r
17388  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that\r
17389  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which\r
17390  * means all its child items are sized to {@link Ext.layout.FitLayout fit}\r
17391  * exactly into its client area.\r
17392  * <p><b><u>Overnesting is a common problem</u></b>.\r
17393  * An example of overnesting occurs when a GridPanel is added to a TabPanel\r
17394  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no\r
17395  * <tt><b>{@link #layout}</b></tt> specified) and then add that wrapping Panel\r
17396  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a\r
17397  * Component which can be added directly to a Container. If the wrapping Panel\r
17398  * has no <tt><b>{@link #layout}</b></tt> configuration, then the overnested\r
17399  * GridPanel will not be sized as expected.<p>\r
17400  *\r
17401  * <p><u><b>Adding via remote configuration</b></u></p>\r
17402  *\r
17403  * <p>A server side script can be used to add Components which are generated dynamically on the server.\r
17404  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server\r
17405  * based on certain parameters:\r
17406  * </p><pre><code>\r
17407 // execute an Ajax request to invoke server side script:\r
17408 Ext.Ajax.request({\r
17409     url: 'gen-invoice-grid.php',\r
17410     // send additional parameters to instruct server script\r
17411     params: {\r
17412         startDate: Ext.getCmp('start-date').getValue(),\r
17413         endDate: Ext.getCmp('end-date').getValue()\r
17414     },\r
17415     // process the response object to add it to the TabPanel:\r
17416     success: function(xhr) {\r
17417         var newComponent = eval(xhr.responseText); // see discussion below\r
17418         myTabPanel.add(newComponent); // add the component to the TabPanel\r
17419         myTabPanel.setActiveTab(newComponent);\r
17420     },\r
17421     failure: function() {\r
17422         Ext.Msg.alert("Grid create failed", "Server communication failure");\r
17423     }\r
17424 });\r
17425 </code></pre>\r
17426  * <p>The server script needs to return an executable Javascript statement which, when processed\r
17427  * using <tt>eval()</tt>, will return either a config object with an {@link Ext.Component#xtype xtype},\r
17428  * or an instantiated Component. The server might return this for example:</p><pre><code>\r
17429 (function() {\r
17430     function formatDate(value){\r
17431         return value ? value.dateFormat('M d, Y') : '';\r
17432     };\r
17433 \r
17434     var store = new Ext.data.Store({\r
17435         url: 'get-invoice-data.php',\r
17436         baseParams: {\r
17437             startDate: '01/01/2008',\r
17438             endDate: '01/31/2008'\r
17439         },\r
17440         reader: new Ext.data.JsonReader({\r
17441             record: 'transaction',\r
17442             idProperty: 'id',\r
17443             totalRecords: 'total'\r
17444         }, [\r
17445            'customer',\r
17446            'invNo',\r
17447            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},\r
17448            {name: 'value', type: 'float'}\r
17449         ])\r
17450     });\r
17451 \r
17452     var grid = new Ext.grid.GridPanel({\r
17453         title: 'Invoice Report',\r
17454         bbar: new Ext.PagingToolbar(store),\r
17455         store: store,\r
17456         columns: [\r
17457             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},\r
17458             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},\r
17459             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},\r
17460             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}\r
17461         ],\r
17462     });\r
17463     store.load();\r
17464     return grid;  // return instantiated component\r
17465 })();\r
17466 </code></pre>\r
17467  * <p>When the above code fragment is passed through the <tt>eval</tt> function in the success handler\r
17468  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function\r
17469  * runs, and returns the instantiated grid component.</p>\r
17470  * <p>Note: since the code above is <i>generated</i> by a server script, the <tt>baseParams</tt> for\r
17471  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel\r
17472  * can all be generated into the code since these are all known on the server.</p>\r
17473  *\r
17474  * @xtype container\r
17475  */\r
17476 Ext.Container = Ext.extend(Ext.BoxComponent, {\r
17477     /**\r
17478      * @cfg {Boolean} monitorResize\r
17479      * True to automatically monitor window resize events to handle anything that is sensitive to the current size\r
17480      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need\r
17481      * to be set manually.\r
17482      */\r
17483     /**\r
17484      * @cfg {String/Object} layout\r
17485      * <p><b>*Important</b>: In order for child items to be correctly sized and\r
17486      * positioned, typically a layout manager <b>must</b> be specified through\r
17487      * the <code>layout</code> configuration option.</p>\r
17488      * <br><p>The sizing and positioning of child {@link items} is the responsibility of\r
17489      * the Container's layout manager which creates and manages the type of layout\r
17490      * you have in mind.  For example:</p><pre><code>\r
17491 new Ext.Window({\r
17492     width:300, height: 300,\r
17493     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')\r
17494     items: [{\r
17495         title: 'Panel inside a Window'\r
17496     }]\r
17497 }).show();\r
17498      * </code></pre>\r
17499      * <p>If the {@link #layout} configuration is not explicitly specified for\r
17500      * a general purpose container (e.g. Container or Panel) the\r
17501      * {@link Ext.layout.ContainerLayout default layout manager} will be used\r
17502      * which does nothing but render child components sequentially into the\r
17503      * Container (no sizing or positioning will be performed in this situation).\r
17504      * Some container classes implicitly specify a default layout\r
17505      * (e.g. FormPanel specifies <code>layout:'form'</code>). Other specific\r
17506      * purpose classes internally specify/manage their internal layout (e.g.\r
17507      * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).</p>\r
17508      * <br><p><b><code>layout</code></b> may be specified as either as an Object or\r
17509      * as a String:</p><div><ul class="mdetail-params">\r
17510      *\r
17511      * <li><u>Specify as an Object</u></li>\r
17512      * <div><ul class="mdetail-params">\r
17513      * <li>Example usage:</li>\r
17514 <pre><code>\r
17515 layout: {\r
17516     type: 'vbox',\r
17517     padding: '5',\r
17518     align: 'left'\r
17519 }\r
17520 </code></pre>\r
17521      *\r
17522      * <li><tt><b>type</b></tt></li>\r
17523      * <br/><p>The layout type to be used for this container.  If not specified,\r
17524      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>\r
17525      * <br/><p>Valid layout <tt>type</tt> values are:</p>\r
17526      * <div class="sub-desc"><ul class="mdetail-params">\r
17527      * <li><tt><b>{@link Ext.layout.AbsoluteLayout absolute}</b></tt></li>\r
17528      * <li><tt><b>{@link Ext.layout.AccordionLayout accordion}</b></tt></li>\r
17529      * <li><tt><b>{@link Ext.layout.AnchorLayout anchor}</b></tt></li>\r
17530      * <li><tt><b>{@link Ext.layout.ContainerLayout auto}</b></tt> &nbsp;&nbsp;&nbsp; <b>Default</b></li>\r
17531      * <li><tt><b>{@link Ext.layout.BorderLayout border}</b></tt></li>\r
17532      * <li><tt><b>{@link Ext.layout.CardLayout card}</b></tt></li>\r
17533      * <li><tt><b>{@link Ext.layout.ColumnLayout column}</b></tt></li>\r
17534      * <li><tt><b>{@link Ext.layout.FitLayout fit}</b></tt></li>\r
17535      * <li><tt><b>{@link Ext.layout.FormLayout form}</b></tt></li>\r
17536      * <li><tt><b>{@link Ext.layout.HBoxLayout hbox}</b></tt></li>\r
17537      * <li><tt><b>{@link Ext.layout.MenuLayout menu}</b></tt></li>\r
17538      * <li><tt><b>{@link Ext.layout.TableLayout table}</b></tt></li>\r
17539      * <li><tt><b>{@link Ext.layout.ToolbarLayout toolbar}</b></tt></li>\r
17540      * <li><tt><b>{@link Ext.layout.VBoxLayout vbox}</b></tt></li>\r
17541      * </ul></div>\r
17542      *\r
17543      * <li>Layout specific configuration properties</li>\r
17544      * <br/><p>Additional layout specific configuration properties may also be\r
17545      * specified. For complete details regarding the valid config options for\r
17546      * each layout type, see the layout class corresponding to the <tt>type</tt>\r
17547      * specified.</p>\r
17548      *\r
17549      * </ul></div>\r
17550      *\r
17551      * <li><u>Specify as a String</u></li>\r
17552      * <div><ul class="mdetail-params">\r
17553      * <li>Example usage:</li>\r
17554 <pre><code>\r
17555 layout: 'vbox',\r
17556 layoutConfig: {\r
17557     padding: '5',\r
17558     align: 'left'\r
17559 }\r
17560 </code></pre>\r
17561      * <li><tt><b>layout</b></tt></li>\r
17562      * <br/><p>The layout <tt>type</tt> to be used for this container (see list\r
17563      * of valid layout type values above).</p><br/>\r
17564      * <li><tt><b>{@link #layoutConfig}</b></tt></li>\r
17565      * <br/><p>Additional layout specific configuration properties. For complete\r
17566      * details regarding the valid config options for each layout type, see the\r
17567      * layout class corresponding to the <tt>layout</tt> specified.</p>\r
17568      * </ul></div></ul></div>\r
17569      */\r
17570     /**\r
17571      * @cfg {Object} layoutConfig\r
17572      * This is a config object containing properties specific to the chosen\r
17573      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>\r
17574      * has been specified as a <i>string</i>.</p>\r
17575      */\r
17576     /**\r
17577      * @cfg {Boolean/Number} bufferResize\r
17578      * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer\r
17579      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers\r
17580      * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <tt>50</tt>.\r
17581      */\r
17582     bufferResize: 50,\r
17583 \r
17584     /**\r
17585      * @cfg {String/Number} activeItem\r
17586      * A string component id or the numeric index of the component that should be initially activated within the\r
17587      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first\r
17588      * item in the container's collection).  activeItem only applies to layout styles that can display\r
17589      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and\r
17590      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.\r
17591      */\r
17592     /**\r
17593      * @cfg {Object/Array} items\r
17594      * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>\r
17595      * <p>A single item, or an array of child Components to be added to this container,\r
17596      * for example:</p>\r
17597      * <pre><code>\r
17598 // specifying a single item\r
17599 items: {...},\r
17600 layout: 'fit',    // specify a layout!\r
17601 \r
17602 // specifying multiple items\r
17603 items: [{...}, {...}],\r
17604 layout: 'anchor', // specify a layout!\r
17605      * </code></pre>\r
17606      * <p>Each item may be:</p>\r
17607      * <div><ul class="mdetail-params">\r
17608      * <li>any type of object based on {@link Ext.Component}</li>\r
17609      * <li>a fully instanciated object or</li>\r
17610      * <li>an object literal that:</li>\r
17611      * <div><ul class="mdetail-params">\r
17612      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>\r
17613      * <li>the {@link Ext.Component#xtype} specified is associated with the Component\r
17614      * desired and should be chosen from one of the available xtypes as listed\r
17615      * in {@link Ext.Component}.</li>\r
17616      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly\r
17617      * specified, the {@link #defaultType} for that Container is used.</li>\r
17618      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully\r
17619      * instanciated Component object</li>\r
17620      * </ul></div></ul></div>\r
17621      * <p><b>Notes</b>:</p>\r
17622      * <div><ul class="mdetail-params">\r
17623      * <li>Ext uses lazy rendering. Child Components will only be rendered\r
17624      * should it become necessary. Items are automatically laid out when they are first\r
17625      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>\r
17626      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/\r
17627      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>\r
17628      * </ul></div>\r
17629      */\r
17630     /**\r
17631      * @cfg {Object} defaults\r
17632      * <p>A config object that will be applied to all components added to this container either via the {@link #items}\r
17633      * config or via the {@link #add} or {@link #insert} methods.  The <tt>defaults</tt> config can contain any\r
17634      * number of name/value property pairs to be added to each item, and should be valid for the types of items\r
17635      * being added to the container.  For example, to automatically apply padding to the body of each of a set of\r
17636      * contained {@link Ext.Panel} items, you could pass: <tt>defaults: {bodyStyle:'padding:15px'}</tt>.</p><br/>\r
17637      * <p><b>Note</b>: <tt>defaults</tt> will not be applied to config objects if the option is already specified.\r
17638      * For example:</p><pre><code>\r
17639 defaults: {               // defaults are applied to items, not the container\r
17640     autoScroll:true\r
17641 },\r
17642 items: [\r
17643     {\r
17644         xtype: 'panel',   // defaults <b>do not</b> have precedence over\r
17645         id: 'panel1',     // options in config objects, so the defaults\r
17646         autoScroll: false // will not be applied here, panel1 will be autoScroll:false\r
17647     },\r
17648     new Ext.Panel({       // defaults <b>do</b> have precedence over options\r
17649         id: 'panel2',     // options in components, so the defaults\r
17650         autoScroll: false // will be applied here, panel2 will be autoScroll:true.\r
17651     })\r
17652 ]\r
17653      * </code></pre>\r
17654      */\r
17655 \r
17656 \r
17657     /** @cfg {Boolean} autoDestroy\r
17658      * If true the container will automatically destroy any contained component that is removed from it, else\r
17659      * destruction must be handled manually (defaults to true).\r
17660      */\r
17661     autoDestroy : true,\r
17662 \r
17663     /** @cfg {Boolean} forceLayout\r
17664      * If true the container will force a layout initially even if hidden or collapsed. This option\r
17665      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).\r
17666      */\r
17667     forceLayout: false,\r
17668 \r
17669     /** @cfg {Boolean} hideBorders\r
17670      * True to hide the borders of each contained component, false to defer to the component's existing\r
17671      * border settings (defaults to false).\r
17672      */\r
17673     /** @cfg {String} defaultType\r
17674      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when\r
17675      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>\r
17676      * <p>Defaults to <tt>'panel'</tt>, except {@link Ext.menu.Menu} which defaults to <tt>'menuitem'</tt>,\r
17677      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <tt>'button'</tt>.</p>\r
17678      */\r
17679     defaultType : 'panel',\r
17680 \r
17681     /** @cfg {String} resizeEvent\r
17682      * The event to listen to for resizing in layouts. Defaults to <tt>'resize'</tt>.\r
17683      */\r
17684     resizeEvent: 'resize',\r
17685     \r
17686     /**\r
17687      * @cfg {Array} bubbleEvents\r
17688      * <p>An array of events that, when fired, should be bubbled to any parent container.\r
17689      * Defaults to <tt>['add', 'remove']</tt>.\r
17690      */\r
17691     bubbleEvents: ['add', 'remove'],\r
17692 \r
17693     // private\r
17694     initComponent : function(){\r
17695         Ext.Container.superclass.initComponent.call(this);\r
17696 \r
17697         this.addEvents(\r
17698             /**\r
17699              * @event afterlayout\r
17700              * Fires when the components in this container are arranged by the associated layout manager.\r
17701              * @param {Ext.Container} this\r
17702              * @param {ContainerLayout} layout The ContainerLayout implementation for this container\r
17703              */\r
17704             'afterlayout',\r
17705             /**\r
17706              * @event beforeadd\r
17707              * Fires before any {@link Ext.Component} is added or inserted into the container.\r
17708              * A handler can return false to cancel the add.\r
17709              * @param {Ext.Container} this\r
17710              * @param {Ext.Component} component The component being added\r
17711              * @param {Number} index The index at which the component will be added to the container's items collection\r
17712              */\r
17713             'beforeadd',\r
17714             /**\r
17715              * @event beforeremove\r
17716              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return\r
17717              * false to cancel the remove.\r
17718              * @param {Ext.Container} this\r
17719              * @param {Ext.Component} component The component being removed\r
17720              */\r
17721             'beforeremove',\r
17722             /**\r
17723              * @event add\r
17724              * @bubbles\r
17725              * Fires after any {@link Ext.Component} is added or inserted into the container.\r
17726              * @param {Ext.Container} this\r
17727              * @param {Ext.Component} component The component that was added\r
17728              * @param {Number} index The index at which the component was added to the container's items collection\r
17729              */\r
17730             'add',\r
17731             /**\r
17732              * @event remove\r
17733              * @bubbles\r
17734              * Fires after any {@link Ext.Component} is removed from the container.\r
17735              * @param {Ext.Container} this\r
17736              * @param {Ext.Component} component The component that was removed\r
17737              */\r
17738             'remove'\r
17739         );\r
17740 \r
17741         this.enableBubble(this.bubbleEvents);\r
17742 \r
17743         /**\r
17744          * The collection of components in this container as a {@link Ext.util.MixedCollection}\r
17745          * @type MixedCollection\r
17746          * @property items\r
17747          */\r
17748         var items = this.items;\r
17749         if(items){\r
17750             delete this.items;\r
17751             this.add(items);\r
17752         }\r
17753     },\r
17754 \r
17755     // private\r
17756     initItems : function(){\r
17757         if(!this.items){\r
17758             this.items = new Ext.util.MixedCollection(false, this.getComponentId);\r
17759             this.getLayout(); // initialize the layout\r
17760         }\r
17761     },\r
17762 \r
17763     // private\r
17764     setLayout : function(layout){\r
17765         if(this.layout && this.layout != layout){\r
17766             this.layout.setContainer(null);\r
17767         }\r
17768         this.initItems();\r
17769         this.layout = layout;\r
17770         layout.setContainer(this);\r
17771     },\r
17772 \r
17773     afterRender: function(){\r
17774         Ext.Container.superclass.afterRender.call(this);\r
17775         if(!this.layout){\r
17776             this.layout = 'auto';\r
17777         }\r
17778         if(Ext.isObject(this.layout) && !this.layout.layout){\r
17779             this.layoutConfig = this.layout;\r
17780             this.layout = this.layoutConfig.type;\r
17781         }\r
17782         if(Ext.isString(this.layout)){\r
17783             this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);\r
17784         }\r
17785         this.setLayout(this.layout);\r
17786 \r
17787         if(this.activeItem !== undefined){\r
17788             var item = this.activeItem;\r
17789             delete this.activeItem;\r
17790             this.layout.setActiveItem(item);\r
17791         }\r
17792         if(!this.ownerCt){\r
17793             // force a layout if no ownerCt is set\r
17794             this.doLayout(false, true);\r
17795         }\r
17796         if(this.monitorResize === true){\r
17797             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);\r
17798         }\r
17799     },\r
17800 \r
17801     /**\r
17802      * <p>Returns the Element to be used to contain the child Components of this Container.</p>\r
17803      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but\r
17804      * if there is a more complex structure to a Container, this may be overridden to return\r
17805      * the element into which the {@link #layout layout} renders child Components.</p>\r
17806      * @return {Ext.Element} The Element to render child Components into.\r
17807      */\r
17808     getLayoutTarget : function(){\r
17809         return this.el;\r
17810     },\r
17811 \r
17812     // private - used as the key lookup function for the items collection\r
17813     getComponentId : function(comp){\r
17814         return comp.getItemId();\r
17815     },\r
17816 \r
17817     /**\r
17818      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>\r
17819      * <br><p><b>Description</b></u> :\r
17820      * <div><ul class="mdetail-params">\r
17821      * <li>Fires the {@link #beforeadd} event before adding</li>\r
17822      * <li>The Container's {@link #defaults default config values} will be applied\r
17823      * accordingly (see <code>{@link #defaults}</code> for details).</li>\r
17824      * <li>Fires the {@link #add} event after the component has been added.</li>\r
17825      * </ul></div>\r
17826      * <br><p><b>Notes</b></u> :\r
17827      * <div><ul class="mdetail-params">\r
17828      * <li>If the Container is <i>already rendered</i> when <tt>add</tt>\r
17829      * is called, you may need to call {@link #doLayout} to refresh the view which causes\r
17830      * any unrendered child Components to be rendered. This is required so that you can\r
17831      * <tt>add</tt> multiple child components if needed while only refreshing the layout\r
17832      * once. For example:<pre><code>\r
17833 var tb = new {@link Ext.Toolbar}();\r
17834 tb.render(document.body);  // toolbar is rendered\r
17835 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')\r
17836 tb.add({text:'Button 2'});\r
17837 tb.{@link #doLayout}();             // refresh the layout\r
17838      * </code></pre></li>\r
17839      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager\r
17840      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}\r
17841      * for more details.</li>\r
17842      * </ul></div>\r
17843      * @param {Object/Array} component\r
17844      * <p>Either a single component or an Array of components to add.  See\r
17845      * <code>{@link #items}</code> for additional information.</p>\r
17846      * @param {Object} (Optional) component_2\r
17847      * @param {Object} (Optional) component_n\r
17848      * @return {Ext.Component} component The Component (or config object) that was added.\r
17849      */\r
17850     add : function(comp){\r
17851         this.initItems();\r
17852         var args = arguments.length > 1;\r
17853         if(args || Ext.isArray(comp)){\r
17854             Ext.each(args ? arguments : comp, function(c){\r
17855                 this.add(c);\r
17856             }, this);\r
17857             return;\r
17858         }\r
17859         var c = this.lookupComponent(this.applyDefaults(comp));\r
17860         var pos = this.items.length;\r
17861         if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){\r
17862             this.items.add(c);\r
17863             c.ownerCt = this;\r
17864             this.onAdd(c);\r
17865             this.fireEvent('add', this, c, pos);\r
17866         }\r
17867         return c;\r
17868     },\r
17869 \r
17870     onAdd : function(c){\r
17871         // Empty template method\r
17872     },\r
17873 \r
17874     /**\r
17875      * Inserts a Component into this Container at a specified index. Fires the\r
17876      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the\r
17877      * Component has been inserted.\r
17878      * @param {Number} index The index at which the Component will be inserted\r
17879      * into the Container's items collection\r
17880      * @param {Ext.Component} component The child Component to insert.<br><br>\r
17881      * Ext uses lazy rendering, and will only render the inserted Component should\r
17882      * it become necessary.<br><br>\r
17883      * A Component config object may be passed in order to avoid the overhead of\r
17884      * constructing a real Component object if lazy rendering might mean that the\r
17885      * inserted Component will not be rendered immediately. To take advantage of\r
17886      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config\r
17887      * property to the registered type of the Component wanted.<br><br>\r
17888      * For a list of all available xtypes, see {@link Ext.Component}.\r
17889      * @return {Ext.Component} component The Component (or config object) that was\r
17890      * inserted with the Container's default config values applied.\r
17891      */\r
17892     insert : function(index, comp){\r
17893         this.initItems();\r
17894         var a = arguments, len = a.length;\r
17895         if(len > 2){\r
17896             for(var i = len-1; i >= 1; --i) {\r
17897                 this.insert(index, a[i]);\r
17898             }\r
17899             return;\r
17900         }\r
17901         var c = this.lookupComponent(this.applyDefaults(comp));\r
17902         index = Math.min(index, this.items.length);\r
17903         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){\r
17904             if(c.ownerCt == this){\r
17905                 this.items.remove(c);\r
17906             }\r
17907             this.items.insert(index, c);\r
17908             c.ownerCt = this;\r
17909             this.onAdd(c);\r
17910             this.fireEvent('add', this, c, index);\r
17911         }\r
17912         return c;\r
17913     },\r
17914 \r
17915     // private\r
17916     applyDefaults : function(c){\r
17917         if(this.defaults){\r
17918             if(Ext.isString(c)){\r
17919                 c = Ext.ComponentMgr.get(c);\r
17920                 Ext.apply(c, this.defaults);\r
17921             }else if(!c.events){\r
17922                 Ext.applyIf(c, this.defaults);\r
17923             }else{\r
17924                 Ext.apply(c, this.defaults);\r
17925             }\r
17926         }\r
17927         return c;\r
17928     },\r
17929 \r
17930     // private\r
17931     onBeforeAdd : function(item){\r
17932         if(item.ownerCt){\r
17933             item.ownerCt.remove(item, false);\r
17934         }\r
17935         if(this.hideBorders === true){\r
17936             item.border = (item.border === true);\r
17937         }\r
17938     },\r
17939 \r
17940     /**\r
17941      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires\r
17942      * the {@link #remove} event after the component has been removed.\r
17943      * @param {Component/String} component The component reference or id to remove.\r
17944      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.\r
17945      * Defaults to the value of this Container's {@link #autoDestroy} config.\r
17946      * @return {Ext.Component} component The Component that was removed.\r
17947      */\r
17948     remove : function(comp, autoDestroy){\r
17949         this.initItems();\r
17950         var c = this.getComponent(comp);\r
17951         if(c && this.fireEvent('beforeremove', this, c) !== false){\r
17952             delete c.ownerCt;\r
17953             if(this.layout && this.rendered){\r
17954                 this.layout.onRemove(c);\r
17955             }\r
17956             this.onRemove(c);\r
17957             this.items.remove(c);\r
17958             if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){\r
17959                 c.destroy();\r
17960             }\r
17961             this.fireEvent('remove', this, c);\r
17962         }\r
17963         return c;\r
17964     },\r
17965 \r
17966     onRemove: function(c){\r
17967         // Empty template method\r
17968     },\r
17969 \r
17970     /**\r
17971      * Removes all components from this container.\r
17972      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.\r
17973      * Defaults to the value of this Container's {@link #autoDestroy} config.\r
17974      * @return {Array} Array of the destroyed components\r
17975      */\r
17976     removeAll: function(autoDestroy){\r
17977         this.initItems();\r
17978         var item, rem = [], items = [];\r
17979         this.items.each(function(i){\r
17980             rem.push(i);\r
17981         });\r
17982         for (var i = 0, len = rem.length; i < len; ++i){\r
17983             item = rem[i];\r
17984             this.remove(item, autoDestroy);\r
17985             if(item.ownerCt !== this){\r
17986                 items.push(item);\r
17987             }\r
17988         }\r
17989         return items;\r
17990     },\r
17991 \r
17992     /**\r
17993      * Examines this container's <code>{@link #items}</code> <b>property</b>\r
17994      * and gets a direct child component of this container.\r
17995      * @param {String/Number} comp This parameter may be any of the following:\r
17996      * <div><ul class="mdetail-params">\r
17997      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>\r
17998      * or <code>{@link Ext.Component#id id}</code> of the child component </li>\r
17999      * <li>a <b><tt>Number</tt></b> : representing the position of the child component\r
18000      * within the <code>{@link #items}</code> <b>property</b></li>\r
18001      * </ul></div>\r
18002      * <p>For additional information see {@link Ext.util.MixedCollection#get}.\r
18003      * @return Ext.Component The component (if found).\r
18004      */\r
18005     getComponent : function(comp){\r
18006         if(Ext.isObject(comp)){\r
18007             comp = comp.getItemId();\r
18008         }\r
18009         return this.items.get(comp);\r
18010     },\r
18011 \r
18012     // private\r
18013     lookupComponent : function(comp){\r
18014         if(Ext.isString(comp)){\r
18015             return Ext.ComponentMgr.get(comp);\r
18016         }else if(!comp.events){\r
18017             return this.createComponent(comp);\r
18018         }\r
18019         return comp;\r
18020     },\r
18021 \r
18022     // private\r
18023     createComponent : function(config){\r
18024         return Ext.create(config, this.defaultType);\r
18025     },\r
18026 \r
18027     // private\r
18028     canLayout: function() {\r
18029         var el = this.getVisibilityEl();\r
18030         return el && !el.isStyle("display", "none");\r
18031     },\r
18032 \r
18033 \r
18034     /**\r
18035      * Force this container's layout to be recalculated. A call to this function is required after adding a new component\r
18036      * to an already rendered container, or possibly after changing sizing/position properties of child components.\r
18037      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto\r
18038      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)\r
18039      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.\r
18040      * @return {Ext.Container} this\r
18041      */\r
18042     doLayout: function(shallow, force){\r
18043         var rendered = this.rendered;\r
18044         forceLayout = force || this.forceLayout;\r
18045 \r
18046         if(!this.canLayout() || this.collapsed){\r
18047             this.deferLayout = this.deferLayout || !shallow;\r
18048             if(!forceLayout){\r
18049                 return;\r
18050             }\r
18051             shallow = shallow && !this.deferLayout;\r
18052         } else {\r
18053             delete this.deferLayout;\r
18054         }\r
18055         if(rendered && this.layout){\r
18056             this.layout.layout();\r
18057         }\r
18058         if(shallow !== true && this.items){\r
18059             var cs = this.items.items;\r
18060             for(var i = 0, len = cs.length; i < len; i++){\r
18061                 var c = cs[i];\r
18062                 if(c.doLayout){\r
18063                     c.doLayout(false, forceLayout);\r
18064                 }\r
18065             }\r
18066         }\r
18067         if(rendered){\r
18068             this.onLayout(shallow, forceLayout);\r
18069         }\r
18070         // Initial layout completed\r
18071         this.hasLayout = true;\r
18072         delete this.forceLayout;\r
18073     },\r
18074 \r
18075     //private\r
18076     onLayout : Ext.emptyFn,\r
18077 \r
18078     // private\r
18079     shouldBufferLayout: function(){\r
18080         /*\r
18081          * Returns true if the container should buffer a layout.\r
18082          * This is true only if the container has previously been laid out\r
18083          * and has a parent container that is pending a layout.\r
18084          */\r
18085         var hl = this.hasLayout;\r
18086         if(this.ownerCt){\r
18087             // Only ever buffer if we've laid out the first time and we have one pending.\r
18088             return hl ? !this.hasLayoutPending() : false;\r
18089         }\r
18090         // Never buffer initial layout\r
18091         return hl;\r
18092     },\r
18093 \r
18094     // private\r
18095     hasLayoutPending: function(){\r
18096         // Traverse hierarchy to see if any parent container has a pending layout.\r
18097         var pending = false;\r
18098         this.ownerCt.bubble(function(c){\r
18099             if(c.layoutPending){\r
18100                 pending = true;\r
18101                 return false;\r
18102             }\r
18103         });\r
18104         return pending;\r
18105     },\r
18106 \r
18107     onShow : function(){\r
18108         Ext.Container.superclass.onShow.call(this);\r
18109         if(this.deferLayout !== undefined){\r
18110             this.doLayout(true);\r
18111         }\r
18112     },\r
18113 \r
18114     /**\r
18115      * Returns the layout currently in use by the container.  If the container does not currently have a layout\r
18116      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.\r
18117      * @return {ContainerLayout} layout The container's layout\r
18118      */\r
18119     getLayout : function(){\r
18120         if(!this.layout){\r
18121             var layout = new Ext.layout.ContainerLayout(this.layoutConfig);\r
18122             this.setLayout(layout);\r
18123         }\r
18124         return this.layout;\r
18125     },\r
18126 \r
18127     // private\r
18128     beforeDestroy : function(){\r
18129         if(this.items){\r
18130             Ext.destroy.apply(Ext, this.items.items);\r
18131         }\r
18132         if(this.monitorResize){\r
18133             Ext.EventManager.removeResizeListener(this.doLayout, this);\r
18134         }\r
18135         Ext.destroy(this.layout);\r
18136         Ext.Container.superclass.beforeDestroy.call(this);\r
18137     },\r
18138 \r
18139     /**\r
18140      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of\r
18141      * function call will be the scope provided or the current component. The arguments to the function\r
18142      * will be the args provided or the current component. If the function returns false at any point,\r
18143      * the bubble is stopped.\r
18144      * @param {Function} fn The function to call\r
18145      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
18146      * @param {Array} args (optional) The args to call the function with (default to passing the current component)\r
18147      * @return {Ext.Container} this\r
18148      */\r
18149     bubble : function(fn, scope, args){\r
18150         var p = this;\r
18151         while(p){\r
18152             if(fn.apply(scope || p, args || [p]) === false){\r
18153                 break;\r
18154             }\r
18155             p = p.ownerCt;\r
18156         }\r
18157         return this;\r
18158     },\r
18159 \r
18160     /**\r
18161      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with\r
18162      * each component. The scope (<i>this</i>) of\r
18163      * function call will be the scope provided or the current component. The arguments to the function\r
18164      * will be the args provided or the current component. If the function returns false at any point,\r
18165      * the cascade is stopped on that branch.\r
18166      * @param {Function} fn The function to call\r
18167      * @param {Object} scope (optional) The scope of the function (defaults to current component)\r
18168      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)\r
18169      * @return {Ext.Container} this\r
18170      */\r
18171     cascade : function(fn, scope, args){\r
18172         if(fn.apply(scope || this, args || [this]) !== false){\r
18173             if(this.items){\r
18174                 var cs = this.items.items;\r
18175                 for(var i = 0, len = cs.length; i < len; i++){\r
18176                     if(cs[i].cascade){\r
18177                         cs[i].cascade(fn, scope, args);\r
18178                     }else{\r
18179                         fn.apply(scope || cs[i], args || [cs[i]]);\r
18180                     }\r
18181                 }\r
18182             }\r
18183         }\r
18184         return this;\r
18185     },\r
18186 \r
18187     /**\r
18188      * Find a component under this container at any level by id\r
18189      * @param {String} id\r
18190      * @return Ext.Component\r
18191      */\r
18192     findById : function(id){\r
18193         var m, ct = this;\r
18194         this.cascade(function(c){\r
18195             if(ct != c && c.id === id){\r
18196                 m = c;\r
18197                 return false;\r
18198             }\r
18199         });\r
18200         return m || null;\r
18201     },\r
18202 \r
18203     /**\r
18204      * Find a component under this container at any level by xtype or class\r
18205      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly\r
18206      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is\r
18207      * the default), or true to check whether this Component is directly of the specified xtype.\r
18208      * @return {Array} Array of Ext.Components\r
18209      */\r
18210     findByType : function(xtype, shallow){\r
18211         return this.findBy(function(c){\r
18212             return c.isXType(xtype, shallow);\r
18213         });\r
18214     },\r
18215 \r
18216     /**\r
18217      * Find a component under this container at any level by property\r
18218      * @param {String} prop\r
18219      * @param {String} value\r
18220      * @return {Array} Array of Ext.Components\r
18221      */\r
18222     find : function(prop, value){\r
18223         return this.findBy(function(c){\r
18224             return c[prop] === value;\r
18225         });\r
18226     },\r
18227 \r
18228     /**\r
18229      * Find a component under this container at any level by a custom function. If the passed function returns\r
18230      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).\r
18231      * @param {Function} fn The function to call\r
18232      * @param {Object} scope (optional)\r
18233      * @return {Array} Array of Ext.Components\r
18234      */\r
18235     findBy : function(fn, scope){\r
18236         var m = [], ct = this;\r
18237         this.cascade(function(c){\r
18238             if(ct != c && fn.call(scope || c, c, ct) === true){\r
18239                 m.push(c);\r
18240             }\r
18241         });\r
18242         return m;\r
18243     },\r
18244 \r
18245     /**\r
18246      * Get a component contained by this container (alias for items.get(key))\r
18247      * @param {String/Number} key The index or id of the component\r
18248      * @return {Ext.Component} Ext.Component\r
18249      */\r
18250     get : function(key){\r
18251         return this.items.get(key);\r
18252     }\r
18253 });\r
18254 \r
18255 Ext.Container.LAYOUTS = {};\r
18256 Ext.reg('container', Ext.Container);\r
18257 /**
18258  * @class Ext.layout.ContainerLayout
18259  * <p>The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to
18260  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
18261  * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout
18262  * classes in Ext. It simply renders all child Components into the Container, performing no sizing or
18263  * positioning services. To utilize a layout that provides sizing and positioning of child Components,
18264  * specify an appropriate <tt>{@link Ext.Container#layout layout}</tt>.</p>
18265  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
18266  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
18267  */
18268 Ext.layout.ContainerLayout = function(config){
18269     Ext.apply(this, config);
18270 };
18271
18272 Ext.layout.ContainerLayout.prototype = {
18273     /**
18274      * @cfg {String} extraCls
18275      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
18276      * customized styles to the container or any of its children using standard CSS rules. See
18277      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
18278      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
18279      * which assign a value by default:
18280      * <div class="mdetail-params"><ul>
18281      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
18282      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
18283      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
18284      * </ul></div>
18285      * To configure the above Classes with an extra CSS class append to the default.  For example,
18286      * for ColumnLayout:<pre><code>
18287      * extraCls: 'x-column custom-class'
18288      * </code></pre>
18289      * </p>
18290      */
18291     /**
18292      * @cfg {Boolean} renderHidden
18293      * True to hide each contained item on render (defaults to false).
18294      */
18295
18296     /**
18297      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
18298      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
18299      * </code></pre>
18300      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
18301      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
18302      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
18303      * @type {Ext.Component}
18304      * @property activeItem
18305      */
18306
18307     // private
18308     monitorResize:false,
18309     // private
18310     activeItem : null,
18311
18312     // private
18313     layout : function(){
18314         var target = this.container.getLayoutTarget();
18315         this.onLayout(this.container, target);
18316         this.container.fireEvent('afterlayout', this.container, this);
18317     },
18318
18319     // private
18320     onLayout : function(ct, target){
18321         this.renderAll(ct, target);
18322     },
18323
18324     // private
18325     isValidParent : function(c, target){
18326                 return target && c.getDomPositionEl().dom.parentNode == (target.dom || target);
18327     },
18328
18329     // private
18330     renderAll : function(ct, target){
18331         var items = ct.items.items;
18332         for(var i = 0, len = items.length; i < len; i++) {
18333             var c = items[i];
18334             if(c && (!c.rendered || !this.isValidParent(c, target))){
18335                 this.renderItem(c, i, target);
18336             }
18337         }
18338     },
18339
18340     // private
18341     renderItem : function(c, position, target){
18342         if(c && !c.rendered){
18343             c.render(target, position);
18344             this.configureItem(c, position);
18345         }else if(c && !this.isValidParent(c, target)){
18346             if(Ext.isNumber(position)){
18347                 position = target.dom.childNodes[position];
18348             }
18349             target.dom.insertBefore(c.getDomPositionEl().dom, position || null);
18350             c.container = target;
18351             this.configureItem(c, position);
18352         }
18353     },
18354     
18355     // private
18356     configureItem: function(c, position){
18357         if(this.extraCls){
18358             var t = c.getPositionEl ? c.getPositionEl() : c;
18359             t.addClass(this.extraCls);
18360         }
18361         if (this.renderHidden && c != this.activeItem) {
18362             c.hide();
18363         }
18364         if(c.doLayout && this.forceLayout){
18365             c.doLayout(false, true);
18366         }
18367     },
18368     
18369     onRemove: function(c){
18370          if(this.activeItem == c){
18371             delete this.activeItem;
18372          }
18373          if(c.rendered && this.extraCls){
18374             var t = c.getPositionEl ? c.getPositionEl() : c;
18375             t.removeClass(this.extraCls);
18376         }
18377     },
18378
18379     // private
18380     onResize: function(){
18381         var ct = this.container,
18382             b;
18383             
18384         if(ct.collapsed){
18385             return;
18386         }
18387         if(b = ct.bufferResize){
18388             // Only allow if we should buffer the layout
18389             if(ct.shouldBufferLayout()){
18390                 if(!this.resizeTask){
18391                     this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
18392                     this.resizeBuffer = Ext.isNumber(b) ? b : 50;
18393                 }
18394                 ct.layoutPending = true;
18395                 this.resizeTask.delay(this.resizeBuffer);
18396             }
18397         }else{
18398             ct.doLayout();
18399         }
18400     },
18401     
18402     // private
18403     runLayout: function(){
18404         var ct = this.container;
18405         ct.doLayout();
18406         delete ct.layoutPending;
18407     },
18408
18409     // private
18410     setContainer : function(ct){
18411         if(this.monitorResize && ct != this.container){
18412             var old = this.container;
18413             if(old){
18414                 old.un(old.resizeEvent, this.onResize, this);
18415             }
18416             if(ct){
18417                 ct.on(ct.resizeEvent, this.onResize, this);
18418             }
18419         }
18420         this.container = ct;
18421     },
18422
18423     // private
18424     parseMargins : function(v){
18425         if(Ext.isNumber(v)){
18426             v = v.toString();
18427         }
18428         var ms = v.split(' ');
18429         var len = ms.length;
18430         if(len == 1){
18431             ms[1] = ms[0];
18432             ms[2] = ms[0];
18433             ms[3] = ms[0];
18434         }
18435         if(len == 2){
18436             ms[2] = ms[0];
18437             ms[3] = ms[1];
18438         }
18439         if(len == 3){
18440             ms[3] = ms[1];
18441         }
18442         return {
18443             top:parseInt(ms[0], 10) || 0,
18444             right:parseInt(ms[1], 10) || 0,
18445             bottom:parseInt(ms[2], 10) || 0,
18446             left:parseInt(ms[3], 10) || 0
18447         };
18448     },
18449
18450     /**
18451      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
18452      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
18453      * labeled and styled form Field. A default Template is supplied, but this may be
18454      * overriden to create custom field structures. The template processes values returned from
18455      * {@link Ext.layout.FormLayout#getTemplateArgs}.
18456      * @property fieldTpl
18457      * @type Ext.Template
18458      */
18459     fieldTpl: (function() {
18460         var t = new Ext.Template(
18461             '<div class="x-form-item {itemCls}" tabIndex="-1">',
18462                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
18463                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
18464                 '</div><div class="{clearCls}"></div>',
18465             '</div>'
18466         );
18467         t.disableFormats = true;
18468         return t.compile();
18469     })(),
18470         
18471     /*
18472      * Destroys this layout. This is a template method that is empty by default, but should be implemented
18473      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
18474      * @protected
18475      */
18476     destroy : Ext.emptyFn
18477 };
18478 Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout;/**\r
18479  * @class Ext.layout.FitLayout\r
18480  * @extends Ext.layout.ContainerLayout\r
18481  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's\r
18482  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}\r
18483  * config, and should generally not need to be created directly via the new keyword.</p>\r
18484  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container\r
18485  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has\r
18486  * multiple panels, only the first one will be displayed.  Example usage:</p>\r
18487  * <pre><code>\r
18488 var p = new Ext.Panel({\r
18489     title: 'Fit Layout',\r
18490     layout:'fit',\r
18491     items: {\r
18492         title: 'Inner Panel',\r
18493         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',\r
18494         border: false\r
18495     }\r
18496 });\r
18497 </code></pre>\r
18498  */\r
18499 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18500     // private\r
18501     monitorResize:true,\r
18502 \r
18503     // private\r
18504     onLayout : function(ct, target){\r
18505         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);\r
18506         if(!this.container.collapsed){\r
18507             var sz = (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getViewSize() : target.getStyleSize();\r
18508             this.setItemSize(this.activeItem || ct.items.itemAt(0), sz);\r
18509         }\r
18510     },\r
18511 \r
18512     // private\r
18513     setItemSize : function(item, size){\r
18514         if(item && size.height > 0){ // display none?\r
18515             item.setSize(size);\r
18516         }\r
18517     }\r
18518 });\r
18519 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**\r
18520  * @class Ext.layout.CardLayout\r
18521  * @extends Ext.layout.FitLayout\r
18522  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be\r
18523  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.\r
18524  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,\r
18525  * and should generally not need to be created directly via the new keyword.</p>\r
18526  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,\r
18527  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of\r
18528  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,\r
18529  * so that functionality must be provided by the developer.</p>\r
18530  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added\r
18531  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a\r
18532  * common navigation routine -- for this example, the implementation of that routine has been ommitted since\r
18533  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a\r
18534  * completely different implementation.  For serious implementations, a better approach would be to extend\r
18535  * CardLayout to provide the custom functionality needed.  Example usage:</p>\r
18536  * <pre><code>\r
18537 var navHandler = function(direction){\r
18538     // This routine could contain business logic required to manage the navigation steps.\r
18539     // It would call setActiveItem as needed, manage navigation button state, handle any\r
18540     // branching logic that might be required, handle alternate actions like cancellation\r
18541     // or finalization, etc.  A complete wizard implementation could get pretty\r
18542     // sophisticated depending on the complexity required, and should probably be\r
18543     // done as a subclass of CardLayout in a real-world implementation.\r
18544 };\r
18545 \r
18546 var card = new Ext.Panel({\r
18547     title: 'Example Wizard',\r
18548     layout:'card',\r
18549     activeItem: 0, // make sure the active item is set on the container config!\r
18550     bodyStyle: 'padding:15px',\r
18551     defaults: {\r
18552         // applied to each contained panel\r
18553         border:false\r
18554     },\r
18555     // just an example of one possible navigation scheme, using buttons\r
18556     bbar: [\r
18557         {\r
18558             id: 'move-prev',\r
18559             text: 'Back',\r
18560             handler: navHandler.createDelegate(this, [-1]),\r
18561             disabled: true\r
18562         },\r
18563         '->', // greedy spacer so that the buttons are aligned to each side\r
18564         {\r
18565             id: 'move-next',\r
18566             text: 'Next',\r
18567             handler: navHandler.createDelegate(this, [1])\r
18568         }\r
18569     ],\r
18570     // the panels (or "cards") within the layout\r
18571     items: [{\r
18572         id: 'card-0',\r
18573         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'\r
18574     },{\r
18575         id: 'card-1',\r
18576         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'\r
18577     },{\r
18578         id: 'card-2',\r
18579         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'\r
18580     }]\r
18581 });\r
18582 </code></pre>\r
18583  */\r
18584 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {\r
18585     /**\r
18586      * @cfg {Boolean} deferredRender\r
18587      * True to render each contained item at the time it becomes active, false to render all contained items\r
18588      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or\r
18589      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to\r
18590      * true might improve performance.\r
18591      */\r
18592     deferredRender : false,\r
18593     \r
18594     /**\r
18595      * @cfg {Boolean} layoutOnCardChange\r
18596      * True to force a layout of the active item when the active card is changed. Defaults to false.\r
18597      */\r
18598     layoutOnCardChange : false,\r
18599 \r
18600     /**\r
18601      * @cfg {Boolean} renderHidden @hide\r
18602      */\r
18603     // private\r
18604     renderHidden : true,\r
18605     \r
18606     constructor: function(config){\r
18607         Ext.layout.CardLayout.superclass.constructor.call(this, config);\r
18608         this.forceLayout = (this.deferredRender === false);\r
18609     },\r
18610 \r
18611     /**\r
18612      * Sets the active (visible) item in the layout.\r
18613      * @param {String/Number} item The string component id or numeric index of the item to activate\r
18614      */\r
18615     setActiveItem : function(item){\r
18616         item = this.container.getComponent(item);\r
18617         if(this.activeItem != item){\r
18618             if(this.activeItem){\r
18619                 this.activeItem.hide();\r
18620             }\r
18621             var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered);\r
18622             this.activeItem = item;\r
18623             item.show();\r
18624             this.layout();\r
18625             if(layout){\r
18626                 item.doLayout();\r
18627             }\r
18628         }\r
18629     },\r
18630 \r
18631     // private\r
18632     renderAll : function(ct, target){\r
18633         if(this.deferredRender){\r
18634             this.renderItem(this.activeItem, undefined, target);\r
18635         }else{\r
18636             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);\r
18637         }\r
18638     }\r
18639 });\r
18640 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/**\r
18641  * @class Ext.layout.AnchorLayout\r
18642  * @extends Ext.layout.ContainerLayout\r
18643  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.\r
18644  * If the container is resized, all anchored items are automatically rerendered according to their\r
18645  * <b><tt>{@link #anchor}</tt></b> rules.</p>\r
18646  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}\r
18647  * config, and should generally not need to be created directly via the new keyword.</p>\r
18648  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,\r
18649  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the\r
18650  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.\r
18651  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating\r
18652  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring\r
18653  * logic if necessary.  For example:</p>\r
18654  * <pre><code>\r
18655 var viewport = new Ext.Viewport({\r
18656     layout:'anchor',\r
18657     anchorSize: {width:800, height:600},\r
18658     items:[{\r
18659         title:'Item 1',\r
18660         html:'Content 1',\r
18661         width:800,\r
18662         anchor:'right 20%'\r
18663     },{\r
18664         title:'Item 2',\r
18665         html:'Content 2',\r
18666         width:300,\r
18667         anchor:'50% 30%'\r
18668     },{\r
18669         title:'Item 3',\r
18670         html:'Content 3',\r
18671         width:600,\r
18672         anchor:'-100 50%'\r
18673     }]\r
18674 });\r
18675  * </code></pre>\r
18676  */\r
18677 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18678     /**\r
18679      * @cfg {String} anchor\r
18680      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by\r
18681      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>\r
18682      * \r
18683      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>\r
18684      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string\r
18685      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').\r
18686      * The following types of anchor values are supported:<div class="mdetail-params"><ul>\r
18687      * \r
18688      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">\r
18689      * The first anchor is the percentage width that the item should take up within the container, and the\r
18690      * second is the percentage height.  For example:<pre><code>\r
18691 // two values specified\r
18692 anchor: '100% 50%' // render item complete width of the container and\r
18693                    // 1/2 height of the container\r
18694 // one value specified\r
18695 anchor: '100%'     // the width value; the height will default to auto\r
18696      * </code></pre></div></li>\r
18697      * \r
18698      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">\r
18699      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,\r
18700      * and the second is the offset from the bottom edge. For example:<pre><code>\r
18701 // two values specified\r
18702 anchor: '-50 -100' // render item the complete width of the container\r
18703                    // minus 50 pixels and\r
18704                    // the complete height minus 100 pixels.\r
18705 // one value specified\r
18706 anchor: '-50'      // anchor value is assumed to be the right offset value\r
18707                    // bottom offset will default to 0\r
18708      * </code></pre></div></li>\r
18709      * \r
18710      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>\r
18711      * (or <tt>'b'</tt>).<div class="sub-desc">\r
18712      * Either the container must have a fixed size or an anchorSize config value defined at render time in\r
18713      * order for these to have any effect.</div></li>\r
18714      *\r
18715      * <li><b>Mixed</b> : <div class="sub-desc">\r
18716      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container\r
18717      * right edge by 50 pixels and 75% of the container's height use:\r
18718      * <pre><code>\r
18719 anchor: '-50 75%' \r
18720      * </code></pre></div></li>\r
18721      * \r
18722      * \r
18723      * </ul></div>\r
18724      */\r
18725     \r
18726     // private\r
18727     monitorResize:true,\r
18728 \r
18729     // private\r
18730     getAnchorViewSize : function(ct, target){\r
18731         return target.dom == document.body ?\r
18732                    target.getViewSize() : target.getStyleSize();\r
18733     },\r
18734 \r
18735     // private\r
18736     onLayout : function(ct, target){\r
18737         Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target);\r
18738 \r
18739         var size = this.getAnchorViewSize(ct, target);\r
18740 \r
18741         var w = size.width, h = size.height;\r
18742 \r
18743         if(w < 20 && h < 20){\r
18744             return;\r
18745         }\r
18746 \r
18747         // find the container anchoring size\r
18748         var aw, ah;\r
18749         if(ct.anchorSize){\r
18750             if(typeof ct.anchorSize == 'number'){\r
18751                 aw = ct.anchorSize;\r
18752             }else{\r
18753                 aw = ct.anchorSize.width;\r
18754                 ah = ct.anchorSize.height;\r
18755             }\r
18756         }else{\r
18757             aw = ct.initialConfig.width;\r
18758             ah = ct.initialConfig.height;\r
18759         }\r
18760 \r
18761         var cs = ct.items.items, len = cs.length, i, c, a, cw, ch;\r
18762         for(i = 0; i < len; i++){\r
18763             c = cs[i];\r
18764             if(c.anchor){\r
18765                 a = c.anchorSpec;\r
18766                 if(!a){ // cache all anchor values\r
18767                     var vs = c.anchor.split(' ');\r
18768                     c.anchorSpec = a = {\r
18769                         right: this.parseAnchor(vs[0], c.initialConfig.width, aw),\r
18770                         bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah)\r
18771                     };\r
18772                 }\r
18773                 cw = a.right ? this.adjustWidthAnchor(a.right(w), c) : undefined;\r
18774                 ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h), c) : undefined;\r
18775 \r
18776                 if(cw || ch){\r
18777                     c.setSize(cw || undefined, ch || undefined);\r
18778                 }\r
18779             }\r
18780         }\r
18781     },\r
18782 \r
18783     // private\r
18784     parseAnchor : function(a, start, cstart){\r
18785         if(a && a != 'none'){\r
18786             var last;\r
18787             if(/^(r|right|b|bottom)$/i.test(a)){   // standard anchor\r
18788                 var diff = cstart - start;\r
18789                 return function(v){\r
18790                     if(v !== last){\r
18791                         last = v;\r
18792                         return v - diff;\r
18793                     }\r
18794                 }\r
18795             }else if(a.indexOf('%') != -1){\r
18796                 var ratio = parseFloat(a.replace('%', ''))*.01;   // percentage\r
18797                 return function(v){\r
18798                     if(v !== last){\r
18799                         last = v;\r
18800                         return Math.floor(v*ratio);\r
18801                     }\r
18802                 }\r
18803             }else{\r
18804                 a = parseInt(a, 10);\r
18805                 if(!isNaN(a)){                            // simple offset adjustment\r
18806                     return function(v){\r
18807                         if(v !== last){\r
18808                             last = v;\r
18809                             return v + a;\r
18810                         }\r
18811                     }\r
18812                 }\r
18813             }\r
18814         }\r
18815         return false;\r
18816     },\r
18817 \r
18818     // private\r
18819     adjustWidthAnchor : function(value, comp){\r
18820         return value;\r
18821     },\r
18822 \r
18823     // private\r
18824     adjustHeightAnchor : function(value, comp){\r
18825         return value;\r
18826     }\r
18827     \r
18828     /**\r
18829      * @property activeItem\r
18830      * @hide\r
18831      */\r
18832 });\r
18833 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;/**\r
18834  * @class Ext.layout.ColumnLayout\r
18835  * @extends Ext.layout.ContainerLayout\r
18836  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of\r
18837  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.\r
18838  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,\r
18839  * and should generally not need to be created directly via the new keyword.</p>\r
18840  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a\r
18841  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The\r
18842  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.\r
18843  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>\r
18844  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.\r
18845  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and\r
18846  * less than 1 (e.g., .25).</p>\r
18847  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the\r
18848  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none\r
18849  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second\r
18850  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on\r
18851  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space\r
18852  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns\r
18853  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your\r
18854  * layout may not render as expected.  Example usage:</p>\r
18855  * <pre><code>\r
18856 // All columns are percentages -- they must add up to 1\r
18857 var p = new Ext.Panel({\r
18858     title: 'Column Layout - Percentage Only',\r
18859     layout:'column',\r
18860     items: [{\r
18861         title: 'Column 1',\r
18862         columnWidth: .25 \r
18863     },{\r
18864         title: 'Column 2',\r
18865         columnWidth: .6\r
18866     },{\r
18867         title: 'Column 3',\r
18868         columnWidth: .15\r
18869     }]\r
18870 });\r
18871 \r
18872 // Mix of width and columnWidth -- all columnWidth values must add up\r
18873 // to 1. The first column will take up exactly 120px, and the last two\r
18874 // columns will fill the remaining container width.\r
18875 var p = new Ext.Panel({\r
18876     title: 'Column Layout - Mixed',\r
18877     layout:'column',\r
18878     items: [{\r
18879         title: 'Column 1',\r
18880         width: 120\r
18881     },{\r
18882         title: 'Column 2',\r
18883         columnWidth: .8\r
18884     },{\r
18885         title: 'Column 3',\r
18886         columnWidth: .2\r
18887     }]\r
18888 });\r
18889 </code></pre>\r
18890  */\r
18891 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
18892     // private\r
18893     monitorResize:true,\r
18894     \r
18895     extraCls: 'x-column',\r
18896 \r
18897     scrollOffset : 0,\r
18898 \r
18899     // private\r
18900     isValidParent : function(c, target){\r
18901         return (c.getPositionEl ? c.getPositionEl() : c.getEl()).dom.parentNode == this.innerCt.dom;\r
18902     },\r
18903 \r
18904     // private\r
18905     onLayout : function(ct, target){\r
18906         var cs = ct.items.items, len = cs.length, c, i;\r
18907 \r
18908         if(!this.innerCt){\r
18909             target.addClass('x-column-layout-ct');\r
18910 \r
18911             // the innerCt prevents wrapping and shuffling while\r
18912             // the container is resizing\r
18913             this.innerCt = target.createChild({cls:'x-column-inner'});\r
18914             this.innerCt.createChild({cls:'x-clear'});\r
18915         }\r
18916         this.renderAll(ct, this.innerCt);\r
18917 \r
18918         var size = Ext.isIE && target.dom != Ext.getBody().dom ? target.getStyleSize() : target.getViewSize();\r
18919 \r
18920         if(size.width < 1 && size.height < 1){ // display none?\r
18921             return;\r
18922         }\r
18923 \r
18924         var w = size.width - target.getPadding('lr') - this.scrollOffset,\r
18925             h = size.height - target.getPadding('tb'),\r
18926             pw = w;\r
18927 \r
18928         this.innerCt.setWidth(w);\r
18929         \r
18930         // some columns can be percentages while others are fixed\r
18931         // so we need to make 2 passes\r
18932 \r
18933         for(i = 0; i < len; i++){\r
18934             c = cs[i];\r
18935             if(!c.columnWidth){\r
18936                 pw -= (c.getSize().width + c.getEl().getMargins('lr'));\r
18937             }\r
18938         }\r
18939 \r
18940         pw = pw < 0 ? 0 : pw;\r
18941 \r
18942         for(i = 0; i < len; i++){\r
18943             c = cs[i];\r
18944             if(c.columnWidth){\r
18945                 c.setSize(Math.floor(c.columnWidth*pw) - c.getEl().getMargins('lr'));\r
18946             }\r
18947         }\r
18948     }\r
18949     \r
18950     /**\r
18951      * @property activeItem\r
18952      * @hide\r
18953      */\r
18954 });\r
18955 \r
18956 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;/**
18957  * @class Ext.layout.BorderLayout
18958  * @extends Ext.layout.ContainerLayout
18959  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
18960  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
18961  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
18962  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
18963  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
18964  * {@link Ext.Container#layout} config, and should generally not need to be created directly
18965  * via the new keyword.</p>
18966  * <p>BorderLayout does not have any direct config options (other than inherited ones).
18967  * All configuration options available for customizing the BorderLayout are at the
18968  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
18969  * levels.</p>
18970  * <p>Example usage:</p>
18971  * <pre><code>
18972 var myBorderPanel = new Ext.Panel({
18973     {@link Ext.Component#renderTo renderTo}: document.body,
18974     {@link Ext.BoxComponent#width width}: 700,
18975     {@link Ext.BoxComponent#height height}: 500,
18976     {@link Ext.Panel#title title}: 'Border Layout',
18977     {@link Ext.Container#layout layout}: 'border',
18978     {@link Ext.Container#items items}: [{
18979         {@link Ext.Panel#title title}: 'South Region is resizable',
18980         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
18981         {@link Ext.BoxComponent#height height}: 100,
18982         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
18983         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} 
18984         {@link Ext.SplitBar#maxSize maxSize}: 150,
18985         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
18986     },{
18987         // xtype: 'panel' implied by default
18988         {@link Ext.Panel#title title}: 'West Region is collapsible',
18989         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
18990         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
18991         {@link Ext.BoxComponent#width width}: 200,
18992         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
18993         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
18994         {@link Ext.Component#id id}: 'west-region-container',
18995         {@link Ext.Container#layout layout}: 'fit',
18996         {@link Ext.Panel#unstyled unstyled}: true
18997     },{
18998         {@link Ext.Panel#title title}: 'Center Region',
18999         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
19000         {@link Ext.Component#xtype xtype}: 'container',
19001         {@link Ext.Container#layout layout}: 'fit',
19002         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
19003     }]
19004 });
19005 </code></pre>
19006  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
19007  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
19008  * The child item in the center region will always be resized to fill the remaining space not used by
19009  * the other regions in the layout.</li>
19010  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
19011  * (an integer representing the number of pixels that the region should take up).</li>
19012  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
19013  * <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
19014  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
19015  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
19016  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
19017  * is added to the west region:
19018  * <div style="margin-left:16px"><pre><code>
19019 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
19020 wrc.{@link Ext.Panel#removeAll removeAll}();
19021 wrc.{@link Ext.Container#add add}({
19022     title: 'Added Panel',
19023     html: 'Some content'
19024 });
19025 wrc.{@link Ext.Container#doLayout doLayout}();
19026  * </code></pre></div>
19027  * </li>
19028  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
19029  * <div style="margin-left:16px"><pre><code>
19030 wr = myBorderPanel.layout.west;
19031  * </code></pre></div>
19032  * </li>
19033  * </ul></div>
19034  */
19035 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
19036     // private
19037     monitorResize:true,
19038     // private
19039     rendered : false,
19040
19041     // private
19042     onLayout : function(ct, target){
19043         var collapsed;
19044         if(!this.rendered){
19045             target.addClass('x-border-layout-ct');
19046             var items = ct.items.items;
19047             collapsed = [];
19048             for(var i = 0, len = items.length; i < len; i++) {
19049                 var c = items[i];
19050                 var pos = c.region;
19051                 if(c.collapsed){
19052                     collapsed.push(c);
19053                 }
19054                 c.collapsed = false;
19055                 if(!c.rendered){
19056                     c.render(target, i);
19057                     c.getDomPositionEl().addClass('x-border-panel');
19058                 }
19059                 this[pos] = pos != 'center' && c.split ?
19060                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
19061                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
19062                 this[pos].render(target, c);
19063             }
19064             this.rendered = true;
19065         }
19066
19067         var size = target.getViewSize();
19068         if(size.width < 20 || size.height < 20){ // display none?
19069             if(collapsed){
19070                 this.restoreCollapsed = collapsed;
19071             }
19072             return;
19073         }else if(this.restoreCollapsed){
19074             collapsed = this.restoreCollapsed;
19075             delete this.restoreCollapsed;
19076         }
19077
19078         var w = size.width, h = size.height;
19079         var centerW = w, centerH = h, centerY = 0, centerX = 0;
19080
19081         var n = this.north, s = this.south, west = this.west, e = this.east, c = this.center;
19082         if(!c && Ext.layout.BorderLayout.WARN !== false){
19083             throw 'No center region defined in BorderLayout ' + ct.id;
19084         }
19085
19086         if(n && n.isVisible()){
19087             var b = n.getSize();
19088             var m = n.getMargins();
19089             b.width = w - (m.left+m.right);
19090             b.x = m.left;
19091             b.y = m.top;
19092             centerY = b.height + b.y + m.bottom;
19093             centerH -= centerY;
19094             n.applyLayout(b);
19095         }
19096         if(s && s.isVisible()){
19097             var b = s.getSize();
19098             var m = s.getMargins();
19099             b.width = w - (m.left+m.right);
19100             b.x = m.left;
19101             var totalHeight = (b.height + m.top + m.bottom);
19102             b.y = h - totalHeight + m.top;
19103             centerH -= totalHeight;
19104             s.applyLayout(b);
19105         }
19106         if(west && west.isVisible()){
19107             var b = west.getSize();
19108             var m = west.getMargins();
19109             b.height = centerH - (m.top+m.bottom);
19110             b.x = m.left;
19111             b.y = centerY + m.top;
19112             var totalWidth = (b.width + m.left + m.right);
19113             centerX += totalWidth;
19114             centerW -= totalWidth;
19115             west.applyLayout(b);
19116         }
19117         if(e && e.isVisible()){
19118             var b = e.getSize();
19119             var m = e.getMargins();
19120             b.height = centerH - (m.top+m.bottom);
19121             var totalWidth = (b.width + m.left + m.right);
19122             b.x = w - totalWidth + m.left;
19123             b.y = centerY + m.top;
19124             centerW -= totalWidth;
19125             e.applyLayout(b);
19126         }
19127         if(c){
19128             var m = c.getMargins();
19129             var centerBox = {
19130                 x: centerX + m.left,
19131                 y: centerY + m.top,
19132                 width: centerW - (m.left+m.right),
19133                 height: centerH - (m.top+m.bottom)
19134             };
19135             c.applyLayout(centerBox);
19136         }
19137         if(collapsed){
19138             for(var i = 0, len = collapsed.length; i < len; i++){
19139                 collapsed[i].collapse(false);
19140             }
19141         }
19142         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
19143             target.repaint();
19144         }
19145     },
19146
19147     destroy: function() {
19148         var r = ['north', 'south', 'east', 'west'];
19149         for (var i = 0; i < r.length; i++) {
19150             var region = this[r[i]];
19151             if(region){
19152                 if(region.destroy){
19153                     region.destroy();
19154                 }else if (region.split){
19155                     region.split.destroy(true);
19156                 }
19157             }
19158         }
19159         Ext.layout.BorderLayout.superclass.destroy.call(this);
19160     }
19161
19162     /**
19163      * @property activeItem
19164      * @hide
19165      */
19166 });
19167
19168 /**
19169  * @class Ext.layout.BorderLayout.Region
19170  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
19171  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
19172  * independent of other regions and the containing BorderLayout, and can be any of the
19173  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
19174  * <p>Region size is managed automatically and cannot be changed by the user -- for
19175  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
19176  * @constructor
19177  * Create a new Region.
19178  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
19179  * @param {Object} config The configuration options
19180  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
19181  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
19182  * <b>must have a center region</b> for the primary content -- all other regions are optional.
19183  */
19184 Ext.layout.BorderLayout.Region = function(layout, config, pos){
19185     Ext.apply(this, config);
19186     this.layout = layout;
19187     this.position = pos;
19188     this.state = {};
19189     if(typeof this.margins == 'string'){
19190         this.margins = this.layout.parseMargins(this.margins);
19191     }
19192     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
19193     if(this.collapsible){
19194         if(typeof this.cmargins == 'string'){
19195             this.cmargins = this.layout.parseMargins(this.cmargins);
19196         }
19197         if(this.collapseMode == 'mini' && !this.cmargins){
19198             this.cmargins = {left:0,top:0,right:0,bottom:0};
19199         }else{
19200             this.cmargins = Ext.applyIf(this.cmargins || {},
19201                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
19202         }
19203     }
19204 };
19205
19206 Ext.layout.BorderLayout.Region.prototype = {
19207     /**
19208      * @cfg {Boolean} animFloat
19209      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
19210      * panel that will close again once the user mouses out of that panel (or clicks out if
19211      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
19212      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
19213      */
19214     /**
19215      * @cfg {Boolean} autoHide
19216      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
19217      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
19218      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
19219      * user clicks outside of the panel (defaults to <tt>true</tt>).
19220      */
19221     /**
19222      * @cfg {String} collapseMode
19223      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
19224      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
19225      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
19226      * title bar.</div></li>
19227      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
19228      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
19229      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
19230      * </div></li>
19231      * </ul></div></p>
19232      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
19233      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
19234      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
19235      * <p>See also <tt>{@link #cmargins}</tt>.</p>
19236      */
19237     /**
19238      * @cfg {Object} margins
19239      * An object containing margins to apply to the region when in the expanded state in the
19240      * format:<pre><code>
19241 {
19242     top: (top margin),
19243     right: (right margin),
19244     bottom: (bottom margin),
19245     left: (left margin)
19246 }</code></pre>
19247      * <p>May also be a string containing space-separated, numeric margin values. The order of the
19248      * sides associated with each value matches the way CSS processes margin values:</p>
19249      * <p><div class="mdetail-params"><ul>
19250      * <li>If there is only one value, it applies to all sides.</li>
19251      * <li>If there are two values, the top and bottom borders are set to the first value and the
19252      * right and left are set to the second.</li>
19253      * <li>If there are three values, the top is set to the first value, the left and right are set
19254      * to the second, and the bottom is set to the third.</li>
19255      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
19256      * </ul></div></p>
19257      * <p>Defaults to:</p><pre><code>
19258      * {top:0, right:0, bottom:0, left:0}
19259      * </code></pre>
19260      */
19261     /**
19262      * @cfg {Object} cmargins
19263      * An object containing margins to apply to the region when in the collapsed state in the
19264      * format:<pre><code>
19265 {
19266     top: (top margin),
19267     right: (right margin),
19268     bottom: (bottom margin),
19269     left: (left margin)
19270 }</code></pre>
19271      * <p>May also be a string containing space-separated, numeric margin values. The order of the
19272      * sides associated with each value matches the way CSS processes margin values.</p>
19273      * <p><ul>
19274      * <li>If there is only one value, it applies to all sides.</li>
19275      * <li>If there are two values, the top and bottom borders are set to the first value and the
19276      * right and left are set to the second.</li>
19277      * <li>If there are three values, the top is set to the first value, the left and right are set
19278      * to the second, and the bottom is set to the third.</li>
19279      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
19280      * </ul></p>
19281      */
19282     /**
19283      * @cfg {Boolean} collapsible
19284      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
19285      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
19286      * bar of the region, otherwise the button will not be shown.</p>
19287      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
19288      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
19289      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
19290      */
19291     collapsible : false,
19292     /**
19293      * @cfg {Boolean} split
19294      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and 
19295      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
19296      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
19297      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
19298      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
19299      * <li>this configuration option is ignored if <tt>region='center'</tt></li> 
19300      * <li>when <tt>split == true</tt>, it is common to specify a
19301      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
19302      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
19303      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
19304      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
19305      * for the collapse tool</tt></li> 
19306      * </ul></div> 
19307      */
19308     split:false,
19309     /**
19310      * @cfg {Boolean} floatable
19311      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
19312      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
19313      * clicking the expand button to see it again (defaults to <tt>true</tt>).
19314      */
19315     floatable: true,
19316     /**
19317      * @cfg {Number} minWidth
19318      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
19319      * <tt>maxWidth</tt> may also be specified.</p><br>
19320      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> / 
19321      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified 
19322      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
19323      */
19324     minWidth:50,
19325     /**
19326      * @cfg {Number} minHeight
19327      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
19328      * <tt>maxHeight</tt> may also be specified.</p><br>
19329      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> / 
19330      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified 
19331      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
19332      */
19333     minHeight:50,
19334
19335     // private
19336     defaultMargins : {left:0,top:0,right:0,bottom:0},
19337     // private
19338     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
19339     // private
19340     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
19341     floatingZIndex: 100,
19342
19343     /**
19344      * True if this region is collapsed. Read-only.
19345      * @type Boolean
19346      * @property
19347      */
19348     isCollapsed : false,
19349
19350     /**
19351      * This region's panel.  Read-only.
19352      * @type Ext.Panel
19353      * @property panel
19354      */
19355     /**
19356      * This region's layout.  Read-only.
19357      * @type Layout
19358      * @property layout
19359      */
19360     /**
19361      * This region's layout position (north, south, east, west or center).  Read-only.
19362      * @type String
19363      * @property position
19364      */
19365
19366     // private
19367     render : function(ct, p){
19368         this.panel = p;
19369         p.el.enableDisplayMode();
19370         this.targetEl = ct;
19371         this.el = p.el;
19372
19373         var gs = p.getState, ps = this.position;
19374         p.getState = function(){
19375             return Ext.apply(gs.call(p) || {}, this.state);
19376         }.createDelegate(this);
19377
19378         if(ps != 'center'){
19379             p.allowQueuedExpand = false;
19380             p.on({
19381                 beforecollapse: this.beforeCollapse,
19382                 collapse: this.onCollapse,
19383                 beforeexpand: this.beforeExpand,
19384                 expand: this.onExpand,
19385                 hide: this.onHide,
19386                 show: this.onShow,
19387                 scope: this
19388             });
19389             if(this.collapsible || this.floatable){
19390                 p.collapseEl = 'el';
19391                 p.slideAnchor = this.getSlideAnchor();
19392             }
19393             if(p.tools && p.tools.toggle){
19394                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
19395                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
19396             }
19397         }
19398     },
19399
19400     // private
19401     getCollapsedEl : function(){
19402         if(!this.collapsedEl){
19403             if(!this.toolTemplate){
19404                 var tt = new Ext.Template(
19405                      '<div class="x-tool x-tool-{id}">&#160;</div>'
19406                 );
19407                 tt.disableFormats = true;
19408                 tt.compile();
19409                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
19410             }
19411             this.collapsedEl = this.targetEl.createChild({
19412                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
19413                 id: this.panel.id + '-xcollapsed'
19414             });
19415             this.collapsedEl.enableDisplayMode('block');
19416
19417             if(this.collapseMode == 'mini'){
19418                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
19419                 this.miniCollapsedEl = this.collapsedEl.createChild({
19420                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
19421                 });
19422                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
19423                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
19424                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
19425             }else {
19426                 if(this.collapsible !== false && !this.hideCollapseTool) {
19427                     var t = this.toolTemplate.append(
19428                             this.collapsedEl.dom,
19429                             {id:'expand-'+this.position}, true);
19430                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
19431                     t.on('click', this.onExpandClick, this, {stopEvent:true});
19432                 }
19433                 if(this.floatable !== false || this.titleCollapse){
19434                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
19435                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
19436                 }
19437             }
19438         }
19439         return this.collapsedEl;
19440     },
19441
19442     // private
19443     onExpandClick : function(e){
19444         if(this.isSlid){
19445             this.afterSlideIn();
19446             this.panel.expand(false);
19447         }else{
19448             this.panel.expand();
19449         }
19450     },
19451
19452     // private
19453     onCollapseClick : function(e){
19454         this.panel.collapse();
19455     },
19456
19457     // private
19458     beforeCollapse : function(p, animate){
19459         this.lastAnim = animate;
19460         if(this.splitEl){
19461             this.splitEl.hide();
19462         }
19463         this.getCollapsedEl().show();
19464         this.panel.el.setStyle('z-index', 100);
19465         this.isCollapsed = true;
19466         this.layout.layout();
19467     },
19468
19469     // private
19470     onCollapse : function(animate){
19471         this.panel.el.setStyle('z-index', 1);
19472         if(this.lastAnim === false || this.panel.animCollapse === false){
19473             this.getCollapsedEl().dom.style.visibility = 'visible';
19474         }else{
19475             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
19476         }
19477         this.state.collapsed = true;
19478         this.panel.saveState();
19479     },
19480
19481     // private
19482     beforeExpand : function(animate){
19483         var c = this.getCollapsedEl();
19484         this.el.show();
19485         if(this.position == 'east' || this.position == 'west'){
19486             this.panel.setSize(undefined, c.getHeight());
19487         }else{
19488             this.panel.setSize(c.getWidth(), undefined);
19489         }
19490         c.hide();
19491         c.dom.style.visibility = 'hidden';
19492         this.panel.el.setStyle('z-index', this.floatingZIndex);
19493     },
19494
19495     // private
19496     onExpand : function(){
19497         this.isCollapsed = false;
19498         if(this.splitEl){
19499             this.splitEl.show();
19500         }
19501         this.layout.layout();
19502         this.panel.el.setStyle('z-index', 1);
19503         this.state.collapsed = false;
19504         this.panel.saveState();
19505     },
19506
19507     // private
19508     collapseClick : function(e){
19509         if(this.isSlid){
19510            e.stopPropagation();
19511            this.slideIn();
19512         }else{
19513            e.stopPropagation();
19514            this.slideOut();
19515         }
19516     },
19517
19518     // private
19519     onHide : function(){
19520         if(this.isCollapsed){
19521             this.getCollapsedEl().hide();
19522         }else if(this.splitEl){
19523             this.splitEl.hide();
19524         }
19525     },
19526
19527     // private
19528     onShow : function(){
19529         if(this.isCollapsed){
19530             this.getCollapsedEl().show();
19531         }else if(this.splitEl){
19532             this.splitEl.show();
19533         }
19534     },
19535
19536     /**
19537      * True if this region is currently visible, else false.
19538      * @return {Boolean}
19539      */
19540     isVisible : function(){
19541         return !this.panel.hidden;
19542     },
19543
19544     /**
19545      * Returns the current margins for this region.  If the region is collapsed, the
19546      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
19547      * {@link #margins} value will be returned.
19548      * @return {Object} An object containing the element's margins: <tt>{left: (left
19549      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
19550      */
19551     getMargins : function(){
19552         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
19553     },
19554
19555     /**
19556      * Returns the current size of this region.  If the region is collapsed, the size of the
19557      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
19558      * @return {Object} An object containing the element's size: <tt>{width: (element width),
19559      * height: (element height)}</tt>
19560      */
19561     getSize : function(){
19562         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
19563     },
19564
19565     /**
19566      * Sets the specified panel as the container element for this region.
19567      * @param {Ext.Panel} panel The new panel
19568      */
19569     setPanel : function(panel){
19570         this.panel = panel;
19571     },
19572
19573     /**
19574      * Returns the minimum allowable width for this region.
19575      * @return {Number} The minimum width
19576      */
19577     getMinWidth: function(){
19578         return this.minWidth;
19579     },
19580
19581     /**
19582      * Returns the minimum allowable height for this region.
19583      * @return {Number} The minimum height
19584      */
19585     getMinHeight: function(){
19586         return this.minHeight;
19587     },
19588
19589     // private
19590     applyLayoutCollapsed : function(box){
19591         var ce = this.getCollapsedEl();
19592         ce.setLeftTop(box.x, box.y);
19593         ce.setSize(box.width, box.height);
19594     },
19595
19596     // private
19597     applyLayout : function(box){
19598         if(this.isCollapsed){
19599             this.applyLayoutCollapsed(box);
19600         }else{
19601             this.panel.setPosition(box.x, box.y);
19602             this.panel.setSize(box.width, box.height);
19603         }
19604     },
19605
19606     // private
19607     beforeSlide: function(){
19608         this.panel.beforeEffect();
19609     },
19610
19611     // private
19612     afterSlide : function(){
19613         this.panel.afterEffect();
19614     },
19615
19616     // private
19617     initAutoHide : function(){
19618         if(this.autoHide !== false){
19619             if(!this.autoHideHd){
19620                 var st = new Ext.util.DelayedTask(this.slideIn, this);
19621                 this.autoHideHd = {
19622                     "mouseout": function(e){
19623                         if(!e.within(this.el, true)){
19624                             st.delay(500);
19625                         }
19626                     },
19627                     "mouseover" : function(e){
19628                         st.cancel();
19629                     },
19630                     scope : this
19631                 };
19632             }
19633             this.el.on(this.autoHideHd);
19634         }
19635     },
19636
19637     // private
19638     clearAutoHide : function(){
19639         if(this.autoHide !== false){
19640             this.el.un("mouseout", this.autoHideHd.mouseout);
19641             this.el.un("mouseover", this.autoHideHd.mouseover);
19642         }
19643     },
19644
19645     // private
19646     clearMonitor : function(){
19647         Ext.getDoc().un("click", this.slideInIf, this);
19648     },
19649
19650     /**
19651      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
19652      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
19653      * are clicked, or the mouse exits the Region.
19654      */
19655     slideOut : function(){
19656         if(this.isSlid || this.el.hasActiveFx()){
19657             return;
19658         }
19659         this.isSlid = true;
19660         var ts = this.panel.tools;
19661         if(ts && ts.toggle){
19662             ts.toggle.hide();
19663         }
19664         this.el.show();
19665         if(this.position == 'east' || this.position == 'west'){
19666             this.panel.setSize(undefined, this.collapsedEl.getHeight());
19667         }else{
19668             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
19669         }
19670         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
19671         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
19672         this.el.setStyle("z-index", this.floatingZIndex+2);
19673         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
19674         if(this.animFloat !== false){
19675             this.beforeSlide();
19676             this.el.slideIn(this.getSlideAnchor(), {
19677                 callback: function(){
19678                     this.afterSlide();
19679                     this.initAutoHide();
19680                     Ext.getDoc().on("click", this.slideInIf, this);
19681                 },
19682                 scope: this,
19683                 block: true
19684             });
19685         }else{
19686             this.initAutoHide();
19687              Ext.getDoc().on("click", this.slideInIf, this);
19688         }
19689     },
19690
19691     // private
19692     afterSlideIn : function(){
19693         this.clearAutoHide();
19694         this.isSlid = false;
19695         this.clearMonitor();
19696         this.el.setStyle("z-index", "");
19697         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
19698         this.el.dom.style.left = this.restoreLT[0];
19699         this.el.dom.style.top = this.restoreLT[1];
19700
19701         var ts = this.panel.tools;
19702         if(ts && ts.toggle){
19703             ts.toggle.show();
19704         }
19705     },
19706
19707     /**
19708      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
19709      * this region back into its collapsed state.
19710      */
19711     slideIn : function(cb){
19712         if(!this.isSlid || this.el.hasActiveFx()){
19713             Ext.callback(cb);
19714             return;
19715         }
19716         this.isSlid = false;
19717         if(this.animFloat !== false){
19718             this.beforeSlide();
19719             this.el.slideOut(this.getSlideAnchor(), {
19720                 callback: function(){
19721                     this.el.hide();
19722                     this.afterSlide();
19723                     this.afterSlideIn();
19724                     Ext.callback(cb);
19725                 },
19726                 scope: this,
19727                 block: true
19728             });
19729         }else{
19730             this.el.hide();
19731             this.afterSlideIn();
19732         }
19733     },
19734
19735     // private
19736     slideInIf : function(e){
19737         if(!e.within(this.el)){
19738             this.slideIn();
19739         }
19740     },
19741
19742     // private
19743     anchors : {
19744         "west" : "left",
19745         "east" : "right",
19746         "north" : "top",
19747         "south" : "bottom"
19748     },
19749
19750     // private
19751     sanchors : {
19752         "west" : "l",
19753         "east" : "r",
19754         "north" : "t",
19755         "south" : "b"
19756     },
19757
19758     // private
19759     canchors : {
19760         "west" : "tl-tr",
19761         "east" : "tr-tl",
19762         "north" : "tl-bl",
19763         "south" : "bl-tl"
19764     },
19765
19766     // private
19767     getAnchor : function(){
19768         return this.anchors[this.position];
19769     },
19770
19771     // private
19772     getCollapseAnchor : function(){
19773         return this.canchors[this.position];
19774     },
19775
19776     // private
19777     getSlideAnchor : function(){
19778         return this.sanchors[this.position];
19779     },
19780
19781     // private
19782     getAlignAdj : function(){
19783         var cm = this.cmargins;
19784         switch(this.position){
19785             case "west":
19786                 return [0, 0];
19787             break;
19788             case "east":
19789                 return [0, 0];
19790             break;
19791             case "north":
19792                 return [0, 0];
19793             break;
19794             case "south":
19795                 return [0, 0];
19796             break;
19797         }
19798     },
19799
19800     // private
19801     getExpandAdj : function(){
19802         var c = this.collapsedEl, cm = this.cmargins;
19803         switch(this.position){
19804             case "west":
19805                 return [-(cm.right+c.getWidth()+cm.left), 0];
19806             break;
19807             case "east":
19808                 return [cm.right+c.getWidth()+cm.left, 0];
19809             break;
19810             case "north":
19811                 return [0, -(cm.top+cm.bottom+c.getHeight())];
19812             break;
19813             case "south":
19814                 return [0, cm.top+cm.bottom+c.getHeight()];
19815             break;
19816         }
19817     }
19818 };
19819
19820 /**
19821  * @class Ext.layout.BorderLayout.SplitRegion
19822  * @extends Ext.layout.BorderLayout.Region
19823  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
19824  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
19825  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
19826  * @constructor
19827  * Create a new SplitRegion.
19828  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
19829  * @param {Object} config The configuration options
19830  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
19831  * BorderLayout must have a center region for the primary content -- all other regions are optional.
19832  */
19833 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
19834     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
19835     // prevent switch
19836     this.applyLayout = this.applyFns[pos];
19837 };
19838
19839 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
19840     /**
19841      * @cfg {Number} tickSize
19842      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
19843      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
19844      */
19845     /**
19846      * @cfg {String} splitTip
19847      * The tooltip to display when the user hovers over a
19848      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
19849      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
19850      * <tt>{@link #useSplitTips} = true</tt>.
19851      */
19852     splitTip : "Drag to resize.",
19853     /**
19854      * @cfg {String} collapsibleSplitTip
19855      * The tooltip to display when the user hovers over a
19856      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
19857      * (defaults to "Drag to resize. Double click to hide."). Only applies if
19858      * <tt>{@link #useSplitTips} = true</tt>.
19859      */
19860     collapsibleSplitTip : "Drag to resize. Double click to hide.",
19861     /**
19862      * @cfg {Boolean} useSplitTips
19863      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
19864      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
19865      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
19866      */
19867     useSplitTips : false,
19868
19869     // private
19870     splitSettings : {
19871         north : {
19872             orientation: Ext.SplitBar.VERTICAL,
19873             placement: Ext.SplitBar.TOP,
19874             maxFn : 'getVMaxSize',
19875             minProp: 'minHeight',
19876             maxProp: 'maxHeight'
19877         },
19878         south : {
19879             orientation: Ext.SplitBar.VERTICAL,
19880             placement: Ext.SplitBar.BOTTOM,
19881             maxFn : 'getVMaxSize',
19882             minProp: 'minHeight',
19883             maxProp: 'maxHeight'
19884         },
19885         east : {
19886             orientation: Ext.SplitBar.HORIZONTAL,
19887             placement: Ext.SplitBar.RIGHT,
19888             maxFn : 'getHMaxSize',
19889             minProp: 'minWidth',
19890             maxProp: 'maxWidth'
19891         },
19892         west : {
19893             orientation: Ext.SplitBar.HORIZONTAL,
19894             placement: Ext.SplitBar.LEFT,
19895             maxFn : 'getHMaxSize',
19896             minProp: 'minWidth',
19897             maxProp: 'maxWidth'
19898         }
19899     },
19900
19901     // private
19902     applyFns : {
19903         west : function(box){
19904             if(this.isCollapsed){
19905                 return this.applyLayoutCollapsed(box);
19906             }
19907             var sd = this.splitEl.dom, s = sd.style;
19908             this.panel.setPosition(box.x, box.y);
19909             var sw = sd.offsetWidth;
19910             s.left = (box.x+box.width-sw)+'px';
19911             s.top = (box.y)+'px';
19912             s.height = Math.max(0, box.height)+'px';
19913             this.panel.setSize(box.width-sw, box.height);
19914         },
19915         east : function(box){
19916             if(this.isCollapsed){
19917                 return this.applyLayoutCollapsed(box);
19918             }
19919             var sd = this.splitEl.dom, s = sd.style;
19920             var sw = sd.offsetWidth;
19921             this.panel.setPosition(box.x+sw, box.y);
19922             s.left = (box.x)+'px';
19923             s.top = (box.y)+'px';
19924             s.height = Math.max(0, box.height)+'px';
19925             this.panel.setSize(box.width-sw, box.height);
19926         },
19927         north : function(box){
19928             if(this.isCollapsed){
19929                 return this.applyLayoutCollapsed(box);
19930             }
19931             var sd = this.splitEl.dom, s = sd.style;
19932             var sh = sd.offsetHeight;
19933             this.panel.setPosition(box.x, box.y);
19934             s.left = (box.x)+'px';
19935             s.top = (box.y+box.height-sh)+'px';
19936             s.width = Math.max(0, box.width)+'px';
19937             this.panel.setSize(box.width, box.height-sh);
19938         },
19939         south : function(box){
19940             if(this.isCollapsed){
19941                 return this.applyLayoutCollapsed(box);
19942             }
19943             var sd = this.splitEl.dom, s = sd.style;
19944             var sh = sd.offsetHeight;
19945             this.panel.setPosition(box.x, box.y+sh);
19946             s.left = (box.x)+'px';
19947             s.top = (box.y)+'px';
19948             s.width = Math.max(0, box.width)+'px';
19949             this.panel.setSize(box.width, box.height-sh);
19950         }
19951     },
19952
19953     // private
19954     render : function(ct, p){
19955         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
19956
19957         var ps = this.position;
19958
19959         this.splitEl = ct.createChild({
19960             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
19961             id: this.panel.id + '-xsplit'
19962         });
19963
19964         if(this.collapseMode == 'mini'){
19965             this.miniSplitEl = this.splitEl.createChild({
19966                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
19967             });
19968             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
19969             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
19970         }
19971
19972         var s = this.splitSettings[ps];
19973
19974         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
19975         this.split.tickSize = this.tickSize;
19976         this.split.placement = s.placement;
19977         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
19978         this.split.minSize = this.minSize || this[s.minProp];
19979         this.split.on("beforeapply", this.onSplitMove, this);
19980         this.split.useShim = this.useShim === true;
19981         this.maxSize = this.maxSize || this[s.maxProp];
19982
19983         if(p.hidden){
19984             this.splitEl.hide();
19985         }
19986
19987         if(this.useSplitTips){
19988             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
19989         }
19990         if(this.collapsible){
19991             this.splitEl.on("dblclick", this.onCollapseClick,  this);
19992         }
19993     },
19994
19995     //docs inherit from superclass
19996     getSize : function(){
19997         if(this.isCollapsed){
19998             return this.collapsedEl.getSize();
19999         }
20000         var s = this.panel.getSize();
20001         if(this.position == 'north' || this.position == 'south'){
20002             s.height += this.splitEl.dom.offsetHeight;
20003         }else{
20004             s.width += this.splitEl.dom.offsetWidth;
20005         }
20006         return s;
20007     },
20008
20009     // private
20010     getHMaxSize : function(){
20011          var cmax = this.maxSize || 10000;
20012          var center = this.layout.center;
20013          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
20014     },
20015
20016     // private
20017     getVMaxSize : function(){
20018         var cmax = this.maxSize || 10000;
20019         var center = this.layout.center;
20020         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
20021     },
20022
20023     // private
20024     onSplitMove : function(split, newSize){
20025         var s = this.panel.getSize();
20026         this.lastSplitSize = newSize;
20027         if(this.position == 'north' || this.position == 'south'){
20028             this.panel.setSize(s.width, newSize);
20029             this.state.height = newSize;
20030         }else{
20031             this.panel.setSize(newSize, s.height);
20032             this.state.width = newSize;
20033         }
20034         this.layout.layout();
20035         this.panel.saveState();
20036         return false;
20037     },
20038
20039     /**
20040      * Returns a reference to the split bar in use by this region.
20041      * @return {Ext.SplitBar} The split bar
20042      */
20043     getSplitBar : function(){
20044         return this.split;
20045     },
20046
20047     // inherit docs
20048     destroy : function() {
20049         Ext.destroy(
20050             this.miniSplitEl,
20051             this.split,
20052             this.splitEl
20053         );
20054     }
20055 });
20056
20057 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
20058  * @class Ext.layout.FormLayout
20059  * @extends Ext.layout.AnchorLayout
20060  * <p>This layout manager is specifically designed for rendering and managing child Components of
20061  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
20062  * {@link Ext.form.Field Field}s.</p>
20063  *
20064  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
20065  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
20066  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
20067  *
20068  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
20069  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
20070  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
20071  *
20072  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
20073  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
20074  * layout-specific config properties:<div class="mdetail-params"><ul>
20075  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
20076  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
20077  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
20078  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
20079  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
20080  * </ul></div></p>
20081  *
20082  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
20083  * <div class="mdetail-params"><ul>
20084  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
20085  * </ul></div></p>
20086  *
20087  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
20088  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
20089  * in this way may be configured with the following options which affect the way the FormLayout renders them:
20090  * <div class="mdetail-params"><ul>
20091  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
20092  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
20093  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
20094  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
20095  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
20096  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
20097  * </ul></div></p>
20098  *
20099  * <p>Example usage:</p>
20100  * <pre><code>
20101 // Required if showing validation messages
20102 Ext.QuickTips.init();
20103
20104 // While you can create a basic Panel with layout:'form', practically
20105 // you should usually use a FormPanel to also get its form functionality
20106 // since it already creates a FormLayout internally.
20107 var form = new Ext.form.FormPanel({
20108     title: 'Form Layout',
20109     bodyStyle: 'padding:15px',
20110     width: 350,
20111     defaultType: 'textfield',
20112     defaults: {
20113         // applied to each contained item
20114         width: 230,
20115         msgTarget: 'side'
20116     },
20117     items: [{
20118             fieldLabel: 'First Name',
20119             name: 'first',
20120             allowBlank: false,
20121             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
20122         },{
20123             fieldLabel: 'Last Name',
20124             name: 'last'
20125         },{
20126             fieldLabel: 'Email',
20127             name: 'email',
20128             vtype:'email'
20129         }, {
20130             xtype: 'textarea',
20131             hideLabel: true,     // override hideLabels layout config
20132             name: 'msg',
20133             anchor: '100% -53'
20134         }
20135     ],
20136     buttons: [
20137         {text: 'Save'},
20138         {text: 'Cancel'}
20139     ],
20140     layoutConfig: {
20141         {@link #labelSeparator}: '~' // superseded by assignment below
20142     },
20143     // config options applicable to container when layout='form':
20144     hideLabels: false,
20145     labelAlign: 'left',   // or 'right' or 'top'
20146     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
20147     labelWidth: 65,       // defaults to 100
20148     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
20149 });
20150 </code></pre>
20151  */
20152 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
20153
20154     /**
20155      * @cfg {String} labelSeparator
20156      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
20157      * of this property at the <b>container</b> level takes precedence.
20158      */
20159     labelSeparator : ':',
20160
20161     /**
20162      * Read only. The CSS style specification string added to field labels in this layout if not
20163      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
20164      * @type String
20165      * @property labelStyle
20166      */
20167     
20168     /**
20169      * @cfg {Boolean} trackLabels
20170      * True to show/hide the field label when the field is hidden. Defaults to <tt>false</tt>.
20171      */
20172     trackLabels: false,
20173     
20174
20175     onRemove: function(c){
20176         Ext.layout.FormLayout.superclass.onRemove.call(this, c);
20177         if(this.trackLabels && !this.isHide(c)){
20178             c.un('show', this.onFieldShow, this);
20179             c.un('hide', this.onFieldHide, this);
20180         }
20181         // check for itemCt, since we may be removing a fieldset or something similar
20182         var el = c.getPositionEl(),
20183                 ct = c.getItemCt && c.getItemCt();
20184         if(c.rendered && ct){
20185             el.insertAfter(ct);
20186             Ext.destroy(ct);
20187             Ext.destroyMembers(c, 'label', 'itemCt');
20188             if(c.customItemCt){
20189                 Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
20190             }
20191         }
20192     },
20193     
20194     // private
20195     setContainer : function(ct){
20196         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
20197         if(ct.labelAlign){
20198             ct.addClass('x-form-label-'+ct.labelAlign);
20199         }
20200
20201         if(ct.hideLabels){
20202             Ext.apply(this, {
20203                 labelStyle: 'display:none',
20204                 elementStyle: 'padding-left:0;',
20205                 labelAdjust: 0
20206             });
20207         }else{
20208             this.labelSeparator = ct.labelSeparator || this.labelSeparator;
20209             ct.labelWidth = ct.labelWidth || 100;
20210             if(Ext.isNumber(ct.labelWidth)){
20211                 var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
20212                 Ext.apply(this, {
20213                     labelAdjust: ct.labelWidth + pad,
20214                     labelStyle: 'width:' + ct.labelWidth + 'px;',
20215                     elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
20216                 });
20217             }
20218             if(ct.labelAlign == 'top'){
20219                 Ext.apply(this, {
20220                     labelStyle: 'width:auto;',
20221                     labelAdjust: 0,
20222                     elementStyle: 'padding-left:0;'
20223                 });
20224             }
20225         }
20226     },
20227     
20228     isHide: function(c){
20229         return c.hideLabel || this.container.hideLabels;
20230     },
20231     
20232     onFieldShow: function(c){
20233         c.getItemCt().removeClass('x-hide-' + c.hideMode);
20234     },
20235     
20236     onFieldHide: function(c){
20237         c.getItemCt().addClass('x-hide-' + c.hideMode);   
20238     },
20239
20240     //private
20241     getLabelStyle: function(s){
20242         var ls = '', items = [this.labelStyle, s];
20243         for (var i = 0, len = items.length; i < len; ++i){
20244             if (items[i]){
20245                 ls += items[i];
20246                 if (ls.substr(-1, 1) != ';'){
20247                     ls += ';'
20248                 }
20249             }
20250         }
20251         return ls;
20252     },
20253
20254     /**
20255      * @cfg {Ext.Template} fieldTpl
20256      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
20257      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
20258 new Ext.Template(
20259     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
20260         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
20261         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
20262         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
20263     '&lt;/div>'
20264 );
20265 </code></pre>
20266      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
20267      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
20268      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
20269      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
20270      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
20271      * supplied at the container level.</div></li>
20272      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
20273      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
20274      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
20275      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
20276      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
20277      * field (defaults to <tt>''</tt>)</div></li>
20278      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
20279      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
20280      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
20281      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
20282      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
20283      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
20284      * </ul></div>
20285      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
20286      */
20287
20288     // private
20289     renderItem : function(c, position, target){
20290         if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
20291             var args = this.getTemplateArgs(c);
20292             if(Ext.isNumber(position)){
20293                 position = target.dom.childNodes[position] || null;
20294             }
20295             if(position){
20296                 c.itemCt = this.fieldTpl.insertBefore(position, args, true);
20297             }else{
20298                 c.itemCt = this.fieldTpl.append(target, args, true);
20299             }
20300             if(!c.rendered){
20301                 c.render('x-form-el-' + c.id);
20302             }else if(!this.isValidParent(c, target)){
20303                 Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
20304             }
20305             if(!c.getItemCt){
20306                 // Non form fields don't have getItemCt, apply it here
20307                 // This will get cleaned up in onRemove
20308                 Ext.apply(c, {
20309                     getItemCt: function(){
20310                         return c.itemCt;
20311                     },
20312                     customItemCt: true
20313                 });
20314             }
20315             c.label = c.getItemCt().child('label.x-form-item-label');
20316             if(this.trackLabels && !this.isHide(c)){
20317                 if(c.hidden){
20318                     this.onFieldHide(c);
20319                 }
20320                 c.on({
20321                     scope: this,
20322                     show: this.onFieldShow,
20323                     hide: this.onFieldHide
20324                 });
20325             }
20326             this.configureItem(c);
20327         }else {
20328             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
20329         }
20330     },
20331
20332     /**
20333      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
20334      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
20335      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
20336      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
20337      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
20338      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
20339      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
20340      * supplied at the container level.</div></li>
20341      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
20342      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
20343      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
20344      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
20345      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
20346      * field (defaults to <tt>''</tt>)</div></li>
20347      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
20348      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
20349      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
20350      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
20351      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
20352      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
20353      * </ul></div>
20354      * @param field The {@link Field Ext.form.Field} being rendered.
20355      * @return An object hash containing the properties required to render the Field.
20356      */
20357     getTemplateArgs: function(field) {
20358         var noLabelSep = !field.fieldLabel || field.hideLabel;
20359         return {
20360             id: field.id,
20361             label: field.fieldLabel,
20362             labelStyle: this.getLabelStyle(field.labelStyle),
20363             elementStyle: this.elementStyle||'',
20364             labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator),
20365             itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
20366             clearCls: field.clearCls || 'x-form-clear-left'
20367         };
20368     },
20369
20370     // private
20371     adjustWidthAnchor: function(value, c){
20372         if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
20373             var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
20374             return value - this.labelAdjust + (adjust ? -3 : 0);
20375         }
20376         return value;
20377     },
20378     
20379     adjustHeightAnchor : function(value, c){
20380         if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
20381             return value - c.label.getHeight();
20382         }
20383         return value;
20384     },
20385
20386     // private
20387     isValidParent : function(c, target){
20388         return target && this.container.getEl().contains(c.getDomPositionEl());
20389     }
20390
20391     /**
20392      * @property activeItem
20393      * @hide
20394      */
20395 });
20396
20397 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;/**\r
20398  * @class Ext.layout.AccordionLayout\r
20399  * @extends Ext.layout.FitLayout\r
20400  * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only\r
20401  * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>\r
20402  * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>\r
20403  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
20404  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
20405  * <p>Example usage:</p>\r
20406  * <pre><code>\r
20407 var accordion = new Ext.Panel({\r
20408     title: 'Accordion Layout',\r
20409     layout:'accordion',\r
20410     defaults: {\r
20411         // applied to each contained panel\r
20412         bodyStyle: 'padding:15px'\r
20413     },\r
20414     layoutConfig: {\r
20415         // layout-specific configs go here\r
20416         titleCollapse: false,\r
20417         animate: true,\r
20418         activeOnTop: true\r
20419     },\r
20420     items: [{\r
20421         title: 'Panel 1',\r
20422         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
20423     },{\r
20424         title: 'Panel 2',\r
20425         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
20426     },{\r
20427         title: 'Panel 3',\r
20428         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
20429     }]\r
20430 });\r
20431 </code></pre>\r
20432  */\r
20433 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {\r
20434     /**\r
20435      * @cfg {Boolean} fill\r
20436      * True to adjust the active item's height to fill the available space in the container, false to use the\r
20437      * item's current height, or auto height if not explicitly set (defaults to true).\r
20438      */\r
20439     fill : true,\r
20440     /**\r
20441      * @cfg {Boolean} autoWidth\r
20442      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).\r
20443      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within\r
20444      * layouts if they have auto width, so in such cases this config should be set to false.\r
20445      */\r
20446     autoWidth : true,\r
20447     /**\r
20448      * @cfg {Boolean} titleCollapse\r
20449      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow\r
20450      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,\r
20451      * {@link #hideCollapseTool} should be false also.\r
20452      */\r
20453     titleCollapse : true,\r
20454     /**\r
20455      * @cfg {Boolean} hideCollapseTool\r
20456      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).\r
20457      * When set to true, {@link #titleCollapse} should be true also.\r
20458      */\r
20459     hideCollapseTool : false,\r
20460     /**\r
20461      * @cfg {Boolean} collapseFirst\r
20462      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools\r
20463      * in the contained panels' title bars, false to render it last (defaults to false).\r
20464      */\r
20465     collapseFirst : false,\r
20466     /**\r
20467      * @cfg {Boolean} animate\r
20468      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and\r
20469      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each\r
20470      * contained panel for this property, set this to undefined at the layout level.\r
20471      */\r
20472     animate : false,\r
20473     /**\r
20474      * @cfg {Boolean} sequence\r
20475      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.\r
20476      */\r
20477     sequence : false,\r
20478     /**\r
20479      * @cfg {Boolean} activeOnTop\r
20480      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,\r
20481      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).\r
20482      */\r
20483     activeOnTop : false,\r
20484 \r
20485     renderItem : function(c){\r
20486         if(this.animate === false){\r
20487             c.animCollapse = false;\r
20488         }\r
20489         c.collapsible = true;\r
20490         if(this.autoWidth){\r
20491             c.autoWidth = true;\r
20492         }\r
20493         if(this.titleCollapse){\r
20494             c.titleCollapse = true;\r
20495         }\r
20496         if(this.hideCollapseTool){\r
20497             c.hideCollapseTool = true;\r
20498         }\r
20499         if(this.collapseFirst !== undefined){\r
20500             c.collapseFirst = this.collapseFirst;\r
20501         }\r
20502         if(!this.activeItem && !c.collapsed){\r
20503             this.activeItem = c;\r
20504         }else if(this.activeItem && this.activeItem != c){\r
20505             c.collapsed = true;\r
20506         }\r
20507         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);\r
20508         c.header.addClass('x-accordion-hd');\r
20509         c.on('beforeexpand', this.beforeExpand, this);\r
20510     },\r
20511     \r
20512     onRemove: function(c){\r
20513         Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);\r
20514         if(c.rendered){\r
20515             c.header.removeClass('x-accordion-hd');\r
20516         }\r
20517         c.un('beforeexpand', this.beforeExpand, this);\r
20518     },\r
20519 \r
20520     // private\r
20521     beforeExpand : function(p, anim){\r
20522         var ai = this.activeItem;\r
20523         if(ai){\r
20524             if(this.sequence){\r
20525                 delete this.activeItem;\r
20526                 if (!ai.collapsed){\r
20527                     ai.collapse({callback:function(){\r
20528                         p.expand(anim || true);\r
20529                     }, scope: this});\r
20530                     return false;\r
20531                 }\r
20532             }else{\r
20533                 ai.collapse(this.animate);\r
20534             }\r
20535         }\r
20536         this.activeItem = p;\r
20537         if(this.activeOnTop){\r
20538             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);\r
20539         }\r
20540         this.layout();\r
20541     },\r
20542 \r
20543     // private\r
20544     setItemSize : function(item, size){\r
20545         if(this.fill && item){\r
20546             var hh = 0;\r
20547             this.container.items.each(function(p){\r
20548                 if(p != item){\r
20549                     hh += p.header.getHeight();\r
20550                 }    \r
20551             });\r
20552             size.height -= hh;\r
20553             item.setSize(size);\r
20554         }\r
20555     },\r
20556 \r
20557     /**\r
20558      * Sets the active (expanded) item in the layout.\r
20559      * @param {String/Number} item The string component id or numeric index of the item to activate\r
20560      */\r
20561     setActiveItem : function(item){\r
20562         item = this.container.getComponent(item);\r
20563         if(this.activeItem != item){\r
20564             if(item.rendered && item.collapsed){\r
20565                 item.expand();\r
20566             }else{\r
20567                 this.activeItem = item;\r
20568             }\r
20569         }\r
20570 \r
20571     }\r
20572 });\r
20573 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;\r
20574 \r
20575 //backwards compat\r
20576 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**\r
20577  * @class Ext.layout.TableLayout\r
20578  * @extends Ext.layout.ContainerLayout\r
20579  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be\r
20580  * specified, and rowspan and colspan can be used to create complex layouts within the table.\r
20581  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,\r
20582  * and should generally not need to be created directly via the new keyword.</p>\r
20583  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via\r
20584  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the\r
20585  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a\r
20586  * TableLayout can supply the following table-specific config properties:</p>\r
20587  * <ul>\r
20588  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>\r
20589  * <li><b>colspan</b> Applied to the table cell containing the item.</li>\r
20590  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>\r
20591  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>\r
20592  * </ul>\r
20593  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard\r
20594  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes\r
20595  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.\r
20596  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the\r
20597  * total column count in the layoutConfig and start adding panels in their natural order from left to right,\r
20598  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,\r
20599  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add\r
20600  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>\r
20601  * <pre><code>\r
20602 // This code will generate a layout table that is 3 columns by 2 rows\r
20603 // with some spanning included.  The basic layout will be:\r
20604 // +--------+-----------------+\r
20605 // |   A    |   B             |\r
20606 // |        |--------+--------|\r
20607 // |        |   C    |   D    |\r
20608 // +--------+--------+--------+\r
20609 var table = new Ext.Panel({\r
20610     title: 'Table Layout',\r
20611     layout:'table',\r
20612     defaults: {\r
20613         // applied to each contained panel\r
20614         bodyStyle:'padding:20px'\r
20615     },\r
20616     layoutConfig: {\r
20617         // The total column count must be specified here\r
20618         columns: 3\r
20619     },\r
20620     items: [{\r
20621         html: '&lt;p&gt;Cell A content&lt;/p&gt;',\r
20622         rowspan: 2\r
20623     },{\r
20624         html: '&lt;p&gt;Cell B content&lt;/p&gt;',\r
20625         colspan: 2\r
20626     },{\r
20627         html: '&lt;p&gt;Cell C content&lt;/p&gt;',\r
20628         cellCls: 'highlight'\r
20629     },{\r
20630         html: '&lt;p&gt;Cell D content&lt;/p&gt;'\r
20631     }]\r
20632 });\r
20633 </code></pre>\r
20634  */\r
20635 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
20636     /**\r
20637      * @cfg {Number} columns\r
20638      * The total number of columns to create in the table for this layout.  If not specified, all Components added to\r
20639      * this layout will be rendered into a single row using one column per Component.\r
20640      */\r
20641 \r
20642     // private\r
20643     monitorResize:false,\r
20644 \r
20645     /**\r
20646      * @cfg {Object} tableAttrs\r
20647      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification\r
20648      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>\r
20649 {\r
20650     xtype: 'panel',\r
20651     layout: 'table',\r
20652     layoutConfig: {\r
20653         tableAttrs: {\r
20654                 style: {\r
20655                         width: '100%'\r
20656                 }\r
20657         },\r
20658         columns: 3\r
20659     }\r
20660 }</code></pre>\r
20661      */\r
20662     tableAttrs:null,\r
20663     \r
20664     // private\r
20665     setContainer : function(ct){\r
20666         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);\r
20667 \r
20668         this.currentRow = 0;\r
20669         this.currentColumn = 0;\r
20670         this.cells = [];\r
20671     },\r
20672 \r
20673     // private\r
20674     onLayout : function(ct, target){\r
20675         var cs = ct.items.items, len = cs.length, c, i;\r
20676 \r
20677         if(!this.table){\r
20678             target.addClass('x-table-layout-ct');\r
20679 \r
20680             this.table = target.createChild(\r
20681                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
20682         }\r
20683         this.renderAll(ct, target);\r
20684     },\r
20685 \r
20686     // private\r
20687     getRow : function(index){\r
20688         var row = this.table.tBodies[0].childNodes[index];\r
20689         if(!row){\r
20690             row = document.createElement('tr');\r
20691             this.table.tBodies[0].appendChild(row);\r
20692         }\r
20693         return row;\r
20694     },\r
20695 \r
20696     // private\r
20697     getNextCell : function(c){\r
20698         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);\r
20699         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];\r
20700         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){\r
20701             if(!this.cells[rowIndex]){\r
20702                 this.cells[rowIndex] = [];\r
20703             }\r
20704             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){\r
20705                 this.cells[rowIndex][colIndex] = true;\r
20706             }\r
20707         }\r
20708         var td = document.createElement('td');\r
20709         if(c.cellId){\r
20710             td.id = c.cellId;\r
20711         }\r
20712         var cls = 'x-table-layout-cell';\r
20713         if(c.cellCls){\r
20714             cls += ' ' + c.cellCls;\r
20715         }\r
20716         td.className = cls;\r
20717         if(c.colspan){\r
20718             td.colSpan = c.colspan;\r
20719         }\r
20720         if(c.rowspan){\r
20721             td.rowSpan = c.rowspan;\r
20722         }\r
20723         this.getRow(curRow).appendChild(td);\r
20724         return td;\r
20725     },\r
20726     \r
20727     // private\r
20728     getNextNonSpan: function(colIndex, rowIndex){\r
20729         var cols = this.columns;\r
20730         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {\r
20731             if(cols && colIndex >= cols){\r
20732                 rowIndex++;\r
20733                 colIndex = 0;\r
20734             }else{\r
20735                 colIndex++;\r
20736             }\r
20737         }\r
20738         return [colIndex, rowIndex];\r
20739     },\r
20740 \r
20741     // private\r
20742     renderItem : function(c, position, target){\r
20743         if(c && !c.rendered){\r
20744             c.render(this.getNextCell(c));\r
20745             this.configureItem(c, position);\r
20746         }else if(c && !this.isValidParent(c, target)){\r
20747             var container = this.getNextCell(c);\r
20748             container.insertBefore(c.getDomPositionEl().dom, null);\r
20749             c.container = Ext.get(container);\r
20750             this.configureItem(c, position);\r
20751         }\r
20752     },\r
20753 \r
20754     // private\r
20755     isValidParent : function(c, target){\r
20756         return c.getDomPositionEl().up('table', 5).dom.parentNode === (target.dom || target);\r
20757     }\r
20758 \r
20759     /**\r
20760      * @property activeItem\r
20761      * @hide\r
20762      */\r
20763 });\r
20764 \r
20765 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**\r
20766  * @class Ext.layout.AbsoluteLayout\r
20767  * @extends Ext.layout.AnchorLayout\r
20768  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the\r
20769  * ability for x/y positioning using the standard x and y component config options.</p>\r
20770  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
20771  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
20772  * <p>Example usage:</p>\r
20773  * <pre><code>\r
20774 var form = new Ext.form.FormPanel({\r
20775     title: 'Absolute Layout',\r
20776     layout:'absolute',\r
20777     layoutConfig: {\r
20778         // layout-specific configs go here\r
20779         extraCls: 'x-abs-layout-item',\r
20780     },\r
20781     baseCls: 'x-plain',\r
20782     url:'save-form.php',\r
20783     defaultType: 'textfield',\r
20784     items: [{\r
20785         x: 0,\r
20786         y: 5,\r
20787         xtype:'label',\r
20788         text: 'Send To:'\r
20789     },{\r
20790         x: 60,\r
20791         y: 0,\r
20792         name: 'to',\r
20793         anchor:'100%'  // anchor width by percentage\r
20794     },{\r
20795         x: 0,\r
20796         y: 35,\r
20797         xtype:'label',\r
20798         text: 'Subject:'\r
20799     },{\r
20800         x: 60,\r
20801         y: 30,\r
20802         name: 'subject',\r
20803         anchor: '100%'  // anchor width by percentage\r
20804     },{\r
20805         x:0,\r
20806         y: 60,\r
20807         xtype: 'textarea',\r
20808         name: 'msg',\r
20809         anchor: '100% 100%'  // anchor width and height\r
20810     }]\r
20811 });\r
20812 </code></pre>\r
20813  */\r
20814 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {\r
20815 \r
20816     extraCls: 'x-abs-layout-item',\r
20817 \r
20818     onLayout : function(ct, target){\r
20819         target.position();\r
20820         this.paddingLeft = target.getPadding('l');\r
20821         this.paddingTop = target.getPadding('t');\r
20822 \r
20823         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);\r
20824     },\r
20825 \r
20826     // private\r
20827     adjustWidthAnchor : function(value, comp){\r
20828         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;\r
20829     },\r
20830 \r
20831     // private\r
20832     adjustHeightAnchor : function(value, comp){\r
20833         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;\r
20834     }\r
20835     /**\r
20836      * @property activeItem\r
20837      * @hide\r
20838      */\r
20839 });\r
20840 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
20841 /**\r
20842  * @class Ext.layout.BoxLayout\r
20843  * @extends Ext.layout.ContainerLayout\r
20844  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>\r
20845  */\r
20846 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
20847     /**\r
20848      * @cfg {Object} defaultMargins\r
20849      * <p>If the individual contained items do not have a <tt>margins</tt>\r
20850      * property specified, the default margins from this property will be\r
20851      * applied to each item.</p>\r
20852      * <br><p>This property may be specified as an object containing margins\r
20853      * to apply in the format:</p><pre><code>\r
20854 {\r
20855     top: (top margin),\r
20856     right: (right margin),\r
20857     bottom: (bottom margin),\r
20858     left: (left margin)\r
20859 }</code></pre>\r
20860      * <p>This property may also be specified as a string containing\r
20861      * space-separated, numeric margin values. The order of the sides associated\r
20862      * with each value matches the way CSS processes margin values:</p>\r
20863      * <div class="mdetail-params"><ul>\r
20864      * <li>If there is only one value, it applies to all sides.</li>\r
20865      * <li>If there are two values, the top and bottom borders are set to the\r
20866      * first value and the right and left are set to the second.</li>\r
20867      * <li>If there are three values, the top is set to the first value, the left\r
20868      * and right are set to the second, and the bottom is set to the third.</li>\r
20869      * <li>If there are four values, they apply to the top, right, bottom, and\r
20870      * left, respectively.</li>\r
20871      * </ul></div>\r
20872      * <p>Defaults to:</p><pre><code>\r
20873      * {top:0, right:0, bottom:0, left:0}\r
20874      * </code></pre>\r
20875      */\r
20876     defaultMargins : {left:0,top:0,right:0,bottom:0},\r
20877     /**\r
20878      * @cfg {String} padding\r
20879      * <p>Sets the padding to be applied to all child items managed by this layout.</p> \r
20880      * <p>This property must be specified as a string containing\r
20881      * space-separated, numeric padding values. The order of the sides associated\r
20882      * with each value matches the way CSS processes padding values:</p>\r
20883      * <div class="mdetail-params"><ul>\r
20884      * <li>If there is only one value, it applies to all sides.</li>\r
20885      * <li>If there are two values, the top and bottom borders are set to the\r
20886      * first value and the right and left are set to the second.</li>\r
20887      * <li>If there are three values, the top is set to the first value, the left\r
20888      * and right are set to the second, and the bottom is set to the third.</li>\r
20889      * <li>If there are four values, they apply to the top, right, bottom, and\r
20890      * left, respectively.</li>\r
20891      * </ul></div>\r
20892      * <p>Defaults to: <code>"0"</code></p>\r
20893      */\r
20894     padding : '0',\r
20895     // documented in subclasses\r
20896     pack : 'start',\r
20897 \r
20898     // private\r
20899     monitorResize : true,\r
20900     scrollOffset : 0,\r
20901     extraCls : 'x-box-item',\r
20902     ctCls : 'x-box-layout-ct',\r
20903     innerCls : 'x-box-inner',\r
20904     \r
20905     constructor : function(config){\r
20906         Ext.layout.BoxLayout.superclass.constructor.call(this, config);\r
20907         if(Ext.isString(this.defaultMargins)){\r
20908             this.defaultMargins = this.parseMargins(this.defaultMargins);\r
20909         }\r
20910     },\r
20911 \r
20912     // private\r
20913     isValidParent : function(c, target){\r
20914         return c.getEl().dom.parentNode == this.innerCt.dom;\r
20915     },\r
20916 \r
20917     // private\r
20918     onLayout : function(ct, target){\r
20919         var cs = ct.items.items, len = cs.length, c, i, last = len-1, cm;\r
20920 \r
20921         if(!this.innerCt){\r
20922             target.addClass(this.ctCls);\r
20923 \r
20924             // the innerCt prevents wrapping and shuffling while\r
20925             // the container is resizing\r
20926             this.innerCt = target.createChild({cls:this.innerCls});\r
20927             this.padding = this.parseMargins(this.padding); \r
20928         }\r
20929         this.renderAll(ct, this.innerCt);\r
20930     },\r
20931 \r
20932     // private\r
20933     renderItem : function(c){\r
20934         if(Ext.isString(c.margins)){\r
20935             c.margins = this.parseMargins(c.margins);\r
20936         }else if(!c.margins){\r
20937             c.margins = this.defaultMargins;\r
20938         }\r
20939         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);\r
20940     },\r
20941 \r
20942     getTargetSize : function(target){
20943         return (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getStyleSize() : target.getViewSize();\r
20944     },\r
20945     \r
20946     getItems: function(ct){\r
20947         var items = [];\r
20948         ct.items.each(function(c){\r
20949             if(c.isVisible()){\r
20950                 items.push(c);\r
20951             }\r
20952         });\r
20953         return items;\r
20954     }\r
20955 \r
20956     /**\r
20957      * @property activeItem\r
20958      * @hide\r
20959      */\r
20960 });\r
20961 \r
20962 /**\r
20963  * @class Ext.layout.VBoxLayout\r
20964  * @extends Ext.layout.BoxLayout\r
20965  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical\r
20966  * space between child items containing a numeric <code>flex</code> configuration.</p>\r
20967  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.\r
20968  */\r
20969 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {\r
20970     /**\r
20971      * @cfg {String} align\r
20972      * Controls how the child items of the container are aligned. Acceptable configuration values for this\r
20973      * property are:\r
20974      * <div class="mdetail-params"><ul>\r
20975      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally\r
20976      * at the <b>left</b> side of the container</div></li>\r
20977      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the\r
20978      * <b>mid-width</b> of the container</div></li>\r
20979      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill\r
20980      * the width of the container</div></li>\r
20981      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to\r
20982      * the size of the largest item.</div></li>\r
20983      * </ul></div>\r
20984      */\r
20985     align : 'left', // left, center, stretch, strechmax\r
20986     /**\r
20987      * @cfg {String} pack\r
20988      * Controls how the child items of the container are packed together. Acceptable configuration values\r
20989      * for this property are:\r
20990      * <div class="mdetail-params"><ul>\r
20991      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at\r
20992      * <b>top</b> side of container</div></li>\r
20993      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at\r
20994      * <b>mid-height</b> of container</div></li>\r
20995      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>\r
20996      * side of container</div></li>\r
20997      * </ul></div>\r
20998      */\r
20999     /**\r
21000      * @cfg {Number} flex\r
21001      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed\r
21002      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>\r
21003      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with\r
21004      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or\r
21005      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).\r
21006      */\r
21007 \r
21008     // private\r
21009     onLayout : function(ct, target){\r
21010         Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target);\r
21011                     \r
21012         \r
21013         var cs = this.getItems(ct), cm, ch, margin,\r
21014             size = this.getTargetSize(target),\r
21015             w = size.width - target.getPadding('lr'),\r
21016             h = size.height - target.getPadding('tb') - this.scrollOffset,\r
21017             l = this.padding.left, t = this.padding.top,\r
21018             isStart = this.pack == 'start',\r
21019             isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,\r
21020             stretchWidth = w - (this.padding.left + this.padding.right),\r
21021             extraHeight = 0,\r
21022             maxWidth = 0,\r
21023             totalFlex = 0,\r
21024             flexHeight = 0,\r
21025             usedHeight = 0;\r
21026             \r
21027         Ext.each(cs, function(c){\r
21028             cm = c.margins;\r
21029             totalFlex += c.flex || 0;\r
21030             ch = c.getHeight();\r
21031             margin = cm.top + cm.bottom;\r
21032             extraHeight += ch + margin;\r
21033             flexHeight += margin + (c.flex ? 0 : ch);\r
21034             maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right);\r
21035         });\r
21036         extraHeight = h - extraHeight - this.padding.top - this.padding.bottom;\r
21037         \r
21038         var innerCtWidth = maxWidth + this.padding.left + this.padding.right;\r
21039         switch(this.align){\r
21040             case 'stretch':\r
21041                 this.innerCt.setSize(w, h);\r
21042                 break;\r
21043             case 'stretchmax':\r
21044             case 'left':\r
21045                 this.innerCt.setSize(innerCtWidth, h);\r
21046                 break;\r
21047             case 'center':\r
21048                 this.innerCt.setSize(w = Math.max(w, innerCtWidth), h);\r
21049                 break;\r
21050         }\r
21051 \r
21052         var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight),\r
21053             leftOver = availHeight,\r
21054             heights = [],\r
21055             restore = [],\r
21056             idx = 0,\r
21057             availableWidth = Math.max(0, w - this.padding.left - this.padding.right);\r
21058             \r
21059 \r
21060         Ext.each(cs, function(c){\r
21061             if(isStart && c.flex){\r
21062                 ch = Math.floor(availHeight * (c.flex / totalFlex));\r
21063                 leftOver -= ch;\r
21064                 heights.push(ch);\r
21065             }\r
21066         }); \r
21067         \r
21068         if(this.pack == 'center'){\r
21069             t += extraHeight ? extraHeight / 2 : 0;\r
21070         }else if(this.pack == 'end'){\r
21071             t += extraHeight;\r
21072         }\r
21073         Ext.each(cs, function(c){\r
21074             cm = c.margins;\r
21075             t += cm.top;\r
21076             c.setPosition(l + cm.left, t);\r
21077             if(isStart && c.flex){\r
21078                 ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0));\r
21079                 if(isRestore){\r
21080                     restore.push(c.getWidth());\r
21081                 }\r
21082                 c.setSize(availableWidth, ch);\r
21083             }else{\r
21084                 ch = c.getHeight();\r
21085             }\r
21086             t += ch + cm.bottom;\r
21087         });\r
21088         \r
21089         idx = 0;\r
21090         Ext.each(cs, function(c){\r
21091             cm = c.margins;\r
21092             if(this.align == 'stretch'){\r
21093                 c.setWidth((stretchWidth - (cm.left + cm.right)).constrain(\r
21094                     c.minWidth || 0, c.maxWidth || 1000000));\r
21095             }else if(this.align == 'stretchmax'){\r
21096                 c.setWidth((maxWidth - (cm.left + cm.right)).constrain(\r
21097                     c.minWidth || 0, c.maxWidth || 1000000));\r
21098             }else{\r
21099                 if(this.align == 'center'){\r
21100                     var diff = availableWidth - (c.getWidth() + cm.left + cm.right);\r
21101                     if(diff > 0){\r
21102                         c.setPosition(l + cm.left + (diff/2), c.y);\r
21103                     }\r
21104                 }\r
21105                 if(isStart && c.flex){\r
21106                     c.setWidth(restore[idx++]);\r
21107                 }\r
21108             }\r
21109         }, this);\r
21110     }\r
21111     /**\r
21112      * @property activeItem\r
21113      * @hide\r
21114      */\r
21115 });\r
21116 \r
21117 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;\r
21118 \r
21119 /**\r
21120  * @class Ext.layout.HBoxLayout\r
21121  * @extends Ext.layout.BoxLayout\r
21122  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal\r
21123  * space between child items containing a numeric <code>flex</code> configuration.</p>\r
21124  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.\r
21125  */\r
21126 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {\r
21127     /**\r
21128      * @cfg {String} align\r
21129      * Controls how the child items of the container are aligned. Acceptable configuration values for this\r
21130      * property are:\r
21131      * <div class="mdetail-params"><ul>\r
21132      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically\r
21133      * at the <b>left</b> side of the container</div></li>\r
21134      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically at the\r
21135      * <b>mid-height</b> of the container</div></li>\r
21136      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill\r
21137      * the height of the container</div></li>\r
21138      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to\r
21139      * the size of the largest item.</div></li>\r
21140      */\r
21141     align : 'top', // top, middle, stretch, strechmax\r
21142     /**\r
21143      * @cfg {String} pack\r
21144      * Controls how the child items of the container are packed together. Acceptable configuration values\r
21145      * for this property are:\r
21146      * <div class="mdetail-params"><ul>\r
21147      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at\r
21148      * <b>left</b> side of container</div></li>\r
21149      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at\r
21150      * <b>mid-width</b> of container</div></li>\r
21151      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>\r
21152      * side of container</div></li>\r
21153      * </ul></div>\r
21154      */\r
21155     /**\r
21156      * @cfg {Number} flex\r
21157      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed\r
21158      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>\r
21159      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with\r
21160      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or\r
21161      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).\r
21162      */\r
21163 \r
21164     // private\r
21165     onLayout : function(ct, target){\r
21166         Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);\r
21167         \r
21168         var cs = this.getItems(ct), cm, cw, margin,\r
21169             size = this.getTargetSize(target),\r
21170             w = size.width - target.getPadding('lr') - this.scrollOffset,\r
21171             h = size.height - target.getPadding('tb'),\r
21172             l = this.padding.left, t = this.padding.top,\r
21173             isStart = this.pack == 'start',\r
21174             isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,\r
21175             stretchHeight = h - (this.padding.top + this.padding.bottom),\r
21176             extraWidth = 0,\r
21177             maxHeight = 0,\r
21178             totalFlex = 0,\r
21179             flexWidth = 0,\r
21180             usedWidth = 0;\r
21181         \r
21182         Ext.each(cs, function(c){\r
21183             cm = c.margins;\r
21184             totalFlex += c.flex || 0;\r
21185             cw = c.getWidth();\r
21186             margin = cm.left + cm.right;\r
21187             extraWidth += cw + margin;\r
21188             flexWidth += margin + (c.flex ? 0 : cw);\r
21189             maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);\r
21190         });\r
21191         extraWidth = w - extraWidth - this.padding.left - this.padding.right;\r
21192         \r
21193         var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;\r
21194         switch(this.align){\r
21195             case 'stretch':\r
21196                 this.innerCt.setSize(w, h);\r
21197                 break;\r
21198             case 'stretchmax':\r
21199             case 'top':\r
21200                 this.innerCt.setSize(w, innerCtHeight);\r
21201                 break;\r
21202             case 'middle':\r
21203                 this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));\r
21204                 break;\r
21205         }\r
21206         \r
21207 \r
21208         var availWidth = Math.max(0, w - this.padding.left - this.padding.right - flexWidth),\r
21209             leftOver = availWidth,\r
21210             widths = [],\r
21211             restore = [],\r
21212             idx = 0,\r
21213             availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);\r
21214             \r
21215 \r
21216         Ext.each(cs, function(c){\r
21217             if(isStart && c.flex){\r
21218                 cw = Math.floor(availWidth * (c.flex / totalFlex));\r
21219                 leftOver -= cw;\r
21220                 widths.push(cw);\r
21221             }\r
21222         }); \r
21223         \r
21224         if(this.pack == 'center'){\r
21225             l += extraWidth ? extraWidth / 2 : 0;\r
21226         }else if(this.pack == 'end'){\r
21227             l += extraWidth;\r
21228         }\r
21229         Ext.each(cs, function(c){\r
21230             cm = c.margins;\r
21231             l += cm.left;\r
21232             c.setPosition(l, t + cm.top);\r
21233             if(isStart && c.flex){\r
21234                 cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));\r
21235                 if(isRestore){\r
21236                     restore.push(c.getHeight());\r
21237                 }\r
21238                 c.setSize(cw, availableHeight);\r
21239             }else{\r
21240                 cw = c.getWidth();\r
21241             }\r
21242             l += cw + cm.right;\r
21243         });\r
21244         \r
21245         idx = 0;\r
21246         Ext.each(cs, function(c){\r
21247             var cm = c.margins;\r
21248             if(this.align == 'stretch'){\r
21249                 c.setHeight((stretchHeight - (cm.top + cm.bottom)).constrain(\r
21250                     c.minHeight || 0, c.maxHeight || 1000000));\r
21251             }else if(this.align == 'stretchmax'){\r
21252                 c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(\r
21253                     c.minHeight || 0, c.maxHeight || 1000000));\r
21254             }else{\r
21255                 if(this.align == 'middle'){\r
21256                     var diff = availableHeight - (c.getHeight() + cm.top + cm.bottom);\r
21257                     if(diff > 0){\r
21258                         c.setPosition(c.x, t + cm.top + (diff/2));\r
21259                     }\r
21260                 }\r
21261                 if(isStart && c.flex){\r
21262                     c.setHeight(restore[idx++]);\r
21263                 }\r
21264             }\r
21265         }, this);\r
21266     }\r
21267 \r
21268     /**\r
21269      * @property activeItem\r
21270      * @hide\r
21271      */\r
21272 });\r
21273 \r
21274 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;
21275 /**\r
21276  * @class Ext.Viewport\r
21277  * @extends Ext.Container\r
21278  * <p>A specialized container representing the viewable application area (the browser viewport).</p>\r
21279  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of\r
21280  * the browser viewport and manages window resizing. There may only be one Viewport created\r
21281  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s\r
21282  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}\r
21283  * method of any of its child Panels may themselves have a layout.</p>\r
21284  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide\r
21285  * for scrolling if needed using the {@link #autoScroll} config.</p>\r
21286  * <p>An example showing a classic application border layout:</p><pre><code>\r
21287 new Ext.Viewport({\r
21288     layout: 'border',\r
21289     items: [{\r
21290         region: 'north',\r
21291         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',\r
21292         autoHeight: true,\r
21293         border: false,\r
21294         margins: '0 0 5 0'\r
21295     }, {\r
21296         region: 'west',\r
21297         collapsible: true,\r
21298         title: 'Navigation',\r
21299         width: 200\r
21300         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout} \r
21301     }, {\r
21302         region: 'south',\r
21303         title: 'Title for Panel',\r
21304         collapsible: true,\r
21305         html: 'Information goes here',\r
21306         split: true,\r
21307         height: 100,\r
21308         minHeight: 100\r
21309     }, {\r
21310         region: 'east',\r
21311         title: 'Title for the Grid Panel',\r
21312         collapsible: true,\r
21313         split: true,\r
21314         width: 200,\r
21315         xtype: 'grid',\r
21316         // remaining grid configuration not shown ...\r
21317         // notice that the GridPanel is added directly as the region\r
21318         // it is not "overnested" inside another Panel\r
21319     }, {\r
21320         region: 'center',\r
21321         xtype: 'tabpanel', // TabPanel itself has no title\r
21322         items: {\r
21323             title: 'Default Tab',\r
21324             html: 'The first tab\'s content. Others may be added dynamically'\r
21325         }\r
21326     }]\r
21327 });\r
21328 </code></pre>\r
21329  * @constructor\r
21330  * Create a new Viewport\r
21331  * @param {Object} config The config object\r
21332  * @xtype viewport\r
21333  */\r
21334 Ext.Viewport = Ext.extend(Ext.Container, {\r
21335         /*\r
21336          * Privatize config options which, if used, would interfere with the\r
21337          * correct operation of the Viewport as the sole manager of the\r
21338          * layout of the document body.\r
21339          */\r
21340     /**\r
21341      * @cfg {Mixed} applyTo @hide\r
21342          */\r
21343     /**\r
21344      * @cfg {Boolean} allowDomMove @hide\r
21345          */\r
21346     /**\r
21347      * @cfg {Boolean} hideParent @hide\r
21348          */\r
21349     /**\r
21350      * @cfg {Mixed} renderTo @hide\r
21351          */\r
21352     /**\r
21353      * @cfg {Boolean} hideParent @hide\r
21354          */\r
21355     /**\r
21356      * @cfg {Number} height @hide\r
21357          */\r
21358     /**\r
21359      * @cfg {Number} width @hide\r
21360          */\r
21361     /**\r
21362      * @cfg {Boolean} autoHeight @hide\r
21363          */\r
21364     /**\r
21365      * @cfg {Boolean} autoWidth @hide\r
21366          */\r
21367     /**\r
21368      * @cfg {Boolean} deferHeight @hide\r
21369          */\r
21370     /**\r
21371      * @cfg {Boolean} monitorResize @hide\r
21372          */\r
21373     initComponent : function() {\r
21374         Ext.Viewport.superclass.initComponent.call(this);\r
21375         document.getElementsByTagName('html')[0].className += ' x-viewport';\r
21376         this.el = Ext.getBody();\r
21377         this.el.setHeight = Ext.emptyFn;\r
21378         this.el.setWidth = Ext.emptyFn;\r
21379         this.el.setSize = Ext.emptyFn;\r
21380         this.el.dom.scroll = 'no';\r
21381         this.allowDomMove = false;\r
21382         this.autoWidth = true;\r
21383         this.autoHeight = true;\r
21384         Ext.EventManager.onWindowResize(this.fireResize, this);\r
21385         this.renderTo = this.el;\r
21386     },\r
21387 \r
21388     fireResize : function(w, h){\r
21389         this.fireEvent('resize', this, w, h, w, h);\r
21390     }\r
21391 });\r
21392 Ext.reg('viewport', Ext.Viewport);/**
21393  * @class Ext.Panel
21394  * @extends Ext.Container
21395  * <p>Panel is a container that has specific functionality and structural components that make
21396  * it the perfect building block for application-oriented user interfaces.</p>
21397  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
21398  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
21399  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
21400  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
21401  * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
21402  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
21403  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
21404  * at all.</p>
21405  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
21406  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
21407  * information).</p>
21408  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
21409  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
21410  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
21411  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
21412  * @constructor
21413  * @param {Object} config The config object
21414  * @xtype panel
21415  */
21416 Ext.Panel = Ext.extend(Ext.Container, {
21417     /**
21418      * The Panel's header {@link Ext.Element Element}. Read-only.
21419      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
21420      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
21421      * @type Ext.Element
21422      * @property header
21423      */
21424     /**
21425      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
21426      * The content may be specified in the {@link #html} config, or it may be loaded using the
21427      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
21428      * <p>If this is used to load visible HTML elements in either way, then
21429      * the Panel may not be used as a Layout for hosting nested Panels.</p>
21430      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
21431      * then the body Element must not be loaded or changed - it is under the control
21432      * of the Panel's Layout.
21433      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
21434      * @type Ext.Element
21435      * @property body
21436      */
21437     /**
21438      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
21439      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
21440      * @type Ext.Element
21441      * @property bwrap
21442      */
21443     /**
21444      * True if this panel is collapsed. Read-only.
21445      * @type Boolean
21446      * @property collapsed
21447      */
21448     /**
21449      * @cfg {Object} bodyCfg
21450      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
21451      * Panel Element.</p>
21452      * <p>By default, the Default element in the table below will be used for the html markup to
21453      * create a child element with the commensurate Default class name (<code>baseCls</code> will be
21454      * replaced by <code>{@link #baseCls}</code>):</p>
21455      * <pre>
21456      * Panel      Default  Default             Custom      Additional       Additional
21457      * Element    element  class               element     class            style
21458      * ========   ==========================   =========   ==============   ===========
21459      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
21460      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
21461      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
21462      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
21463      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
21464      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
21465      * </pre>
21466      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
21467      * to use a different form of markup than is created by default. An example of this might be
21468      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
21469      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
21470      * element:</p>
21471      * <pre><code>
21472 new Ext.Panel({
21473     title: 'Message Title',
21474     renderTo: Ext.getBody(),
21475     width: 200, height: 130,
21476     <b>bodyCfg</b>: {
21477         tag: 'center',
21478         cls: 'x-panel-body',  // Default class not applied if Custom element specified
21479         html: 'Message'
21480     },
21481     footerCfg: {
21482         tag: 'h2',
21483         cls: 'x-panel-footer'        // same as the Default class
21484         html: 'footer html'
21485     },
21486     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
21487     footerStyle:    'background-color:red' // see {@link #bodyStyle}
21488 });
21489      * </code></pre>
21490      * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
21491      * styling applied.</p>
21492      */
21493     /**
21494      * @cfg {Object} headerCfg
21495      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
21496      * of this Panel's {@link #header} Element.  See <code>{@link #bodyCfg}</code> also.</p>
21497      */
21498     /**
21499      * @cfg {Object} bwrapCfg
21500      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
21501      * of this Panel's {@link #bwrap} Element.  See <code>{@link #bodyCfg}</code> also.</p>
21502      */
21503     /**
21504      * @cfg {Object} tbarCfg
21505      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
21506      * of this Panel's {@link #tbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
21507      */
21508     /**
21509      * @cfg {Object} bbarCfg
21510      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
21511      * of this Panel's {@link #bbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
21512      */
21513     /**
21514      * @cfg {Object} footerCfg
21515      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
21516      * of this Panel's {@link #footer} Element.  See <code>{@link #bodyCfg}</code> also.</p>
21517      */
21518     /**
21519      * @cfg {Boolean} closable
21520      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
21521      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <code>true</code>
21522      * to enable closing in such situations. Defaults to <code>false</code>.
21523      */
21524     /**
21525      * The Panel's footer {@link Ext.Element Element}. Read-only.
21526      * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
21527      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
21528      * @type Ext.Element
21529      * @property footer
21530      */
21531     /**
21532      * @cfg {Mixed} applyTo
21533      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
21534      * the document that specifies some panel-specific structural markup.  When <code>applyTo</code> is used,
21535      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
21536      * will automatically create those components from that markup. Any required components not specified in the
21537      * markup will be autogenerated if necessary.</p>
21538      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
21539      * <ul><li>baseCls + '-header'</li>
21540      * <li>baseCls + '-header-text'</li>
21541      * <li>baseCls + '-bwrap'</li>
21542      * <li>baseCls + '-tbar'</li>
21543      * <li>baseCls + '-body'</li>
21544      * <li>baseCls + '-bbar'</li>
21545      * <li>baseCls + '-footer'</li></ul>
21546      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
21547      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
21548      * panel's container.</p>
21549      */
21550     /**
21551      * @cfg {Object/Array} tbar
21552      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
21553      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
21554      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
21555      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
21556      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
21557      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
21558      * submission parameters are collected from the DOM tree.</p>
21559      */
21560     /**
21561      * @cfg {Object/Array} bbar
21562      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
21563      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
21564      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
21565      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
21566      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
21567      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
21568      * submission parameters are collected from the DOM tree.</p>
21569      */
21570     /** @cfg {Object/Array} fbar
21571      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
21572      * {@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>
21573      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
21574      * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
21575      * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
21576      * <pre><code>
21577 var w = new Ext.Window({
21578     height: 250,
21579     width: 500,
21580     bbar: new Ext.Toolbar({
21581         items: [{
21582             text: 'bbar Left'
21583         },'->',{
21584             text: 'bbar Right'
21585         }]
21586     }),
21587     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
21588                                   // to control the alignment of fbar items
21589     fbar: [{
21590         text: 'fbar Left'
21591     },'->',{
21592         text: 'fbar Right'
21593     }]
21594 }).show();
21595      * </code></pre>
21596      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
21597      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
21598      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
21599      * submission parameters are collected from the DOM tree.</p>
21600      */
21601     /**
21602      * @cfg {Boolean} header
21603      * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
21604      * it.  If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
21605      * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
21606      * will not be rendered.
21607      */
21608     /**
21609      * @cfg {Boolean} footer
21610      * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
21611      * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
21612      * been configured.  See <code>{@link #bodyCfg}</code> for an example.
21613      */
21614     /**
21615      * @cfg {String} title
21616      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
21617      * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
21618      * <code>{@link #header}</code> element will automatically be created and displayed unless
21619      * {@link #header} is explicitly set to <code>false</code>.  If you do not want to specify a
21620      * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
21621      * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
21622      * element will get created.
21623      */
21624     /**
21625      * @cfg {Array} buttons
21626      * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
21627      * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
21628      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
21629      * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
21630      * that width will be applied to the item.
21631      */
21632     /**
21633      * @cfg {Object/String/Function} autoLoad
21634      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
21635      * If autoLoad is not null, the panel will attempt to load its contents
21636      * immediately upon render.<p>
21637      * The URL will become the default URL for this panel's {@link #body} element,
21638      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
21639      */
21640     /**
21641      * @cfg {Boolean} frame
21642      * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
21643      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
21644      * <p>The template generated for each condition is depicted below:</p><pre><code>
21645      *
21646 // frame = false
21647 &lt;div id="developer-specified-id-goes-here" class="x-panel">
21648
21649     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
21650
21651     &lt;div class="x-panel-bwrap">
21652         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
21653     &lt;/div>
21654 &lt;/div>
21655
21656 // frame = true (create 9 elements)
21657 &lt;div id="developer-specified-id-goes-here" class="x-panel">
21658     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
21659         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
21660     &lt;/div>&lt;/div>&lt;/div>
21661
21662     &lt;div class="x-panel-bwrap">
21663         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
21664             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
21665         &lt;/div>&lt;/div>&lt;/div>
21666
21667         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
21668         &lt;/div>&lt;/div>&lt;/div>
21669 &lt;/div>
21670      * </code></pre>
21671      */
21672     /**
21673      * @cfg {Boolean} border
21674      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
21675      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
21676      */
21677     /**
21678      * @cfg {Boolean} bodyBorder
21679      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
21680      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
21681      * as a 1px wide inset border, giving the entire body element an inset appearance.
21682      */
21683     /**
21684      * @cfg {String/Object/Function} bodyCssClass
21685      * Additional css class selector to be applied to the {@link #body} element in the format expected by
21686      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
21687      */
21688     /**
21689      * @cfg {String/Object/Function} bodyStyle
21690      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
21691      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
21692      */
21693     /**
21694      * @cfg {String} iconCls
21695      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
21696      * <p>An example of specifying a custom icon class would be something like:
21697      * </p><pre><code>
21698 // specify the property in the config for the class:
21699      ...
21700      iconCls: 'my-icon'
21701
21702 // css class that specifies background image to be used as the icon image:
21703 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
21704 </code></pre>
21705      */
21706     /**
21707      * @cfg {Boolean} collapsible
21708      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
21709      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
21710      */
21711     /**
21712      * @cfg {Array} tools
21713      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
21714      * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i>&lt;tool-type&gt;</i></code>
21715      * <p>Each tool config may contain the following properties:
21716      * <div class="mdetail-params"><ul>
21717      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
21718      * of tool to create. By default, this assigns a CSS class of the form <code>x-tool-<i>&lt;tool-type&gt;</i></code> to the
21719      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
21720      * The developer may implement custom tools by supplying alternate CSS rules and background images:
21721      * <ul>
21722      * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><code> toggle</code> (Created by default when {@link #collapsible} is <code>true</code>)</div>
21723      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
21724      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
21725      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
21726      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
21727      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
21728      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
21729      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
21730      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
21731      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
21732      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
21733      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
21734      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
21735      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
21736      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
21737      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
21738      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
21739      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
21740      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
21741      * </ul></div></li>
21742      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
21743      * call when clicked. Arguments passed are:<ul>
21744      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
21745      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
21746      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
21747      * <li><b>tc</b> : Ext.Panel<div class="sub-desc">The tool configuration object</div></li>
21748      * </ul></div></li>
21749      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
21750      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
21751      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
21752      * a config argument to {@link Ext.QuickTip#register}</div></li>
21753      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
21754      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
21755      * event listeners in the format of an argument to {@link #addListener}</div></li>
21756      * </ul></div>
21757      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
21758      * tools only provide the visual button. Any required functionality must be provided by adding
21759      * handlers that implement the necessary behavior.</p>
21760      * <p>Example usage:</p>
21761      * <pre><code>
21762 tools:[{
21763     id:'refresh',
21764     qtip: 'Refresh form Data',
21765     // hidden:true,
21766     handler: function(event, toolEl, panel){
21767         // refresh logic
21768     }
21769 },
21770 {
21771     id:'help',
21772     qtip: 'Get Help',
21773     handler: function(event, toolEl, panel){
21774         // whatever
21775     }
21776 }]
21777 </code></pre>
21778      * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
21779      * a 15x15 image:</p>
21780      * <pre><code>
21781 .x-tool-help {background-image: url(images/help.png);}
21782 .x-tool-help-over {background-image: url(images/help_over.png);}
21783 // if using an image sprite:
21784 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
21785 .x-tool-help-over {background-position:-15px 0;}
21786 </code></pre>
21787      */
21788     /**
21789      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
21790      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
21791 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
21792      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
21793      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
21794      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
21795      * visual indication when hovering over the tool:</p><pre><code>
21796 var win = new Ext.Window({
21797     tools: [{
21798         id: 'download',
21799         href: '/MyPdfDoc.pdf'
21800     }],
21801     toolTemplate: new Ext.XTemplate(
21802         '&lt;tpl if="id==\'download\'">',
21803             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
21804         '&lt;/tpl>',
21805         '&lt;tpl if="id!=\'download\'">',
21806             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
21807         '&lt;/tpl>'
21808     ),
21809     width:500,
21810     height:300,
21811     closeAction:'hide'
21812 });</code></pre>
21813      * <p>Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an
21814      * appropriate background image, something like:</p>
21815     <pre><code>
21816     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
21817     </code></pre>
21818      */
21819     /**
21820      * @cfg {Boolean} hideCollapseTool
21821      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
21822      * <code>false</code> to display it (defaults to <code>false</code>).
21823      */
21824     /**
21825      * @cfg {Boolean} titleCollapse
21826      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
21827      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
21828      * (defaults to <code>false</code>)). If this panel is a child item of a border layout also see the
21829      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
21830      * <code>{@link Ext.layout.BorderLayout.Region#floatable floatable}</code> config option.
21831      */
21832     /**
21833      * @cfg {Boolean} autoScroll
21834      * <code>true</code> to use overflow:'auto' on the panel's body element and show scroll bars automatically when
21835      * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
21836      */
21837     /**
21838      * @cfg {Mixed} floating
21839      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
21840      * configuration property are:</p><div class="mdetail-params"><ul>
21841      * <li><b><code>false</code></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
21842      * rendered.</div></li>
21843      * <li><b><code>true</code></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
21844      * shimming and shadow).<ul>
21845      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
21846      * panel at negative offsets so that it is hidden.</div>
21847      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
21848      * <i>after</i> render (e.g., <code>myPanel.setPosition(100,100);</code>).</div>
21849      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
21850      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
21851      * </ul></div></li>
21852      * <li><b><code>{@link Ext.Layer object}</code></b> : <div class="sub-desc">The specified object will be used
21853      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
21854      * </ul></div>
21855      */
21856     /**
21857      * @cfg {Boolean/String} shadow
21858      * <code>true</code> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
21859      * panel, <code>false</code> to display no shadow (defaults to <code>'sides'</code>).  Note that this option
21860      * only applies when <code>{@link #floating} = true</code>.
21861      */
21862     /**
21863      * @cfg {Number} shadowOffset
21864      * The number of pixels to offset the shadow if displayed (defaults to <code>4</code>). Note that this
21865      * option only applies when <code>{@link #floating} = true</code>.
21866      */
21867     /**
21868      * @cfg {Boolean} shim
21869      * <code>false</code> to disable the iframe shim in browsers which need one (defaults to <code>true</code>).
21870      * Note that this option only applies when <code>{@link #floating} = true</code>.
21871      */
21872     /**
21873      * @cfg {String/Object} html
21874      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the panel's body
21875      * content (defaults to ''). The HTML content is added by the Panel's {@link #afterRender} method,
21876      * and so the document will not contain this HTML at the time the {@link #render} event is fired.
21877      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
21878      */
21879     /**
21880      * @cfg {String} contentEl
21881      * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as this Panel's
21882      * <code><b>{@link #body}</b></code> content.</p>
21883      * <ul>
21884      * <li><b>Description</b> :
21885      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the body
21886      * of a new panel (it simply moves the specified DOM element into the body element of the Panel
21887      * <i>after the Panel is rendered</i> to use as the content (it is not going to be the actual panel itself).</div></li>
21888      * <li><b>Notes</b> :
21889      * <div class="sub-desc">The specified HTML element is appended to the Panel's {@link #body} Element by the
21890      * Panel's <code>afterRender</code> method <i>after any configured {@link #html HTML} has
21891      * been inserted</i>, and so the document will not contain this element at the time the
21892      * {@link #render} event is fired.</div>
21893      * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
21894      * scheme that the Panel may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
21895      * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
21896      * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
21897      * </ul>
21898      */
21899     /**
21900      * @cfg {Object/Array} keys
21901      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
21902      * used to assign custom key handling to this panel (defaults to <code>null</code>).
21903      */
21904     /**
21905      * @cfg {Boolean/Object} draggable
21906      * <p><code>true</code> to enable dragging of this Panel (defaults to <code>false</code>).</p>
21907      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
21908      * in this config instead of <code>true</code>. Ext.Panel.DD is an internal, undocumented class which
21909      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
21910      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
21911      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
21912      * <pre><code>
21913 new Ext.Panel({
21914     title: 'Drag me',
21915     x: 100,
21916     y: 100,
21917     renderTo: Ext.getBody(),
21918     floating: true,
21919     frame: true,
21920     width: 400,
21921     height: 200,
21922     draggable: {
21923 //      Config option of Ext.Panel.DD class.
21924 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
21925         insertProxy: false,
21926
21927 //      Called for each mousemove event while dragging the DD object.
21928         onDrag : function(e){
21929 //          Record the x,y position of the drag proxy so that we can
21930 //          position the Panel at end of drag.
21931             var pel = this.proxy.getEl();
21932             this.x = pel.getLeft(true);
21933             this.y = pel.getTop(true);
21934
21935 //          Keep the Shadow aligned if there is one.
21936             var s = this.panel.getEl().shadow;
21937             if (s) {
21938                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
21939             }
21940         },
21941
21942 //      Called on the mouseup event.
21943         endDrag : function(e){
21944             this.panel.setPosition(this.x, this.y);
21945         }
21946     }
21947 }).show();
21948 </code></pre>
21949      */
21950     /**
21951      * @cfg {Boolean} disabled
21952      * Render this panel disabled (default is <code>false</code>). An important note when using the disabled
21953      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
21954      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
21955      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
21956      * the disabled state:
21957      * <pre><code>
21958 new Ext.Panel({
21959     ...
21960     listeners: {
21961         'afterlayout': {
21962             fn: function(p){
21963                 p.disable();
21964             },
21965             single: true // important, as many layouts can occur
21966         }
21967     }
21968 });
21969 </code></pre>
21970      */
21971     /**
21972      * @cfg {Boolean} autoHeight
21973      * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
21974      * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
21975      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
21976      * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
21977      * can cause issues with scrolling and will not generally work as expected since the panel will take
21978      * on the height of its contents rather than the height required by the Ext layout.
21979      */
21980
21981
21982     /**
21983      * @cfg {String} baseCls
21984      * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
21985      * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
21986      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
21987      * See <code>{@link #unstyled}</code> also.</p>
21988      */
21989     baseCls : 'x-panel',
21990     /**
21991      * @cfg {String} collapsedCls
21992      * A CSS class to add to the panel's element after it has been collapsed (defaults to
21993      * <code>'x-panel-collapsed'</code>).
21994      */
21995     collapsedCls : 'x-panel-collapsed',
21996     /**
21997      * @cfg {Boolean} maskDisabled
21998      * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
21999      * to <code>true</code>).  Either way, the panel will always tell its contained elements to disable themselves
22000      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
22001      * disabled.
22002      */
22003     maskDisabled : true,
22004     /**
22005      * @cfg {Boolean} animCollapse
22006      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
22007      * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
22008      */
22009     animCollapse : Ext.enableFx,
22010     /**
22011      * @cfg {Boolean} headerAsText
22012      * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
22013      * <code>false</code> to hide it (defaults to <code>true</code>).
22014      */
22015     headerAsText : true,
22016     /**
22017      * @cfg {String} buttonAlign
22018      * The alignment of any {@link #buttons} added to this panel.  Valid values are <code>'right'</code>,
22019      * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
22020      */
22021     buttonAlign : 'right',
22022     /**
22023      * @cfg {Boolean} collapsed
22024      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
22025      * <code>false</code>).
22026      */
22027     collapsed : false,
22028     /**
22029      * @cfg {Boolean} collapseFirst
22030      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
22031      * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
22032      */
22033     collapseFirst : true,
22034     /**
22035      * @cfg {Number} minButtonWidth
22036      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
22037      */
22038     minButtonWidth : 75,
22039     /**
22040      * @cfg {Boolean} unstyled
22041      * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
22042      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
22043      */
22044     /**
22045      * @cfg {String} elements
22046      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
22047      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
22048      * make sure a structural element is rendered even if not specified at config time (for example, you may want
22049      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
22050      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
22051      * <li><code>header</code></li>
22052      * <li><code>tbar</code> (top bar)</li>
22053      * <li><code>body</code></li>
22054      * <li><code>bbar</code> (bottom bar)</li>
22055      * <li><code>footer</code></li>
22056      * </ul></div>
22057      * Defaults to '<code>body</code>'.
22058      */
22059     elements : 'body',
22060     /**
22061      * @cfg {Boolean} preventBodyReset
22062      * Defaults to <code>false</code>.  When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
22063      * will be added to the panel's element, effectively applying css styles suggested by the W3C
22064      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
22065      * footer, etc.).
22066      */
22067     preventBodyReset : false,
22068     
22069     /** @cfg {String} resizeEvent
22070      * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
22071      */
22072     resizeEvent: 'bodyresize',
22073
22074     // protected - these could be used to customize the behavior of the window,
22075     // but changing them would not be useful without further mofifications and
22076     // could lead to unexpected or undesirable results.
22077     toolTarget : 'header',
22078     collapseEl : 'bwrap',
22079     slideAnchor : 't',
22080     disabledClass : '',
22081
22082     // private, notify box this class will handle heights
22083     deferHeight : true,
22084     // private
22085     expandDefaults: {
22086         duration : 0.25
22087     },
22088     // private
22089     collapseDefaults : {
22090         duration : 0.25
22091     },
22092
22093     // private
22094     initComponent : function(){
22095         Ext.Panel.superclass.initComponent.call(this);
22096
22097         this.addEvents(
22098             /**
22099              * @event bodyresize
22100              * Fires after the Panel has been resized.
22101              * @param {Ext.Panel} p the Panel which has been resized.
22102              * @param {Number} width The Panel's new width.
22103              * @param {Number} height The Panel's new height.
22104              */
22105             'bodyresize',
22106             /**
22107              * @event titlechange
22108              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
22109              * @param {Ext.Panel} p the Panel which has had its title changed.
22110              * @param {String} The new title.
22111              */
22112             'titlechange',
22113             /**
22114              * @event iconchange
22115              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
22116              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
22117              * @param {String} The new icon class.
22118              * @param {String} The old icon class.
22119              */
22120             'iconchange',
22121             /**
22122              * @event collapse
22123              * Fires after the Panel has been collapsed.
22124              * @param {Ext.Panel} p the Panel that has been collapsed.
22125              */
22126             'collapse',
22127             /**
22128              * @event expand
22129              * Fires after the Panel has been expanded.
22130              * @param {Ext.Panel} p The Panel that has been expanded.
22131              */
22132             'expand',
22133             /**
22134              * @event beforecollapse
22135              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
22136              * @param {Ext.Panel} p the Panel being collapsed.
22137              * @param {Boolean} animate True if the collapse is animated, else false.
22138              */
22139             'beforecollapse',
22140             /**
22141              * @event beforeexpand
22142              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
22143              * @param {Ext.Panel} p The Panel being expanded.
22144              * @param {Boolean} animate True if the expand is animated, else false.
22145              */
22146             'beforeexpand',
22147             /**
22148              * @event beforeclose
22149              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
22150              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
22151              * applies to such subclasses.
22152              * A handler can return false to cancel the close.
22153              * @param {Ext.Panel} p The Panel being closed.
22154              */
22155             'beforeclose',
22156             /**
22157              * @event close
22158              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
22159              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
22160              * @param {Ext.Panel} p The Panel that has been closed.
22161              */
22162             'close',
22163             /**
22164              * @event activate
22165              * Fires after the Panel has been visually activated.
22166              * Note that Panels do not directly support being activated, but some Panel subclasses
22167              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
22168              * activate and deactivate events under the control of the TabPanel.
22169              * @param {Ext.Panel} p The Panel that has been activated.
22170              */
22171             'activate',
22172             /**
22173              * @event deactivate
22174              * Fires after the Panel has been visually deactivated.
22175              * Note that Panels do not directly support being deactivated, but some Panel subclasses
22176              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
22177              * activate and deactivate events under the control of the TabPanel.
22178              * @param {Ext.Panel} p The Panel that has been deactivated.
22179              */
22180             'deactivate'
22181         );
22182
22183         if(this.unstyled){
22184             this.baseCls = 'x-plain';
22185         }
22186
22187         // shortcuts
22188         if(this.tbar){
22189             this.elements += ',tbar';
22190             if(Ext.isObject(this.tbar)){
22191                 this.topToolbar = this.tbar;
22192             }
22193             delete this.tbar;
22194         }
22195         if(this.bbar){
22196             this.elements += ',bbar';
22197             if(Ext.isObject(this.bbar)){
22198                 this.bottomToolbar = this.bbar;
22199             }
22200             delete this.bbar;
22201         }
22202
22203         if(this.header === true){
22204             this.elements += ',header';
22205             delete this.header;
22206         }else if(this.headerCfg || (this.title && this.header !== false)){
22207             this.elements += ',header';
22208         }
22209
22210         if(this.footerCfg || this.footer === true){
22211             this.elements += ',footer';
22212             delete this.footer;
22213         }
22214
22215         if(this.buttons){
22216             this.elements += ',footer';
22217             var btns = this.buttons;
22218             /**
22219              * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
22220              * config property. Read only.
22221              * @type Array
22222              * @property buttons
22223              */
22224             this.buttons = [];
22225             Ext.each(btns, function(btn){
22226                 if(btn.render){ // button instance
22227                     this.buttons.push(btn);
22228                 }else if(btn.xtype){
22229                     this.buttons.push(Ext.create(btn, 'button'));
22230                 }else{
22231                     this.addButton(btn);
22232                 }
22233             }, this);
22234         }
22235         if(this.fbar){
22236             this.elements += ',footer';
22237         }
22238         if(this.autoLoad){
22239             this.on('render', this.doAutoLoad, this, {delay:10});
22240         }
22241     },
22242
22243     // private
22244     createElement : function(name, pnode){
22245         if(this[name]){
22246             pnode.appendChild(this[name].dom);
22247             return;
22248         }
22249
22250         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
22251             if(this[name+'Cfg']){
22252                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
22253             }else{
22254                 var el = document.createElement('div');
22255                 el.className = this[name+'Cls'];
22256                 this[name] = Ext.get(pnode.appendChild(el));
22257             }
22258             if(this[name+'CssClass']){
22259                 this[name].addClass(this[name+'CssClass']);
22260             }
22261             if(this[name+'Style']){
22262                 this[name].applyStyles(this[name+'Style']);
22263             }
22264         }
22265     },
22266
22267     // private
22268     onRender : function(ct, position){
22269         Ext.Panel.superclass.onRender.call(this, ct, position);
22270         this.createClasses();
22271
22272         var el = this.el,
22273             d = el.dom,
22274             bw,
22275             ts;
22276             
22277             
22278         if(this.collapsible && !this.hideCollapseTool){
22279             this.tools = this.tools ? this.tools.slice(0) : [];
22280             this.tools[this.collapseFirst?'unshift':'push']({
22281                 id: 'toggle',
22282                 handler : this.toggleCollapse,
22283                 scope: this
22284             });   
22285         }
22286         
22287         if(this.tools){
22288             ts = this.tools;
22289             this.elements += (this.header !== false) ? ',header' : '';
22290         }
22291         this.tools = {};
22292             
22293         el.addClass(this.baseCls);
22294         if(d.firstChild){ // existing markup
22295             this.header = el.down('.'+this.headerCls);
22296             this.bwrap = el.down('.'+this.bwrapCls);
22297             var cp = this.bwrap ? this.bwrap : el;
22298             this.tbar = cp.down('.'+this.tbarCls);
22299             this.body = cp.down('.'+this.bodyCls);
22300             this.bbar = cp.down('.'+this.bbarCls);
22301             this.footer = cp.down('.'+this.footerCls);
22302             this.fromMarkup = true;
22303         }
22304         if (this.preventBodyReset === true) {
22305             el.addClass('x-panel-reset');
22306         }
22307         if(this.cls){
22308             el.addClass(this.cls);
22309         }
22310
22311         if(this.buttons){
22312             this.elements += ',footer';
22313         }
22314
22315         // This block allows for maximum flexibility and performance when using existing markup
22316
22317         // framing requires special markup
22318         if(this.frame){
22319             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
22320
22321             this.createElement('header', d.firstChild.firstChild.firstChild);
22322             this.createElement('bwrap', d);
22323
22324             // append the mid and bottom frame to the bwrap
22325             bw = this.bwrap.dom;
22326             var ml = d.childNodes[1], bl = d.childNodes[2];
22327             bw.appendChild(ml);
22328             bw.appendChild(bl);
22329
22330             var mc = bw.firstChild.firstChild.firstChild;
22331             this.createElement('tbar', mc);
22332             this.createElement('body', mc);
22333             this.createElement('bbar', mc);
22334             this.createElement('footer', bw.lastChild.firstChild.firstChild);
22335
22336             if(!this.footer){
22337                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
22338             }
22339             /*
22340              * Store a reference to this element so:
22341              * a) We aren't looking it up all the time
22342              * b) The last element is reported incorrectly when using a loadmask
22343              */
22344             this.ft = Ext.get(this.bwrap.dom.lastChild);
22345             this.mc = Ext.get(this.bwrap.dom.firstChild.firstChild.firstChild);
22346         }else{
22347             this.createElement('header', d);
22348             this.createElement('bwrap', d);
22349
22350             // append the mid and bottom frame to the bwrap
22351             bw = this.bwrap.dom;
22352             this.createElement('tbar', bw);
22353             this.createElement('body', bw);
22354             this.createElement('bbar', bw);
22355             this.createElement('footer', bw);
22356
22357             if(!this.header){
22358                 this.body.addClass(this.bodyCls + '-noheader');
22359                 if(this.tbar){
22360                     this.tbar.addClass(this.tbarCls + '-noheader');
22361                 }
22362             }
22363         }
22364
22365         if(Ext.isDefined(this.padding)){
22366             this.body.setStyle('padding', this.body.addUnits(this.padding));
22367         }
22368
22369         if(this.border === false){
22370             this.el.addClass(this.baseCls + '-noborder');
22371             this.body.addClass(this.bodyCls + '-noborder');
22372             if(this.header){
22373                 this.header.addClass(this.headerCls + '-noborder');
22374             }
22375             if(this.footer){
22376                 this.footer.addClass(this.footerCls + '-noborder');
22377             }
22378             if(this.tbar){
22379                 this.tbar.addClass(this.tbarCls + '-noborder');
22380             }
22381             if(this.bbar){
22382                 this.bbar.addClass(this.bbarCls + '-noborder');
22383             }
22384         }
22385
22386         if(this.bodyBorder === false){
22387            this.body.addClass(this.bodyCls + '-noborder');
22388         }
22389
22390         this.bwrap.enableDisplayMode('block');
22391
22392         if(this.header){
22393             this.header.unselectable();
22394
22395             // for tools, we need to wrap any existing header markup
22396             if(this.headerAsText){
22397                 this.header.dom.innerHTML =
22398                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
22399
22400                 if(this.iconCls){
22401                     this.setIconClass(this.iconCls);
22402                 }
22403             }
22404         }
22405
22406         if(this.floating){
22407             this.makeFloating(this.floating);
22408         }
22409
22410         if(this.collapsible && this.titleCollapse && this.header){
22411             this.mon(this.header, 'click', this.toggleCollapse, this);
22412             this.header.setStyle('cursor', 'pointer');
22413         }
22414         if(ts){
22415             this.addTool.apply(this, ts);
22416         }
22417
22418         if(this.buttons && this.buttons.length > 0){
22419             this.fbar = new Ext.Toolbar({
22420                 items: this.buttons,
22421                 toolbarCls: 'x-panel-fbar'
22422             });
22423         }
22424         this.toolbars = [];
22425         if(this.fbar){
22426             this.fbar = Ext.create(this.fbar, 'toolbar');
22427             this.fbar.enableOverflow = false;
22428             if(this.fbar.items){
22429                 this.fbar.items.each(function(c){
22430                     c.minWidth = c.minWidth || this.minButtonWidth;
22431                 }, this);
22432             }
22433             this.fbar.toolbarCls = 'x-panel-fbar';
22434
22435             var bct = this.footer.createChild({cls: 'x-panel-btns x-panel-btns-'+this.buttonAlign});
22436             this.fbar.ownerCt = this;
22437             this.fbar.render(bct);
22438             bct.createChild({cls:'x-clear'});
22439             this.toolbars.push(this.fbar);
22440         }
22441
22442         if(this.tbar && this.topToolbar){
22443             if(Ext.isArray(this.topToolbar)){
22444                 this.topToolbar = new Ext.Toolbar(this.topToolbar);
22445             }else if(!this.topToolbar.events){
22446                 this.topToolbar = Ext.create(this.topToolbar, 'toolbar');
22447             }
22448             this.topToolbar.ownerCt = this;
22449             this.topToolbar.render(this.tbar);
22450             this.toolbars.push(this.topToolbar);
22451         }
22452         if(this.bbar && this.bottomToolbar){
22453             if(Ext.isArray(this.bottomToolbar)){
22454                 this.bottomToolbar = new Ext.Toolbar(this.bottomToolbar);
22455             }else if(!this.bottomToolbar.events){
22456                 this.bottomToolbar = Ext.create(this.bottomToolbar, 'toolbar');
22457             }
22458             this.bottomToolbar.ownerCt = this;
22459             this.bottomToolbar.render(this.bbar);
22460             this.toolbars.push(this.bottomToolbar);
22461         }
22462     },
22463
22464     /**
22465      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
22466      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
22467      * @param {String} cls The new CSS class name
22468      */
22469     setIconClass : function(cls){
22470         var old = this.iconCls;
22471         this.iconCls = cls;
22472         if(this.rendered && this.header){
22473             if(this.frame){
22474                 this.header.addClass('x-panel-icon');
22475                 this.header.replaceClass(old, this.iconCls);
22476             }else{
22477                 var hd = this.header,
22478                     img = hd.child('img.x-panel-inline-icon');
22479                 if(img){
22480                     Ext.fly(img).replaceClass(old, this.iconCls);
22481                 }else{
22482                     Ext.DomHelper.insertBefore(hd.dom.firstChild, {
22483                         tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
22484                     });
22485                  }
22486             }
22487         }
22488         this.fireEvent('iconchange', this, cls, old);
22489     },
22490
22491     // private
22492     makeFloating : function(cfg){
22493         this.floating = true;
22494         this.el = new Ext.Layer(
22495             Ext.isObject(cfg) ? cfg : {
22496                 shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
22497                 shadowOffset: this.shadowOffset,
22498                 constrain:false,
22499                 shim: this.shim === false ? false : undefined
22500             }, this.el
22501         );
22502     },
22503
22504     /**
22505      * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
22506      * @return {Ext.Toolbar} The toolbar
22507      */
22508     getTopToolbar : function(){
22509         return this.topToolbar;
22510     },
22511
22512     /**
22513      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
22514      * @return {Ext.Toolbar} The toolbar
22515      */
22516     getBottomToolbar : function(){
22517         return this.bottomToolbar;
22518     },
22519
22520     /**
22521      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
22522      * approach is to add buttons via the {@link #buttons} config.
22523      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
22524      * button config, an object will be treated as a button config object.
22525      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
22526      * @param {Object} scope The scope to use for the button handler function
22527      * @return {Ext.Button} The button that was added
22528      */
22529     addButton : function(config, handler, scope){
22530         var bc = {
22531             handler: handler,
22532             scope: scope,
22533             minWidth: this.minButtonWidth,
22534             hideParent:true
22535         };
22536         if(Ext.isString(config)){
22537             bc.text = config;
22538         }else{
22539             Ext.apply(bc, config);
22540         }
22541         var btn = new Ext.Button(bc);
22542         if(!this.buttons){
22543             this.buttons = [];
22544         }
22545         this.buttons.push(btn);
22546         return btn;
22547     },
22548
22549     // private
22550     addTool : function(){
22551         if(!this.rendered){
22552             if(!this.tools){
22553                 this.tools = [];
22554             }
22555             Ext.each(arguments, function(arg){
22556                 this.tools.push(arg)
22557             }, this);
22558             return;
22559         }
22560          // nowhere to render tools!
22561         if(!this[this.toolTarget]){
22562             return;
22563         }
22564         if(!this.toolTemplate){
22565             // initialize the global tool template on first use
22566             var tt = new Ext.Template(
22567                  '<div class="x-tool x-tool-{id}">&#160;</div>'
22568             );
22569             tt.disableFormats = true;
22570             tt.compile();
22571             Ext.Panel.prototype.toolTemplate = tt;
22572         }
22573         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
22574             var tc = a[i];
22575             if(!this.tools[tc.id]){
22576                 var overCls = 'x-tool-'+tc.id+'-over';
22577                 var t = this.toolTemplate.insertFirst((tc.align !== 'left') ? this[this.toolTarget] : this[this.toolTarget].child('span'), tc, true);
22578                 this.tools[tc.id] = t;
22579                 t.enableDisplayMode('block');
22580                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
22581                 if(tc.on){
22582                     this.mon(t, tc.on);
22583                 }
22584                 if(tc.hidden){
22585                     t.hide();
22586                 }
22587                 if(tc.qtip){
22588                     if(Ext.isObject(tc.qtip)){
22589                         Ext.QuickTips.register(Ext.apply({
22590                               target: t.id
22591                         }, tc.qtip));
22592                     } else {
22593                         t.dom.qtip = tc.qtip;
22594                     }
22595                 }
22596                 t.addClassOnOver(overCls);
22597             }
22598         }
22599     },
22600
22601     onLayout : function(shallow, force){
22602         if(this.hasLayout && this.toolbars.length > 0){
22603             Ext.each(this.toolbars, function(tb){
22604                 tb.doLayout(undefined, force);
22605             });
22606             this.syncHeight();
22607         }
22608     },
22609
22610     syncHeight : function(){
22611         var h = this.toolbarHeight,
22612                 bd = this.body,
22613                 lsh = this.lastSize.height;
22614                 
22615         if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
22616             return;
22617         }
22618             
22619            
22620         if(h != this.getToolbarHeight()){
22621             h = Math.max(0, this.adjustBodyHeight(lsh - this.getFrameHeight()));
22622             bd.setHeight(h);
22623             sz = bd.getSize();
22624             this.toolbarHeight = this.getToolbarHeight();
22625             this.onBodyResize(sz.width, sz.height);
22626         }
22627     },
22628
22629     // private
22630     onShow : function(){
22631         if(this.floating){
22632             return this.el.show();
22633         }
22634         Ext.Panel.superclass.onShow.call(this);
22635     },
22636
22637     // private
22638     onHide : function(){
22639         if(this.floating){
22640             return this.el.hide();
22641         }
22642         Ext.Panel.superclass.onHide.call(this);
22643     },
22644
22645     // private
22646     createToolHandler : function(t, tc, overCls, panel){
22647         return function(e){
22648             t.removeClass(overCls);
22649             if(tc.stopEvent !== false){
22650                 e.stopEvent();
22651             }
22652             if(tc.handler){
22653                 tc.handler.call(tc.scope || t, e, t, panel, tc);
22654             }
22655         };
22656     },
22657
22658     // private
22659     afterRender : function(){
22660         if(this.floating && !this.hidden){
22661             this.el.show();
22662         }
22663         if(this.title){
22664             this.setTitle(this.title);
22665         }
22666         this.setAutoScroll();
22667         if(this.html){
22668             this.body.update(Ext.isObject(this.html) ?
22669                              Ext.DomHelper.markup(this.html) :
22670                              this.html);
22671             delete this.html;
22672         }
22673         if(this.contentEl){
22674             var ce = Ext.getDom(this.contentEl);
22675             Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
22676             this.body.dom.appendChild(ce);
22677         }
22678         if(this.collapsed){
22679             this.collapsed = false;
22680             this.collapse(false);
22681         }
22682         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
22683         this.initEvents();
22684     },
22685
22686     // private
22687     setAutoScroll : function(){
22688         if(this.rendered && this.autoScroll){
22689             var el = this.body || this.el;
22690             if(el){
22691                 el.setOverflow('auto');
22692             }
22693         }
22694     },
22695
22696     // private
22697     getKeyMap : function(){
22698         if(!this.keyMap){
22699             this.keyMap = new Ext.KeyMap(this.el, this.keys);
22700         }
22701         return this.keyMap;
22702     },
22703
22704     // private
22705     initEvents : function(){
22706         if(this.keys){
22707             this.getKeyMap();
22708         }
22709         if(this.draggable){
22710             this.initDraggable();
22711         }
22712         if(this.toolbars.length > 0){
22713             Ext.each(this.toolbars, function(tb){
22714                 tb.doLayout();
22715                 tb.on({
22716                     scope: this,
22717                     afterlayout: this.syncHeight,
22718                     remove: this.syncHeight
22719                 });
22720             }, this);
22721             if(!this.ownerCt){
22722                 this.syncHeight();
22723             }
22724         }
22725     },
22726
22727     // private
22728     initDraggable : function(){
22729         /**
22730          * <p>If this Panel is configured {@link #draggable}, this property will contain
22731          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
22732          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
22733          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
22734          * @type Ext.dd.DragSource.
22735          * @property dd
22736          */
22737         this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
22738     },
22739
22740     // private
22741     beforeEffect : function(anim){
22742         if(this.floating){
22743             this.el.beforeAction();
22744         }
22745         if(anim !== false){
22746             this.el.addClass('x-panel-animated');
22747         }
22748     },
22749
22750     // private
22751     afterEffect : function(anim){
22752         this.syncShadow();
22753         if(anim !== false){
22754             this.el.removeClass('x-panel-animated');
22755         }
22756     },
22757
22758     // private - wraps up an animation param with internal callbacks
22759     createEffect : function(a, cb, scope){
22760         var o = {
22761             scope:scope,
22762             block:true
22763         };
22764         if(a === true){
22765             o.callback = cb;
22766             return o;
22767         }else if(!a.callback){
22768             o.callback = cb;
22769         }else { // wrap it up
22770             o.callback = function(){
22771                 cb.call(scope);
22772                 Ext.callback(a.callback, a.scope);
22773             };
22774         }
22775         return Ext.applyIf(o, a);
22776     },
22777
22778     /**
22779      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
22780      * cancel the collapse action if it returns false.
22781      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22782      * {@link #animCollapse} panel config)
22783      * @return {Ext.Panel} this
22784      */
22785     collapse : function(animate){
22786         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
22787             return;
22788         }
22789         var doAnim = animate === true || (animate !== false && this.animCollapse);
22790         this.beforeEffect(doAnim);
22791         this.onCollapse(doAnim, animate);
22792         return this;
22793     },
22794
22795     // private
22796     onCollapse : function(doAnim, animArg){
22797         if(doAnim){
22798             this[this.collapseEl].slideOut(this.slideAnchor,
22799                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
22800                         this.collapseDefaults));
22801         }else{
22802             this[this.collapseEl].hide();
22803             this.afterCollapse(false);
22804         }
22805     },
22806
22807     // private
22808     afterCollapse : function(anim){
22809         this.collapsed = true;
22810         this.el.addClass(this.collapsedCls);
22811         this.afterEffect(anim);
22812         this.fireEvent('collapse', this);
22813     },
22814
22815     /**
22816      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
22817      * cancel the expand action if it returns false.
22818      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22819      * {@link #animCollapse} panel config)
22820      * @return {Ext.Panel} this
22821      */
22822     expand : function(animate){
22823         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
22824             return;
22825         }
22826         var doAnim = animate === true || (animate !== false && this.animCollapse);
22827         this.el.removeClass(this.collapsedCls);
22828         this.beforeEffect(doAnim);
22829         this.onExpand(doAnim, animate);
22830         return this;
22831     },
22832
22833     // private
22834     onExpand : function(doAnim, animArg){
22835         if(doAnim){
22836             this[this.collapseEl].slideIn(this.slideAnchor,
22837                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
22838                         this.expandDefaults));
22839         }else{
22840             this[this.collapseEl].show();
22841             this.afterExpand(false);
22842         }
22843     },
22844
22845     // private
22846     afterExpand : function(anim){
22847         this.collapsed = false;
22848         this.afterEffect(anim);
22849         if(Ext.isDefined(this.deferLayout)){
22850             this.doLayout(true);
22851         }
22852         this.fireEvent('expand', this);
22853     },
22854
22855     /**
22856      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
22857      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
22858      * {@link #animCollapse} panel config)
22859      * @return {Ext.Panel} this
22860      */
22861     toggleCollapse : function(animate){
22862         this[this.collapsed ? 'expand' : 'collapse'](animate);
22863         return this;
22864     },
22865
22866     // private
22867     onDisable : function(){
22868         if(this.rendered && this.maskDisabled){
22869             this.el.mask();
22870         }
22871         Ext.Panel.superclass.onDisable.call(this);
22872     },
22873
22874     // private
22875     onEnable : function(){
22876         if(this.rendered && this.maskDisabled){
22877             this.el.unmask();
22878         }
22879         Ext.Panel.superclass.onEnable.call(this);
22880     },
22881
22882     // private
22883     onResize : function(w, h){
22884         if(Ext.isDefined(w) || Ext.isDefined(h)){
22885             if(!this.collapsed){
22886                 if(Ext.isNumber(w)){
22887                     w = this.adjustBodyWidth(w - this.getFrameWidth());
22888                     if(this.tbar){
22889                         this.tbar.setWidth(w);
22890                         if(this.topToolbar){
22891                             this.topToolbar.setSize(w);
22892                         }
22893                     }
22894                     if(this.bbar){
22895                         this.bbar.setWidth(w);
22896                         if(this.bottomToolbar){
22897                             this.bottomToolbar.setSize(w);
22898                         }
22899                     }
22900                     if(this.fbar){
22901                         var f = this.fbar,
22902                             fWidth = 1,
22903                             strict = Ext.isStrict;
22904                         if(this.buttonAlign == 'left'){
22905                            fWidth = w - f.container.getFrameWidth('lr');
22906                         }else{
22907                             //center/right alignment off in webkit
22908                             if(Ext.isIE || Ext.isWebKit){
22909                                 //center alignment ok on webkit.
22910                                 //right broken in both, center on IE
22911                                 if(!(this.buttonAlign == 'center' && Ext.isWebKit) && (!strict || (!Ext.isIE8 && strict))){
22912                                     (function(){
22913                                         f.setWidth(f.getEl().child('.x-toolbar-ct').getWidth());
22914                                     }).defer(1);
22915                                 }else{
22916                                     fWidth = 'auto';
22917                                 }
22918                             }else{
22919                                 fWidth = 'auto';
22920                             }
22921                         }
22922                         f.setWidth(fWidth);
22923                     }
22924                     this.body.setWidth(w);
22925                 }else if(w == 'auto'){
22926                     this.body.setWidth(w);
22927                 }
22928
22929                 if(Ext.isNumber(h)){
22930                     h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight()));
22931                     this.body.setHeight(h);
22932                 }else if(h == 'auto'){
22933                     this.body.setHeight(h);
22934                 }
22935
22936                 if(this.disabled && this.el._mask){
22937                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
22938                 }
22939             }else{
22940                 this.queuedBodySize = {width: w, height: h};
22941                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
22942                     this.queuedExpand = true;
22943                     this.on('expand', function(){
22944                         delete this.queuedExpand;
22945                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
22946                         this.doLayout();
22947                     }, this, {single:true});
22948                 }
22949             }
22950             this.onBodyResize(w, h);
22951         }
22952         this.syncShadow();
22953     },
22954     
22955     // private
22956     onBodyResize: function(w, h){
22957         this.fireEvent('bodyresize', this, w, h);
22958     },
22959     
22960     // private
22961     getToolbarHeight: function(){
22962         var h = 0;
22963         if(this.rendered){
22964             Ext.each(this.toolbars, function(tb){
22965                 h += tb.getHeight();
22966             }, this);
22967         }
22968         return h;
22969     },
22970
22971     // private
22972     adjustBodyHeight : function(h){
22973         return h;
22974     },
22975
22976     // private
22977     adjustBodyWidth : function(w){
22978         return w;
22979     },
22980
22981     // private
22982     onPosition : function(){
22983         this.syncShadow();
22984     },
22985
22986     /**
22987      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
22988      * retrieve the body width see {@link #getInnerWidth}.
22989      * @return {Number} The frame width
22990      */
22991     getFrameWidth : function(){
22992         var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
22993
22994         if(this.frame){
22995             var l = this.bwrap.dom.firstChild;
22996             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
22997             w += this.mc.getFrameWidth('lr');
22998         }
22999         return w;
23000     },
23001
23002     /**
23003      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
23004      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
23005      * @return {Number} The frame height
23006      */
23007     getFrameHeight : function(){
23008         var h  = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
23009         h += (this.tbar ? this.tbar.getHeight() : 0) +
23010              (this.bbar ? this.bbar.getHeight() : 0);
23011
23012         if(this.frame){
23013             h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
23014         }else{
23015             h += (this.header ? this.header.getHeight() : 0) +
23016                 (this.footer ? this.footer.getHeight() : 0);
23017         }
23018         return h;
23019     },
23020
23021     /**
23022      * Returns the width in pixels of the body element (not including the width of any framing elements).
23023      * For the frame width see {@link #getFrameWidth}.
23024      * @return {Number} The body width
23025      */
23026     getInnerWidth : function(){
23027         return this.getSize().width - this.getFrameWidth();
23028     },
23029
23030     /**
23031      * Returns the height in pixels of the body element (not including the height of any framing elements).
23032      * For the frame height see {@link #getFrameHeight}.
23033      * @return {Number} The body height
23034      */
23035     getInnerHeight : function(){
23036         return this.getSize().height - this.getFrameHeight();
23037     },
23038
23039     // private
23040     syncShadow : function(){
23041         if(this.floating){
23042             this.el.sync(true);
23043         }
23044     },
23045
23046     // private
23047     getLayoutTarget : function(){
23048         return this.body;
23049     },
23050
23051     /**
23052      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
23053      * <p>In order to be able to set the title, a header element must have been created
23054      * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
23055      * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
23056      * @param {String} title The title text to set
23057      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
23058      */
23059     setTitle : function(title, iconCls){
23060         this.title = title;
23061         if(this.header && this.headerAsText){
23062             this.header.child('span').update(title);
23063         }
23064         if(iconCls){
23065             this.setIconClass(iconCls);
23066         }
23067         this.fireEvent('titlechange', this, title);
23068         return this;
23069     },
23070
23071     /**
23072      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
23073      * @return {Ext.Updater} The Updater
23074      */
23075     getUpdater : function(){
23076         return this.body.getUpdater();
23077     },
23078
23079      /**
23080      * Loads this content panel immediately with content returned from an XHR call.
23081      * @param {Object/String/Function} config A config object containing any of the following options:
23082 <pre><code>
23083 panel.load({
23084     url: 'your-url.php',
23085     params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
23086     callback: yourFunction,
23087     scope: yourObject, // optional scope for the callback
23088     discardUrl: false,
23089     nocache: false,
23090     text: 'Loading...',
23091     timeout: 30,
23092     scripts: false
23093 });
23094 </code></pre>
23095      * The only required property is url. The optional properties nocache, text and scripts
23096      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
23097      * associated property on this panel Updater instance.
23098      * @return {Ext.Panel} this
23099      */
23100     load : function(){
23101         var um = this.body.getUpdater();
23102         um.update.apply(um, arguments);
23103         return this;
23104     },
23105
23106     // private
23107     beforeDestroy : function(){
23108         if(this.header){
23109             this.header.removeAllListeners();
23110             if(this.headerAsText){
23111                 Ext.Element.uncache(this.header.child('span'));
23112             }
23113         }
23114         Ext.Element.uncache(
23115             this.ft,
23116             this.mc,
23117             this.header,
23118             this.tbar,
23119             this.bbar,
23120             this.footer,
23121             this.body,
23122             this.bwrap
23123         );
23124         if(this.tools){
23125             for(var k in this.tools){
23126                 Ext.destroy(this.tools[k]);
23127             }
23128         }
23129         if(this.buttons){
23130             for(var b in this.buttons){
23131                 Ext.destroy(this.buttons[b]);
23132             }
23133         }
23134         if(this.rendered){
23135             Ext.destroy(this.toolbars);
23136         }else{
23137             Ext.destroy(this.topToolbar, this.bottomToolbar);
23138         }
23139         Ext.Panel.superclass.beforeDestroy.call(this);
23140     },
23141
23142     // private
23143     createClasses : function(){
23144         this.headerCls = this.baseCls + '-header';
23145         this.headerTextCls = this.baseCls + '-header-text';
23146         this.bwrapCls = this.baseCls + '-bwrap';
23147         this.tbarCls = this.baseCls + '-tbar';
23148         this.bodyCls = this.baseCls + '-body';
23149         this.bbarCls = this.baseCls + '-bbar';
23150         this.footerCls = this.baseCls + '-footer';
23151     },
23152
23153     // private
23154     createGhost : function(cls, useShim, appendTo){
23155         var el = document.createElement('div');
23156         el.className = 'x-panel-ghost ' + (cls ? cls : '');
23157         if(this.header){
23158             el.appendChild(this.el.dom.firstChild.cloneNode(true));
23159         }
23160         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
23161         el.style.width = this.el.dom.offsetWidth + 'px';;
23162         if(!appendTo){
23163             this.container.dom.appendChild(el);
23164         }else{
23165             Ext.getDom(appendTo).appendChild(el);
23166         }
23167         if(useShim !== false && this.el.useShim !== false){
23168             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
23169             layer.show();
23170             return layer;
23171         }else{
23172             return new Ext.Element(el);
23173         }
23174     },
23175
23176     // private
23177     doAutoLoad : function(){
23178         var u = this.body.getUpdater();
23179         if(this.renderer){
23180             u.setRenderer(this.renderer);
23181         }
23182         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
23183     },
23184
23185     /**
23186      * Retrieve a tool by id.
23187      * @param {String} id
23188      * @return {Object} tool
23189      */
23190     getTool : function(id) {
23191         return this.tools[id];
23192     }
23193
23194 /**
23195  * @cfg {String} autoEl @hide
23196  */
23197 });
23198 Ext.reg('panel', Ext.Panel);
23199 /**
23200  * @class Ext.Editor
23201  * @extends Ext.Component
23202  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
23203  * @constructor
23204  * Create a new Editor
23205  * @param {Object} config The config object
23206  * @xtype editor
23207  */
23208 Ext.Editor = function(field, config){
23209     if(field.field){
23210         this.field = Ext.create(field.field, 'textfield');
23211         config = Ext.apply({}, field); // copy so we don't disturb original config
23212         delete config.field;
23213     }else{
23214         this.field = field;
23215     }
23216     Ext.Editor.superclass.constructor.call(this, config);
23217 };
23218
23219 Ext.extend(Ext.Editor, Ext.Component, {
23220     /**
23221     * @cfg {Ext.form.Field} field
23222     * The Field object (or descendant) or config object for field
23223     */
23224     /**
23225      * @cfg {Boolean} allowBlur
23226      * True to {@link #completeEdit complete the editing process} if in edit mode when the
23227      * field is blurred. Defaults to <tt>false</tt>.
23228      */
23229     /**
23230      * @cfg {Boolean/String} autoSize
23231      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
23232      * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
23233      */
23234     /**
23235      * @cfg {Boolean} revertInvalid
23236      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
23237      * validation fails (defaults to true)
23238      */
23239     /**
23240      * @cfg {Boolean} ignoreNoChange
23241      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
23242      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
23243      * will never be ignored.
23244      */
23245     /**
23246      * @cfg {Boolean} hideEl
23247      * False to keep the bound element visible while the editor is displayed (defaults to true)
23248      */
23249     /**
23250      * @cfg {Mixed} value
23251      * The data value of the underlying field (defaults to "")
23252      */
23253     value : "",
23254     /**
23255      * @cfg {String} alignment
23256      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
23257      */
23258     alignment: "c-c?",
23259     /**
23260      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
23261      * for bottom-right shadow (defaults to "frame")
23262      */
23263     shadow : "frame",
23264     /**
23265      * @cfg {Boolean} constrain True to constrain the editor to the viewport
23266      */
23267     constrain : false,
23268     /**
23269      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
23270      */
23271     swallowKeys : true,
23272     /**
23273      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
23274      */
23275     completeOnEnter : true,
23276     /**
23277      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
23278      */
23279     cancelOnEsc : true,
23280     /**
23281      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
23282      */
23283     updateEl : false,
23284
23285     initComponent : function(){
23286         Ext.Editor.superclass.initComponent.call(this);
23287         this.addEvents(
23288             /**
23289              * @event beforestartedit
23290              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
23291              * false from the handler of this event.
23292              * @param {Editor} this
23293              * @param {Ext.Element} boundEl The underlying element bound to this editor
23294              * @param {Mixed} value The field value being set
23295              */
23296             "beforestartedit",
23297             /**
23298              * @event startedit
23299              * Fires when this editor is displayed
23300              * @param {Ext.Element} boundEl The underlying element bound to this editor
23301              * @param {Mixed} value The starting field value
23302              */
23303             "startedit",
23304             /**
23305              * @event beforecomplete
23306              * Fires after a change has been made to the field, but before the change is reflected in the underlying
23307              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
23308              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
23309              * event will not fire since no edit actually occurred.
23310              * @param {Editor} this
23311              * @param {Mixed} value The current field value
23312              * @param {Mixed} startValue The original field value
23313              */
23314             "beforecomplete",
23315             /**
23316              * @event complete
23317              * Fires after editing is complete and any changed value has been written to the underlying field.
23318              * @param {Editor} this
23319              * @param {Mixed} value The current field value
23320              * @param {Mixed} startValue The original field value
23321              */
23322             "complete",
23323             /**
23324              * @event canceledit
23325              * Fires after editing has been canceled and the editor's value has been reset.
23326              * @param {Editor} this
23327              * @param {Mixed} value The user-entered field value that was discarded
23328              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
23329              */
23330             "canceledit",
23331             /**
23332              * @event specialkey
23333              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
23334              * {@link Ext.EventObject#getKey} to determine which key was pressed.
23335              * @param {Ext.form.Field} this
23336              * @param {Ext.EventObject} e The event object
23337              */
23338             "specialkey"
23339         );
23340     },
23341
23342     // private
23343     onRender : function(ct, position){
23344         this.el = new Ext.Layer({
23345             shadow: this.shadow,
23346             cls: "x-editor",
23347             parentEl : ct,
23348             shim : this.shim,
23349             shadowOffset: this.shadowOffset || 4,
23350             id: this.id,
23351             constrain: this.constrain
23352         });
23353         if(this.zIndex){
23354             this.el.setZIndex(this.zIndex);
23355         }
23356         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
23357         if(this.field.msgTarget != 'title'){
23358             this.field.msgTarget = 'qtip';
23359         }
23360         this.field.inEditor = true;
23361         this.mon(this.field, {
23362             scope: this,
23363             blur: this.onBlur,
23364             specialkey: this.onSpecialKey
23365         });
23366         if(this.field.grow){
23367             this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
23368         }
23369         this.field.render(this.el).show();
23370         this.field.getEl().dom.name = '';
23371         if(this.swallowKeys){
23372             this.field.el.swallowEvent([
23373                 'keypress', // *** Opera
23374                 'keydown'   // *** all other browsers
23375             ]);
23376         }
23377     },
23378
23379     // private
23380     onSpecialKey : function(field, e){
23381         var key = e.getKey(),
23382             complete = this.completeOnEnter && key == e.ENTER,
23383             cancel = this.cancelOnEsc && key == e.ESC;
23384         if(complete || cancel){
23385             e.stopEvent();
23386             if(complete){
23387                 this.completeEdit();
23388             }else{
23389                 this.cancelEdit();
23390             }
23391             if(field.triggerBlur){
23392                 field.triggerBlur(); 
23393             }
23394         }
23395         this.fireEvent('specialkey', field, e);
23396     },
23397
23398     /**
23399      * Starts the editing process and shows the editor.
23400      * @param {Mixed} el The element to edit
23401      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
23402       * to the innerHTML of el.
23403      */
23404     startEdit : function(el, value){
23405         if(this.editing){
23406             this.completeEdit();
23407         }
23408         this.boundEl = Ext.get(el);
23409         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
23410         if(!this.rendered){
23411             this.render(this.parentEl || document.body);
23412         }
23413         if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
23414             this.startValue = v;
23415             this.field.setValue(v);
23416             this.doAutoSize();
23417             this.el.alignTo(this.boundEl, this.alignment);
23418             this.editing = true;
23419             this.show();
23420         }
23421     },
23422
23423     // private
23424     doAutoSize : function(){
23425         if(this.autoSize){
23426             var sz = this.boundEl.getSize(),
23427                 fs = this.field.getSize();
23428
23429             switch(this.autoSize){
23430                 case "width":
23431                     this.setSize(sz.width, fs.height);
23432                     break;
23433                 case "height":
23434                     this.setSize(fs.width, sz.height);
23435                     break;
23436                 case "none":
23437                     this.setSize(fs.width, fs.height);
23438                     break;
23439                 default:
23440                     this.setSize(sz.width, sz.height);
23441             }
23442         }
23443     },
23444
23445     /**
23446      * Sets the height and width of this editor.
23447      * @param {Number} width The new width
23448      * @param {Number} height The new height
23449      */
23450     setSize : function(w, h){
23451         delete this.field.lastSize;
23452         this.field.setSize(w, h);
23453         if(this.el){
23454             if(Ext.isGecko2 || Ext.isOpera){
23455                 // prevent layer scrollbars
23456                 this.el.setSize(w, h);
23457             }
23458             this.el.sync();
23459         }
23460     },
23461
23462     /**
23463      * Realigns the editor to the bound field based on the current alignment config value.
23464      */
23465     realign : function(){
23466         this.el.alignTo(this.boundEl, this.alignment);
23467     },
23468
23469     /**
23470      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
23471      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
23472      */
23473     completeEdit : function(remainVisible){
23474         if(!this.editing){
23475             return;
23476         }
23477         var v = this.getValue();
23478         if(!this.field.isValid()){
23479             if(this.revertInvalid !== false){
23480                 this.cancelEdit(remainVisible);
23481             }
23482             return;
23483         }
23484         if(String(v) === String(this.startValue) && this.ignoreNoChange){
23485             this.hideEdit(remainVisible);
23486             return;
23487         }
23488         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
23489             v = this.getValue();
23490             if(this.updateEl && this.boundEl){
23491                 this.boundEl.update(v);
23492             }
23493             this.hideEdit(remainVisible);
23494             this.fireEvent("complete", this, v, this.startValue);
23495         }
23496     },
23497
23498     // private
23499     onShow : function(){
23500         this.el.show();
23501         if(this.hideEl !== false){
23502             this.boundEl.hide();
23503         }
23504         this.field.show().focus(false, true);
23505         this.fireEvent("startedit", this.boundEl, this.startValue);
23506     },
23507
23508     /**
23509      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
23510      * reverted to the original starting value.
23511      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
23512      * cancel (defaults to false)
23513      */
23514     cancelEdit : function(remainVisible){
23515         if(this.editing){
23516             var v = this.getValue();
23517             this.setValue(this.startValue);
23518             this.hideEdit(remainVisible);
23519             this.fireEvent("canceledit", this, v, this.startValue);
23520         }
23521     },
23522     
23523     // private
23524     hideEdit: function(remainVisible){
23525         if(remainVisible !== true){
23526             this.editing = false;
23527             this.hide();
23528         }
23529     },
23530
23531     // private
23532     onBlur : function(){
23533         if(this.allowBlur !== true && this.editing){
23534             this.completeEdit();
23535         }
23536     },
23537
23538     // private
23539     onHide : function(){
23540         if(this.editing){
23541             this.completeEdit();
23542             return;
23543         }
23544         this.field.blur();
23545         if(this.field.collapse){
23546             this.field.collapse();
23547         }
23548         this.el.hide();
23549         if(this.hideEl !== false){
23550             this.boundEl.show();
23551         }
23552     },
23553
23554     /**
23555      * Sets the data value of the editor
23556      * @param {Mixed} value Any valid value supported by the underlying field
23557      */
23558     setValue : function(v){
23559         this.field.setValue(v);
23560     },
23561
23562     /**
23563      * Gets the data value of the editor
23564      * @return {Mixed} The data value
23565      */
23566     getValue : function(){
23567         return this.field.getValue();
23568     },
23569
23570     beforeDestroy : function(){
23571         Ext.destroy(this.field);
23572         this.field = null;
23573     }
23574 });
23575 Ext.reg('editor', Ext.Editor);/**
23576  * @class Ext.ColorPalette
23577  * @extends Ext.Component
23578  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
23579  * Here's an example of typical usage:
23580  * <pre><code>
23581 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
23582 cp.render('my-div');
23583
23584 cp.on('select', function(palette, selColor){
23585     // do something with selColor
23586 });
23587 </code></pre>
23588  * @constructor
23589  * Create a new ColorPalette
23590  * @param {Object} config The config object
23591  * @xtype colorpalette
23592  */
23593 Ext.ColorPalette = function(config){
23594     Ext.ColorPalette.superclass.constructor.call(this, config);
23595     this.addEvents(
23596         /**
23597              * @event select
23598              * Fires when a color is selected
23599              * @param {ColorPalette} this
23600              * @param {String} color The 6-digit color hex code (without the # symbol)
23601              */
23602         'select'
23603     );
23604
23605     if(this.handler){
23606         this.on('select', this.handler, this.scope, true);
23607     }
23608 };
23609 Ext.extend(Ext.ColorPalette, Ext.Component, {
23610         /**
23611          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
23612          */
23613     /**
23614      * @cfg {String} itemCls
23615      * The CSS class to apply to the containing element (defaults to 'x-color-palette')
23616      */
23617     itemCls : 'x-color-palette',
23618     /**
23619      * @cfg {String} value
23620      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
23621      * the hex codes are case-sensitive.
23622      */
23623     value : null,
23624     clickEvent :'click',
23625     // private
23626     ctype : 'Ext.ColorPalette',
23627
23628     /**
23629      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
23630      */
23631     allowReselect : false,
23632
23633     /**
23634      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
23635      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
23636      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
23637      * of colors with the width setting until the box is symmetrical.</p>
23638      * <p>You can override individual colors if needed:</p>
23639      * <pre><code>
23640 var cp = new Ext.ColorPalette();
23641 cp.colors[0] = 'FF0000';  // change the first box to red
23642 </code></pre>
23643
23644 Or you can provide a custom array of your own for complete control:
23645 <pre><code>
23646 var cp = new Ext.ColorPalette();
23647 cp.colors = ['000000', '993300', '333300'];
23648 </code></pre>
23649      * @type Array
23650      */
23651     colors : [
23652         '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
23653         '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
23654         'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
23655         'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
23656         'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
23657     ],
23658
23659     /**
23660      * @cfg {Function} handler
23661      * Optional. A function that will handle the select event of this palette.
23662      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
23663      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
23664      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
23665      * </ul></div>
23666      */
23667     /**
23668      * @cfg {Object} scope
23669      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
23670      * function will be called.  Defaults to this ColorPalette instance.
23671      */
23672
23673     // private
23674     onRender : function(container, position){
23675         var t = this.tpl || new Ext.XTemplate(
23676             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
23677         );
23678         var el = document.createElement('div');
23679         el.id = this.getId();
23680         el.className = this.itemCls;
23681         t.overwrite(el, this.colors);
23682         container.dom.insertBefore(el, position);
23683         this.el = Ext.get(el);
23684         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
23685         if(this.clickEvent != 'click'){
23686                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
23687         }
23688     },
23689
23690     // private
23691     afterRender : function(){
23692         Ext.ColorPalette.superclass.afterRender.call(this);
23693         if(this.value){
23694             var s = this.value;
23695             this.value = null;
23696             this.select(s);
23697         }
23698     },
23699
23700     // private
23701     handleClick : function(e, t){
23702         e.preventDefault();
23703         if(!this.disabled){
23704             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
23705             this.select(c.toUpperCase());
23706         }
23707     },
23708
23709     /**
23710      * Selects the specified color in the palette (fires the {@link #select} event)
23711      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
23712      */
23713     select : function(color){
23714         color = color.replace('#', '');
23715         if(color != this.value || this.allowReselect){
23716             var el = this.el;
23717             if(this.value){
23718                 el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
23719             }
23720             el.child('a.color-'+color).addClass('x-color-palette-sel');
23721             this.value = color;
23722             this.fireEvent('select', this, color);
23723         }
23724     }
23725
23726     /**
23727      * @cfg {String} autoEl @hide
23728      */
23729 });
23730 Ext.reg('colorpalette', Ext.ColorPalette);
23731 /**\r
23732  * @class Ext.DatePicker\r
23733  * @extends Ext.Component\r
23734  * Simple date picker class.\r
23735  * @constructor\r
23736  * Create a new DatePicker\r
23737  * @param {Object} config The config object\r
23738  * @xtype datepicker\r
23739  */\r
23740 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {\r
23741     /**\r
23742      * @cfg {String} todayText\r
23743      * The text to display on the button that selects the current date (defaults to <tt>'Today'</tt>)\r
23744      */\r
23745     todayText : 'Today',\r
23746     /**\r
23747      * @cfg {String} okText\r
23748      * The text to display on the ok button (defaults to <tt>'&#160;OK&#160;'</tt> to give the user extra clicking room)\r
23749      */\r
23750     okText : '&#160;OK&#160;',\r
23751     /**\r
23752      * @cfg {String} cancelText\r
23753      * The text to display on the cancel button (defaults to <tt>'Cancel'</tt>)\r
23754      */\r
23755     cancelText : 'Cancel',\r
23756     /**\r
23757      * @cfg {Function} handler\r
23758      * Optional. A function that will handle the select event of this picker.\r
23759      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
23760      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>\r
23761      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>\r
23762      * </ul></div>\r
23763      */\r
23764     /**\r
23765      * @cfg {Object} scope\r
23766      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
23767      * function will be called.  Defaults to this DatePicker instance.\r
23768      */ \r
23769     /**\r
23770      * @cfg {String} todayTip\r
23771      * The tooltip to display for the button that selects the current date (defaults to <tt>'{current date} (Spacebar)'</tt>)\r
23772      */\r
23773     todayTip : '{0} (Spacebar)',\r
23774     /**\r
23775      * @cfg {String} minText\r
23776      * The error text to display if the minDate validation fails (defaults to <tt>'This date is before the minimum date'</tt>)\r
23777      */\r
23778     minText : 'This date is before the minimum date',\r
23779     /**\r
23780      * @cfg {String} maxText\r
23781      * The error text to display if the maxDate validation fails (defaults to <tt>'This date is after the maximum date'</tt>)\r
23782      */\r
23783     maxText : 'This date is after the maximum date',\r
23784     /**\r
23785      * @cfg {String} format\r
23786      * The default date format string which can be overriden for localization support.  The format must be\r
23787      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/y'</tt>).\r
23788      */\r
23789     format : 'm/d/y',\r
23790     /**\r
23791      * @cfg {String} disabledDaysText\r
23792      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)\r
23793      */\r
23794     disabledDaysText : 'Disabled',\r
23795     /**\r
23796      * @cfg {String} disabledDatesText\r
23797      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)\r
23798      */\r
23799     disabledDatesText : 'Disabled',\r
23800     /**\r
23801      * @cfg {Array} monthNames\r
23802      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)\r
23803      */\r
23804     monthNames : Date.monthNames,\r
23805     /**\r
23806      * @cfg {Array} dayNames\r
23807      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)\r
23808      */\r
23809     dayNames : Date.dayNames,\r
23810     /**\r
23811      * @cfg {String} nextText\r
23812      * The next month navigation button tooltip (defaults to <tt>'Next Month (Control+Right)'</tt>)\r
23813      */\r
23814     nextText : 'Next Month (Control+Right)',\r
23815     /**\r
23816      * @cfg {String} prevText\r
23817      * The previous month navigation button tooltip (defaults to <tt>'Previous Month (Control+Left)'</tt>)\r
23818      */\r
23819     prevText : 'Previous Month (Control+Left)',\r
23820     /**\r
23821      * @cfg {String} monthYearText\r
23822      * The header month selector tooltip (defaults to <tt>'Choose a month (Control+Up/Down to move years)'</tt>)\r
23823      */\r
23824     monthYearText : 'Choose a month (Control+Up/Down to move years)',\r
23825     /**\r
23826      * @cfg {Number} startDay\r
23827      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)\r
23828      */\r
23829     startDay : 0,\r
23830     /**\r
23831      * @cfg {Boolean} showToday\r
23832      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar\r
23833      * that selects the current date (defaults to <tt>true</tt>).\r
23834      */\r
23835     showToday : true,\r
23836     /**\r
23837      * @cfg {Date} minDate\r
23838      * Minimum allowable date (JavaScript date object, defaults to null)\r
23839      */\r
23840     /**\r
23841      * @cfg {Date} maxDate\r
23842      * Maximum allowable date (JavaScript date object, defaults to null)\r
23843      */\r
23844     /**\r
23845      * @cfg {Array} disabledDays\r
23846      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).\r
23847      */\r
23848     /**\r
23849      * @cfg {RegExp} disabledDatesRE\r
23850      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}\r
23851      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the\r
23852      * disabledDates value.\r
23853      */\r
23854     /**\r
23855      * @cfg {Array} disabledDates\r
23856      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular\r
23857      * expression so they are very powerful. Some examples:\r
23858      * <ul>\r
23859      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>\r
23860      * <li>['03/08', '09/16'] would disable those days for every year</li>\r
23861      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>\r
23862      * <li>['03/../2006'] would disable every day in March 2006</li>\r
23863      * <li>['^03'] would disable every day in every March</li>\r
23864      * </ul>\r
23865      * Note that the format of the dates included in the array should exactly match the {@link #format} config.\r
23866      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to\r
23867      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].\r
23868      */\r
23869 \r
23870     // private\r
23871     initComponent : function(){\r
23872         Ext.DatePicker.superclass.initComponent.call(this);\r
23873 \r
23874         this.value = this.value ?\r
23875                  this.value.clearTime(true) : new Date().clearTime();\r
23876 \r
23877         this.addEvents(\r
23878             /**\r
23879              * @event select\r
23880              * Fires when a date is selected\r
23881              * @param {DatePicker} this\r
23882              * @param {Date} date The selected date\r
23883              */\r
23884             'select'\r
23885         );\r
23886 \r
23887         if(this.handler){\r
23888             this.on('select', this.handler,  this.scope || this);\r
23889         }\r
23890 \r
23891         this.initDisabledDays();\r
23892     },\r
23893 \r
23894     // private\r
23895     initDisabledDays : function(){\r
23896         if(!this.disabledDatesRE && this.disabledDates){\r
23897             var dd = this.disabledDates,\r
23898                 len = dd.length - 1,\r
23899                 re = '(?:';\r
23900                 \r
23901             Ext.each(dd, function(d, i){\r
23902                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];\r
23903                 if(i != len){\r
23904                     re += '|';\r
23905                 }\r
23906             }, this);\r
23907             this.disabledDatesRE = new RegExp(re + ')');\r
23908         }\r
23909     },\r
23910 \r
23911     /**\r
23912      * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
23913      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config\r
23914      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.\r
23915      */\r
23916     setDisabledDates : function(dd){\r
23917         if(Ext.isArray(dd)){\r
23918             this.disabledDates = dd;\r
23919             this.disabledDatesRE = null;\r
23920         }else{\r
23921             this.disabledDatesRE = dd;\r
23922         }\r
23923         this.initDisabledDays();\r
23924         this.update(this.value, true);\r
23925     },\r
23926 \r
23927     /**\r
23928      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
23929      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config\r
23930      * for details on supported values.\r
23931      */\r
23932     setDisabledDays : function(dd){\r
23933         this.disabledDays = dd;\r
23934         this.update(this.value, true);\r
23935     },\r
23936 \r
23937     /**\r
23938      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.\r
23939      * @param {Date} value The minimum date that can be selected\r
23940      */\r
23941     setMinDate : function(dt){\r
23942         this.minDate = dt;\r
23943         this.update(this.value, true);\r
23944     },\r
23945 \r
23946     /**\r
23947      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.\r
23948      * @param {Date} value The maximum date that can be selected\r
23949      */\r
23950     setMaxDate : function(dt){\r
23951         this.maxDate = dt;\r
23952         this.update(this.value, true);\r
23953     },\r
23954 \r
23955     /**\r
23956      * Sets the value of the date field\r
23957      * @param {Date} value The date to set\r
23958      */\r
23959     setValue : function(value){\r
23960         this.value = value.clearTime(true);\r
23961         this.update(this.value);\r
23962     },\r
23963 \r
23964     /**\r
23965      * Gets the current selected value of the date field\r
23966      * @return {Date} The selected date\r
23967      */\r
23968     getValue : function(){\r
23969         return this.value;\r
23970     },\r
23971 \r
23972     // private\r
23973     focus : function(){\r
23974         this.update(this.activeDate);\r
23975     },\r
23976     \r
23977     // private\r
23978     onEnable: function(initial){\r
23979         Ext.DatePicker.superclass.onEnable.call(this);    \r
23980         this.doDisabled(false);\r
23981         this.update(initial ? this.value : this.activeDate);\r
23982         if(Ext.isIE){\r
23983             this.el.repaint();\r
23984         }\r
23985         \r
23986     },\r
23987     \r
23988     // private\r
23989     onDisable : function(){\r
23990         Ext.DatePicker.superclass.onDisable.call(this);   \r
23991         this.doDisabled(true);\r
23992         if(Ext.isIE && !Ext.isIE8){\r
23993             /* Really strange problem in IE6/7, when disabled, have to explicitly\r
23994              * repaint each of the nodes to get them to display correctly, simply\r
23995              * calling repaint on the main element doesn't appear to be enough.\r
23996              */\r
23997              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){\r
23998                  Ext.fly(el).repaint();\r
23999              });\r
24000         }\r
24001     },\r
24002     \r
24003     // private\r
24004     doDisabled : function(disabled){\r
24005         this.keyNav.setDisabled(disabled);\r
24006         this.prevRepeater.setDisabled(disabled);\r
24007         this.nextRepeater.setDisabled(disabled);\r
24008         if(this.showToday){\r
24009             this.todayKeyListener.setDisabled(disabled);\r
24010             this.todayBtn.setDisabled(disabled);\r
24011         }\r
24012     },\r
24013 \r
24014     // private\r
24015     onRender : function(container, position){\r
24016         var m = [\r
24017              '<table cellspacing="0">',\r
24018                 '<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
24019                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],\r
24020                 dn = this.dayNames,\r
24021                 i;\r
24022         for(i = 0; i < 7; i++){\r
24023             var d = this.startDay+i;\r
24024             if(d > 6){\r
24025                 d = d-7;\r
24026             }\r
24027             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');\r
24028         }\r
24029         m[m.length] = '</tr></thead><tbody><tr>';\r
24030         for(i = 0; i < 42; i++) {\r
24031             if(i % 7 === 0 && i !== 0){\r
24032                 m[m.length] = '</tr><tr>';\r
24033             }\r
24034             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';\r
24035         }\r
24036         m.push('</tr></tbody></table></td></tr>',\r
24037                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',\r
24038                 '</table><div class="x-date-mp"></div>');\r
24039 \r
24040         var el = document.createElement('div');\r
24041         el.className = 'x-date-picker';\r
24042         el.innerHTML = m.join('');\r
24043 \r
24044         container.dom.insertBefore(el, position);\r
24045 \r
24046         this.el = Ext.get(el);\r
24047         this.eventEl = Ext.get(el.firstChild);\r
24048 \r
24049         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {\r
24050             handler: this.showPrevMonth,\r
24051             scope: this,\r
24052             preventDefault:true,\r
24053             stopDefault:true\r
24054         });\r
24055 \r
24056         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {\r
24057             handler: this.showNextMonth,\r
24058             scope: this,\r
24059             preventDefault:true,\r
24060             stopDefault:true\r
24061         });\r
24062 \r
24063         this.monthPicker = this.el.down('div.x-date-mp');\r
24064         this.monthPicker.enableDisplayMode('block');\r
24065 \r
24066         this.keyNav = new Ext.KeyNav(this.eventEl, {\r
24067             'left' : function(e){\r
24068                 if(e.ctrlKey){\r
24069                     this.showPrevMonth();\r
24070                 }else{\r
24071                     this.update(this.activeDate.add('d', -1));    \r
24072                 }\r
24073             },\r
24074 \r
24075             'right' : function(e){\r
24076                 if(e.ctrlKey){\r
24077                     this.showNextMonth();\r
24078                 }else{\r
24079                     this.update(this.activeDate.add('d', 1));    \r
24080                 }\r
24081             },\r
24082 \r
24083             'up' : function(e){\r
24084                 if(e.ctrlKey){\r
24085                     this.showNextYear();\r
24086                 }else{\r
24087                     this.update(this.activeDate.add('d', -7));\r
24088                 }\r
24089             },\r
24090 \r
24091             'down' : function(e){\r
24092                 if(e.ctrlKey){\r
24093                     this.showPrevYear();\r
24094                 }else{\r
24095                     this.update(this.activeDate.add('d', 7));\r
24096                 }\r
24097             },\r
24098 \r
24099             'pageUp' : function(e){\r
24100                 this.showNextMonth();\r
24101             },\r
24102 \r
24103             'pageDown' : function(e){\r
24104                 this.showPrevMonth();\r
24105             },\r
24106 \r
24107             'enter' : function(e){\r
24108                 e.stopPropagation();\r
24109                 return true;\r
24110             },\r
24111 \r
24112             scope : this\r
24113         });\r
24114 \r
24115         this.el.unselectable();\r
24116 \r
24117         this.cells = this.el.select('table.x-date-inner tbody td');\r
24118         this.textNodes = this.el.query('table.x-date-inner tbody span');\r
24119 \r
24120         this.mbtn = new Ext.Button({\r
24121             text: '&#160;',\r
24122             tooltip: this.monthYearText,\r
24123             renderTo: this.el.child('td.x-date-middle', true)\r
24124         });\r
24125         this.mbtn.el.child('em').addClass('x-btn-arrow');\r
24126 \r
24127         if(this.showToday){\r
24128             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);\r
24129             var today = (new Date()).dateFormat(this.format);\r
24130             this.todayBtn = new Ext.Button({\r
24131                 renderTo: this.el.child('td.x-date-bottom', true),\r
24132                 text: String.format(this.todayText, today),\r
24133                 tooltip: String.format(this.todayTip, today),\r
24134                 handler: this.selectToday,\r
24135                 scope: this\r
24136             });\r
24137         }\r
24138         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);\r
24139         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});\r
24140         this.mon(this.mbtn, 'click', this.showMonthPicker, this);\r
24141         this.onEnable(true);\r
24142     },\r
24143 \r
24144     // private\r
24145     createMonthPicker : function(){\r
24146         if(!this.monthPicker.dom.firstChild){\r
24147             var buf = ['<table border="0" cellspacing="0">'];\r
24148             for(var i = 0; i < 6; i++){\r
24149                 buf.push(\r
24150                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',\r
24151                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',\r
24152                     i === 0 ?\r
24153                     '<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
24154                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'\r
24155                 );\r
24156             }\r
24157             buf.push(\r
24158                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',\r
24159                     this.okText,\r
24160                     '</button><button type="button" class="x-date-mp-cancel">',\r
24161                     this.cancelText,\r
24162                     '</button></td></tr>',\r
24163                 '</table>'\r
24164             );\r
24165             this.monthPicker.update(buf.join(''));\r
24166 \r
24167             this.mon(this.monthPicker, 'click', this.onMonthClick, this);\r
24168             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);\r
24169 \r
24170             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');\r
24171             this.mpYears = this.monthPicker.select('td.x-date-mp-year');\r
24172 \r
24173             this.mpMonths.each(function(m, a, i){\r
24174                 i += 1;\r
24175                 if((i%2) === 0){\r
24176                     m.dom.xmonth = 5 + Math.round(i * 0.5);\r
24177                 }else{\r
24178                     m.dom.xmonth = Math.round((i-1) * 0.5);\r
24179                 }\r
24180             });\r
24181         }\r
24182     },\r
24183 \r
24184     // private\r
24185     showMonthPicker : function(){\r
24186         if(!this.disabled){\r
24187             this.createMonthPicker();\r
24188             var size = this.el.getSize();\r
24189             this.monthPicker.setSize(size);\r
24190             this.monthPicker.child('table').setSize(size);\r
24191 \r
24192             this.mpSelMonth = (this.activeDate || this.value).getMonth();\r
24193             this.updateMPMonth(this.mpSelMonth);\r
24194             this.mpSelYear = (this.activeDate || this.value).getFullYear();\r
24195             this.updateMPYear(this.mpSelYear);\r
24196 \r
24197             this.monthPicker.slideIn('t', {duration:0.2});\r
24198         }\r
24199     },\r
24200 \r
24201     // private\r
24202     updateMPYear : function(y){\r
24203         this.mpyear = y;\r
24204         var ys = this.mpYears.elements;\r
24205         for(var i = 1; i <= 10; i++){\r
24206             var td = ys[i-1], y2;\r
24207             if((i%2) === 0){\r
24208                 y2 = y + Math.round(i * 0.5);\r
24209                 td.firstChild.innerHTML = y2;\r
24210                 td.xyear = y2;\r
24211             }else{\r
24212                 y2 = y - (5-Math.round(i * 0.5));\r
24213                 td.firstChild.innerHTML = y2;\r
24214                 td.xyear = y2;\r
24215             }\r
24216             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
24217         }\r
24218     },\r
24219 \r
24220     // private\r
24221     updateMPMonth : function(sm){\r
24222         this.mpMonths.each(function(m, a, i){\r
24223             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');\r
24224         });\r
24225     },\r
24226 \r
24227     // private\r
24228     selectMPMonth : function(m){\r
24229 \r
24230     },\r
24231 \r
24232     // private\r
24233     onMonthClick : function(e, t){\r
24234         e.stopEvent();\r
24235         var el = new Ext.Element(t), pn;\r
24236         if(el.is('button.x-date-mp-cancel')){\r
24237             this.hideMonthPicker();\r
24238         }\r
24239         else if(el.is('button.x-date-mp-ok')){\r
24240             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());\r
24241             if(d.getMonth() != this.mpSelMonth){\r
24242                 // 'fix' the JS rolling date conversion if needed\r
24243                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();\r
24244             }\r
24245             this.update(d);\r
24246             this.hideMonthPicker();\r
24247         }\r
24248         else if((pn = el.up('td.x-date-mp-month', 2))){\r
24249             this.mpMonths.removeClass('x-date-mp-sel');\r
24250             pn.addClass('x-date-mp-sel');\r
24251             this.mpSelMonth = pn.dom.xmonth;\r
24252         }\r
24253         else if((pn = el.up('td.x-date-mp-year', 2))){\r
24254             this.mpYears.removeClass('x-date-mp-sel');\r
24255             pn.addClass('x-date-mp-sel');\r
24256             this.mpSelYear = pn.dom.xyear;\r
24257         }\r
24258         else if(el.is('a.x-date-mp-prev')){\r
24259             this.updateMPYear(this.mpyear-10);\r
24260         }\r
24261         else if(el.is('a.x-date-mp-next')){\r
24262             this.updateMPYear(this.mpyear+10);\r
24263         }\r
24264     },\r
24265 \r
24266     // private\r
24267     onMonthDblClick : function(e, t){\r
24268         e.stopEvent();\r
24269         var el = new Ext.Element(t), pn;\r
24270         if((pn = el.up('td.x-date-mp-month', 2))){\r
24271             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));\r
24272             this.hideMonthPicker();\r
24273         }\r
24274         else if((pn = el.up('td.x-date-mp-year', 2))){\r
24275             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));\r
24276             this.hideMonthPicker();\r
24277         }\r
24278     },\r
24279 \r
24280     // private\r
24281     hideMonthPicker : function(disableAnim){\r
24282         if(this.monthPicker){\r
24283             if(disableAnim === true){\r
24284                 this.monthPicker.hide();\r
24285             }else{\r
24286                 this.monthPicker.slideOut('t', {duration:0.2});\r
24287             }\r
24288         }\r
24289     },\r
24290 \r
24291     // private\r
24292     showPrevMonth : function(e){\r
24293         this.update(this.activeDate.add('mo', -1));\r
24294     },\r
24295 \r
24296     // private\r
24297     showNextMonth : function(e){\r
24298         this.update(this.activeDate.add('mo', 1));\r
24299     },\r
24300 \r
24301     // private\r
24302     showPrevYear : function(){\r
24303         this.update(this.activeDate.add('y', -1));\r
24304     },\r
24305 \r
24306     // private\r
24307     showNextYear : function(){\r
24308         this.update(this.activeDate.add('y', 1));\r
24309     },\r
24310 \r
24311     // private\r
24312     handleMouseWheel : function(e){\r
24313         e.stopEvent();\r
24314         if(!this.disabled){\r
24315             var delta = e.getWheelDelta();\r
24316             if(delta > 0){\r
24317                 this.showPrevMonth();\r
24318             } else if(delta < 0){\r
24319                 this.showNextMonth();\r
24320             }\r
24321         }\r
24322     },\r
24323 \r
24324     // private\r
24325     handleDateClick : function(e, t){\r
24326         e.stopEvent();\r
24327         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){\r
24328             this.setValue(new Date(t.dateValue));\r
24329             this.fireEvent('select', this, this.value);\r
24330         }\r
24331     },\r
24332 \r
24333     // private\r
24334     selectToday : function(){\r
24335         if(this.todayBtn && !this.todayBtn.disabled){\r
24336             this.setValue(new Date().clearTime());\r
24337             this.fireEvent('select', this, this.value);\r
24338         }\r
24339     },\r
24340 \r
24341     // private\r
24342     update : function(date, forceRefresh){\r
24343         if(this.rendered){\r
24344                 var vd = this.activeDate, vis = this.isVisible();\r
24345                 this.activeDate = date;\r
24346                 if(!forceRefresh && vd && this.el){\r
24347                     var t = date.getTime();\r
24348                     if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){\r
24349                         this.cells.removeClass('x-date-selected');\r
24350                         this.cells.each(function(c){\r
24351                            if(c.dom.firstChild.dateValue == t){\r
24352                                c.addClass('x-date-selected');\r
24353                                if(vis){\r
24354                                    Ext.fly(c.dom.firstChild).focus(50);\r
24355                                }\r
24356                                return false;\r
24357                            }\r
24358                         });\r
24359                         return;\r
24360                     }\r
24361                 }\r
24362                 var days = date.getDaysInMonth(),\r
24363                     firstOfMonth = date.getFirstDateOfMonth(),\r
24364                     startingPos = firstOfMonth.getDay()-this.startDay;\r
24365         \r
24366                 if(startingPos < 0){\r
24367                     startingPos += 7;\r
24368                 }\r
24369                 days += startingPos;\r
24370         \r
24371                 var pm = date.add('mo', -1),\r
24372                     prevStart = pm.getDaysInMonth()-startingPos,\r
24373                     cells = this.cells.elements,\r
24374                     textEls = this.textNodes,\r
24375                     // convert everything to numbers so it's fast\r
24376                     day = 86400000,\r
24377                     d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(),\r
24378                     today = new Date().clearTime().getTime(),\r
24379                     sel = date.clearTime(true).getTime(),\r
24380                     min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,\r
24381                     max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,\r
24382                     ddMatch = this.disabledDatesRE,\r
24383                     ddText = this.disabledDatesText,\r
24384                     ddays = this.disabledDays ? this.disabledDays.join('') : false,\r
24385                     ddaysText = this.disabledDaysText,\r
24386                     format = this.format;\r
24387         \r
24388                 if(this.showToday){\r
24389                     var td = new Date().clearTime(),\r
24390                         disable = (td < min || td > max ||\r
24391                         (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||\r
24392                         (ddays && ddays.indexOf(td.getDay()) != -1));\r
24393         \r
24394                     if(!this.disabled){\r
24395                         this.todayBtn.setDisabled(disable);\r
24396                         this.todayKeyListener[disable ? 'disable' : 'enable']();\r
24397                     }\r
24398                 }\r
24399         \r
24400                 var setCellClass = function(cal, cell){\r
24401                     cell.title = '';\r
24402                     var t = d.getTime();\r
24403                     cell.firstChild.dateValue = t;\r
24404                     if(t == today){\r
24405                         cell.className += ' x-date-today';\r
24406                         cell.title = cal.todayText;\r
24407                     }\r
24408                     if(t == sel){\r
24409                         cell.className += ' x-date-selected';\r
24410                         if(vis){\r
24411                             Ext.fly(cell.firstChild).focus(50);\r
24412                         }\r
24413                     }\r
24414                     // disabling\r
24415                     if(t < min) {\r
24416                         cell.className = ' x-date-disabled';\r
24417                         cell.title = cal.minText;\r
24418                         return;\r
24419                     }\r
24420                     if(t > max) {\r
24421                         cell.className = ' x-date-disabled';\r
24422                         cell.title = cal.maxText;\r
24423                         return;\r
24424                     }\r
24425                     if(ddays){\r
24426                         if(ddays.indexOf(d.getDay()) != -1){\r
24427                             cell.title = ddaysText;\r
24428                             cell.className = ' x-date-disabled';\r
24429                         }\r
24430                     }\r
24431                     if(ddMatch && format){\r
24432                         var fvalue = d.dateFormat(format);\r
24433                         if(ddMatch.test(fvalue)){\r
24434                             cell.title = ddText.replace('%0', fvalue);\r
24435                             cell.className = ' x-date-disabled';\r
24436                         }\r
24437                     }\r
24438                 };\r
24439         \r
24440                 var i = 0;\r
24441                 for(; i < startingPos; i++) {\r
24442                     textEls[i].innerHTML = (++prevStart);\r
24443                     d.setDate(d.getDate()+1);\r
24444                     cells[i].className = 'x-date-prevday';\r
24445                     setCellClass(this, cells[i]);\r
24446                 }\r
24447                 for(; i < days; i++){\r
24448                     var intDay = i - startingPos + 1;\r
24449                     textEls[i].innerHTML = (intDay);\r
24450                     d.setDate(d.getDate()+1);\r
24451                     cells[i].className = 'x-date-active';\r
24452                     setCellClass(this, cells[i]);\r
24453                 }\r
24454                 var extraDays = 0;\r
24455                 for(; i < 42; i++) {\r
24456                      textEls[i].innerHTML = (++extraDays);\r
24457                      d.setDate(d.getDate()+1);\r
24458                      cells[i].className = 'x-date-nextday';\r
24459                      setCellClass(this, cells[i]);\r
24460                 }\r
24461         \r
24462                 this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());\r
24463         \r
24464                 if(!this.internalRender){\r
24465                     var main = this.el.dom.firstChild,\r
24466                         w = main.offsetWidth;\r
24467                     this.el.setWidth(w + this.el.getBorderWidth('lr'));\r
24468                     Ext.fly(main).setWidth(w);\r
24469                     this.internalRender = true;\r
24470                     // opera does not respect the auto grow header center column\r
24471                     // then, after it gets a width opera refuses to recalculate\r
24472                     // without a second pass\r
24473                     if(Ext.isOpera && !this.secondPass){\r
24474                         main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';\r
24475                         this.secondPass = true;\r
24476                         this.update.defer(10, this, [date]);\r
24477                     }\r
24478                 }\r
24479         }\r
24480     },\r
24481 \r
24482     // private\r
24483     beforeDestroy : function() {\r
24484         if(this.rendered){\r
24485             this.keyNav.disable();\r
24486             this.keyNav = null;\r
24487             Ext.destroy(\r
24488                 this.leftClickRpt,\r
24489                 this.rightClickRpt,\r
24490                 this.monthPicker,\r
24491                 this.eventEl,\r
24492                 this.mbtn,\r
24493                 this.todayBtn\r
24494             );\r
24495         }\r
24496     }\r
24497 \r
24498     /**\r
24499      * @cfg {String} autoEl @hide\r
24500      */\r
24501 });\r
24502 \r
24503 Ext.reg('datepicker', Ext.DatePicker);\r
24504 /**
24505  * @class Ext.LoadMask
24506  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
24507  * config option is specified, the masking will be automatically synchronized with the store's loading
24508  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
24509  * element's Updater load indicator and will be destroyed after the initial load.
24510  * <p>Example usage:</p>
24511  *<pre><code>
24512 // Basic mask:
24513 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
24514 myMask.show();
24515 </code></pre>
24516  * @constructor
24517  * Create a new LoadMask
24518  * @param {Mixed} el The element or DOM node, or its id
24519  * @param {Object} config The config object
24520  */
24521 Ext.LoadMask = function(el, config){
24522     this.el = Ext.get(el);
24523     Ext.apply(this, config);
24524     if(this.store){
24525         this.store.on('beforeload', this.onBeforeLoad, this);
24526         this.store.on('load', this.onLoad, this);
24527         this.store.on('exception', this.onLoad, this);
24528         this.removeMask = Ext.value(this.removeMask, false);
24529     }else{
24530         var um = this.el.getUpdater();
24531         um.showLoadIndicator = false; // disable the default indicator
24532         um.on('beforeupdate', this.onBeforeLoad, this);
24533         um.on('update', this.onLoad, this);
24534         um.on('failure', this.onLoad, this);
24535         this.removeMask = Ext.value(this.removeMask, true);
24536     }
24537 };
24538
24539 Ext.LoadMask.prototype = {
24540     /**
24541      * @cfg {Ext.data.Store} store
24542      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
24543      * hidden on either load sucess, or load fail.
24544      */
24545     /**
24546      * @cfg {Boolean} removeMask
24547      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
24548      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
24549      */
24550     /**
24551      * @cfg {String} msg
24552      * The text to display in a centered loading message box (defaults to 'Loading...')
24553      */
24554     msg : 'Loading...',
24555     /**
24556      * @cfg {String} msgCls
24557      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
24558      */
24559     msgCls : 'x-mask-loading',
24560
24561     /**
24562      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
24563      * @type Boolean
24564      */
24565     disabled: false,
24566
24567     /**
24568      * Disables the mask to prevent it from being displayed
24569      */
24570     disable : function(){
24571        this.disabled = true;
24572     },
24573
24574     /**
24575      * Enables the mask so that it can be displayed
24576      */
24577     enable : function(){
24578         this.disabled = false;
24579     },
24580
24581     // private
24582     onLoad : function(){
24583         this.el.unmask(this.removeMask);
24584     },
24585
24586     // private
24587     onBeforeLoad : function(){
24588         if(!this.disabled){
24589             this.el.mask(this.msg, this.msgCls);
24590         }
24591     },
24592
24593     /**
24594      * Show this LoadMask over the configured Element.
24595      */
24596     show: function(){
24597         this.onBeforeLoad();
24598     },
24599
24600     /**
24601      * Hide this LoadMask.
24602      */
24603     hide: function(){
24604         this.onLoad();
24605     },
24606
24607     // private
24608     destroy : function(){
24609         if(this.store){
24610             this.store.un('beforeload', this.onBeforeLoad, this);
24611             this.store.un('load', this.onLoad, this);
24612             this.store.un('exception', this.onLoad, this);
24613         }else{
24614             var um = this.el.getUpdater();
24615             um.un('beforeupdate', this.onBeforeLoad, this);
24616             um.un('update', this.onLoad, this);
24617             um.un('failure', this.onLoad, this);
24618         }
24619     }
24620 };/**\r
24621  * @class Ext.Slider\r
24622  * @extends Ext.BoxComponent\r
24623  * Slider which supports vertical or horizontal orientation, keyboard adjustments,\r
24624  * configurable snapping, axis clicking and animation. Can be added as an item to\r
24625  * any container. Example usage:\r
24626 <pre><code>\r
24627 new Ext.Slider({\r
24628     renderTo: Ext.getBody(),\r
24629     width: 200,\r
24630     value: 50,\r
24631     increment: 10,\r
24632     minValue: 0,\r
24633     maxValue: 100\r
24634 });\r
24635 </code></pre>\r
24636  */\r
24637 Ext.Slider = Ext.extend(Ext.BoxComponent, {\r
24638         /**\r
24639          * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.\r
24640          */\r
24641         /**\r
24642          * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.\r
24643          */\r
24644     vertical: false,\r
24645         /**\r
24646          * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.\r
24647          */\r
24648     minValue: 0,\r
24649         /**\r
24650          * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.\r
24651          */\r
24652     maxValue: 100,\r
24653     /**\r
24654      * @cfg {Number/Boolean} decimalPrecision.\r
24655      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>\r
24656      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>\r
24657      */\r
24658     decimalPrecision: 0,\r
24659         /**\r
24660          * @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
24661          */\r
24662     keyIncrement: 1,\r
24663         /**\r
24664          * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.\r
24665          */\r
24666     increment: 0,\r
24667         // private\r
24668     clickRange: [5,15],\r
24669         /**\r
24670          * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true\r
24671          */\r
24672     clickToChange : true,\r
24673         /**\r
24674          * @cfg {Boolean} animate Turn on or off animation. Defaults to true\r
24675          */\r
24676     animate: true,\r
24677 \r
24678     /**\r
24679      * True while the thumb is in a drag operation\r
24680      * @type boolean\r
24681      */\r
24682     dragging: false,\r
24683 \r
24684     // private override\r
24685     initComponent : function(){\r
24686         if(!Ext.isDefined(this.value)){\r
24687             this.value = this.minValue;\r
24688         }\r
24689         Ext.Slider.superclass.initComponent.call(this);\r
24690         this.keyIncrement = Math.max(this.increment, this.keyIncrement);\r
24691         this.addEvents(\r
24692             /**\r
24693              * @event beforechange\r
24694              * Fires before the slider value is changed. By returning false from an event handler,\r
24695              * you can cancel the event and prevent the slider from changing.\r
24696                          * @param {Ext.Slider} slider The slider\r
24697                          * @param {Number} newValue The new value which the slider is being changed to.\r
24698                          * @param {Number} oldValue The old value which the slider was previously.\r
24699              */\r
24700                         'beforechange',\r
24701                         /**\r
24702                          * @event change\r
24703                          * Fires when the slider value is changed.\r
24704                          * @param {Ext.Slider} slider The slider\r
24705                          * @param {Number} newValue The new value which the slider has been changed to.\r
24706                          */\r
24707                         'change',\r
24708                         /**\r
24709                          * @event changecomplete\r
24710                          * Fires when the slider value is changed by the user and any drag operations have completed.\r
24711                          * @param {Ext.Slider} slider The slider\r
24712                          * @param {Number} newValue The new value which the slider has been changed to.\r
24713                          */\r
24714                         'changecomplete',\r
24715                         /**\r
24716                          * @event dragstart\r
24717              * Fires after a drag operation has started.\r
24718                          * @param {Ext.Slider} slider The slider\r
24719                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24720                          */\r
24721                         'dragstart',\r
24722                         /**\r
24723                          * @event drag\r
24724              * Fires continuously during the drag operation while the mouse is moving.\r
24725                          * @param {Ext.Slider} slider The slider\r
24726                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24727                          */\r
24728                         'drag',\r
24729                         /**\r
24730                          * @event dragend\r
24731              * Fires after the drag operation has completed.\r
24732                          * @param {Ext.Slider} slider The slider\r
24733                          * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
24734                          */\r
24735                         'dragend'\r
24736                 );\r
24737 \r
24738         if(this.vertical){\r
24739             Ext.apply(this, Ext.Slider.Vertical);\r
24740         }\r
24741     },\r
24742 \r
24743         // private override\r
24744     onRender : function(){\r
24745         this.autoEl = {\r
24746             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),\r
24747             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
24748         };\r
24749         Ext.Slider.superclass.onRender.apply(this, arguments);\r
24750         this.endEl = this.el.first();\r
24751         this.innerEl = this.endEl.first();\r
24752         this.thumb = this.innerEl.first();\r
24753         this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2;\r
24754         this.focusEl = this.thumb.next();\r
24755         this.initEvents();\r
24756     },\r
24757 \r
24758         // private override\r
24759     initEvents : function(){\r
24760         this.thumb.addClassOnOver('x-slider-thumb-over');\r
24761         this.mon(this.el, {\r
24762             scope: this,\r
24763             mousedown: this.onMouseDown,\r
24764             keydown: this.onKeyDown\r
24765         });\r
24766 \r
24767         this.focusEl.swallowEvent("click", true);\r
24768 \r
24769         this.tracker = new Ext.dd.DragTracker({\r
24770             onBeforeStart: this.onBeforeDragStart.createDelegate(this),\r
24771             onStart: this.onDragStart.createDelegate(this),\r
24772             onDrag: this.onDrag.createDelegate(this),\r
24773             onEnd: this.onDragEnd.createDelegate(this),\r
24774             tolerance: 3,\r
24775             autoStart: 300\r
24776         });\r
24777         this.tracker.initEl(this.thumb);\r
24778         this.on('beforedestroy', this.tracker.destroy, this.tracker);\r
24779     },\r
24780 \r
24781         // private override\r
24782     onMouseDown : function(e){\r
24783         if(this.disabled) {return;}\r
24784         if(this.clickToChange && e.target != this.thumb.dom){\r
24785             var local = this.innerEl.translatePoints(e.getXY());\r
24786             this.onClickChange(local);\r
24787         }\r
24788         this.focus();\r
24789     },\r
24790 \r
24791         // private\r
24792     onClickChange : function(local){\r
24793         if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){\r
24794             this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);\r
24795         }\r
24796     },\r
24797 \r
24798         // private\r
24799     onKeyDown : function(e){\r
24800         if(this.disabled){e.preventDefault();return;}\r
24801         var k = e.getKey();\r
24802         switch(k){\r
24803             case e.UP:\r
24804             case e.RIGHT:\r
24805                 e.stopEvent();\r
24806                 if(e.ctrlKey){\r
24807                     this.setValue(this.maxValue, undefined, true);\r
24808                 }else{\r
24809                     this.setValue(this.value+this.keyIncrement, undefined, true);\r
24810                 }\r
24811             break;\r
24812             case e.DOWN:\r
24813             case e.LEFT:\r
24814                 e.stopEvent();\r
24815                 if(e.ctrlKey){\r
24816                     this.setValue(this.minValue, undefined, true);\r
24817                 }else{\r
24818                     this.setValue(this.value-this.keyIncrement, undefined, true);\r
24819                 }\r
24820             break;\r
24821             default:\r
24822                 e.preventDefault();\r
24823         }\r
24824     },\r
24825 \r
24826         // private\r
24827     doSnap : function(value){\r
24828         if(!this.increment || this.increment == 1 || !value) {\r
24829             return value;\r
24830         }\r
24831         var newValue = value, inc = this.increment;\r
24832         var m = value % inc;\r
24833         if(m != 0){\r
24834             newValue -= m;\r
24835             if(m * 2 > inc){\r
24836                 newValue += inc;\r
24837             }else if(m * 2 < -inc){\r
24838                 newValue -= inc;\r
24839             }\r
24840         }\r
24841         return newValue.constrain(this.minValue,  this.maxValue);\r
24842     },\r
24843 \r
24844         // private\r
24845     afterRender : function(){\r
24846         Ext.Slider.superclass.afterRender.apply(this, arguments);\r
24847         if(this.value !== undefined){\r
24848             var v = this.normalizeValue(this.value);\r
24849             if(v !== this.value){\r
24850                 delete this.value;\r
24851                 this.setValue(v, false);\r
24852             }else{\r
24853                 this.moveThumb(this.translateValue(v), false);\r
24854             }\r
24855         }\r
24856     },\r
24857 \r
24858         // private\r
24859     getRatio : function(){\r
24860         var w = this.innerEl.getWidth();\r
24861         var v = this.maxValue - this.minValue;\r
24862         return v == 0 ? w : (w/v);\r
24863     },\r
24864 \r
24865         // private\r
24866     normalizeValue : function(v){\r
24867         v = this.doSnap(v);\r
24868         v = Ext.util.Format.round(v, this.decimalPrecision);\r
24869         v = v.constrain(this.minValue, this.maxValue);\r
24870         return v;\r
24871     },\r
24872 \r
24873         /**\r
24874          * Programmatically sets the value of the Slider. Ensures that the value is constrained within\r
24875          * the minValue and maxValue.\r
24876          * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)\r
24877          * @param {Boolean} animate Turn on or off animation, defaults to true\r
24878          */\r
24879     setValue : function(v, animate, changeComplete){\r
24880         v = this.normalizeValue(v);\r
24881         if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){\r
24882             this.value = v;\r
24883             this.moveThumb(this.translateValue(v), animate !== false);\r
24884             this.fireEvent('change', this, v);\r
24885             if(changeComplete){\r
24886                 this.fireEvent('changecomplete', this, v);\r
24887             }\r
24888         }\r
24889     },\r
24890 \r
24891         // private\r
24892     translateValue : function(v){\r
24893         var ratio = this.getRatio();\r
24894         return (v * ratio)-(this.minValue * ratio)-this.halfThumb;\r
24895     },\r
24896 \r
24897         reverseValue : function(pos){\r
24898         var ratio = this.getRatio();\r
24899         return (pos+this.halfThumb+(this.minValue * ratio))/ratio;\r
24900     },\r
24901 \r
24902         // private\r
24903     moveThumb: function(v, animate){\r
24904         if(!animate || this.animate === false){\r
24905             this.thumb.setLeft(v);\r
24906         }else{\r
24907             this.thumb.shift({left: v, stopFx: true, duration:.35});\r
24908         }\r
24909     },\r
24910 \r
24911         // private\r
24912     focus : function(){\r
24913         this.focusEl.focus(10);\r
24914     },\r
24915 \r
24916         // private\r
24917     onBeforeDragStart : function(e){\r
24918         return !this.disabled;\r
24919     },\r
24920 \r
24921         // private\r
24922     onDragStart: function(e){\r
24923         this.thumb.addClass('x-slider-thumb-drag');\r
24924         this.dragging = true;\r
24925         this.dragStartValue = this.value;\r
24926         this.fireEvent('dragstart', this, e);\r
24927     },\r
24928 \r
24929         // private\r
24930     onDrag: function(e){\r
24931         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
24932         this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false);\r
24933         this.fireEvent('drag', this, e);\r
24934     },\r
24935 \r
24936         // private\r
24937     onDragEnd: function(e){\r
24938         this.thumb.removeClass('x-slider-thumb-drag');\r
24939         this.dragging = false;\r
24940         this.fireEvent('dragend', this, e);\r
24941         if(this.dragStartValue != this.value){\r
24942             this.fireEvent('changecomplete', this, this.value);\r
24943         }\r
24944     },\r
24945 \r
24946         // private\r
24947     onResize : function(w, h){\r
24948         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));\r
24949         this.syncThumb();\r
24950     },\r
24951     \r
24952     //private\r
24953     onDisable: function(){\r
24954         Ext.Slider.superclass.onDisable.call(this);\r
24955         this.thumb.addClass(this.disabledClass);\r
24956         if(Ext.isIE){\r
24957             //IE breaks when using overflow visible and opacity other than 1.\r
24958             //Create a place holder for the thumb and display it.\r
24959             var xy = this.thumb.getXY();\r
24960             this.thumb.hide();\r
24961             this.innerEl.addClass(this.disabledClass).dom.disabled = true;\r
24962             if (!this.thumbHolder){\r
24963                 this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});    \r
24964             }\r
24965             this.thumbHolder.show().setXY(xy);\r
24966         }\r
24967     },\r
24968     \r
24969     //private\r
24970     onEnable: function(){\r
24971         Ext.Slider.superclass.onEnable.call(this);\r
24972         this.thumb.removeClass(this.disabledClass);\r
24973         if(Ext.isIE){\r
24974             this.innerEl.removeClass(this.disabledClass).dom.disabled = false;\r
24975             if (this.thumbHolder){\r
24976                 this.thumbHolder.hide();\r
24977             }\r
24978             this.thumb.show();\r
24979             this.syncThumb();\r
24980         }\r
24981     },\r
24982     \r
24983     /**\r
24984      * Synchronizes the thumb position to the proper proportion of the total component width based\r
24985      * on the current slider {@link #value}.  This will be called automatically when the Slider\r
24986      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
24987      * another resize handler to sync the Slider if necessary.\r
24988      */\r
24989     syncThumb : function(){\r
24990         if(this.rendered){\r
24991             this.moveThumb(this.translateValue(this.value));\r
24992         }\r
24993     },\r
24994 \r
24995         /**\r
24996          * Returns the current value of the slider\r
24997          * @return {Number} The current value of the slider\r
24998          */\r
24999     getValue : function(){\r
25000         return this.value;\r
25001     }\r
25002 });\r
25003 Ext.reg('slider', Ext.Slider);\r
25004 \r
25005 // private class to support vertical sliders\r
25006 Ext.Slider.Vertical = {\r
25007     onResize : function(w, h){\r
25008         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));\r
25009         this.syncThumb();\r
25010     },\r
25011 \r
25012     getRatio : function(){\r
25013         var h = this.innerEl.getHeight();\r
25014         var v = this.maxValue - this.minValue;\r
25015         return h/v;\r
25016     },\r
25017 \r
25018     moveThumb: function(v, animate){\r
25019         if(!animate || this.animate === false){\r
25020             this.thumb.setBottom(v);\r
25021         }else{\r
25022             this.thumb.shift({bottom: v, stopFx: true, duration:.35});\r
25023         }\r
25024     },\r
25025 \r
25026     onDrag: function(e){\r
25027         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
25028         var bottom = this.innerEl.getHeight()-pos.top;\r
25029         this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false);\r
25030         this.fireEvent('drag', this, e);\r
25031     },\r
25032 \r
25033     onClickChange : function(local){\r
25034         if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){\r
25035             var bottom = this.innerEl.getHeight()-local.top;\r
25036             this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true);\r
25037         }\r
25038     }\r
25039 };/**\r
25040  * @class Ext.ProgressBar\r
25041  * @extends Ext.BoxComponent\r
25042  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>\r
25043  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the\r
25044  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress\r
25045  * throughout an operation that has predictable points of interest at which you can update the control.</p>\r
25046  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it\r
25047  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time\r
25048  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in\r
25049  * which you have no need for indicating intermediate progress.</p>\r
25050  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
25051  * @cfg {String} text The progress bar text (defaults to '')\r
25052  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress\r
25053  * bar's internal text element)\r
25054  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)\r
25055  * @xtype progress\r
25056  */\r
25057 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {\r
25058    /**\r
25059     * @cfg {String} baseCls\r
25060     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')\r
25061     */\r
25062     baseCls : 'x-progress',\r
25063     \r
25064     /**\r
25065     * @cfg {Boolean} animate\r
25066     * True to animate the progress bar during transitions (defaults to false)\r
25067     */\r
25068     animate : false,\r
25069 \r
25070     // private\r
25071     waitTimer : null,\r
25072 \r
25073     // private\r
25074     initComponent : function(){\r
25075         Ext.ProgressBar.superclass.initComponent.call(this);\r
25076         this.addEvents(\r
25077             /**\r
25078              * @event update\r
25079              * Fires after each update interval\r
25080              * @param {Ext.ProgressBar} this\r
25081              * @param {Number} The current progress value\r
25082              * @param {String} The current progress text\r
25083              */\r
25084             "update"\r
25085         );\r
25086     },\r
25087 \r
25088     // private\r
25089     onRender : function(ct, position){\r
25090         var tpl = new Ext.Template(\r
25091             '<div class="{cls}-wrap">',\r
25092                 '<div class="{cls}-inner">',\r
25093                     '<div class="{cls}-bar">',\r
25094                         '<div class="{cls}-text">',\r
25095                             '<div>&#160;</div>',\r
25096                         '</div>',\r
25097                     '</div>',\r
25098                     '<div class="{cls}-text {cls}-text-back">',\r
25099                         '<div>&#160;</div>',\r
25100                     '</div>',\r
25101                 '</div>',\r
25102             '</div>'\r
25103         );\r
25104 \r
25105         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)\r
25106                 : tpl.append(ct, {cls: this.baseCls}, true);\r
25107                         \r
25108         if(this.id){\r
25109             this.el.dom.id = this.id;\r
25110         }\r
25111         var inner = this.el.dom.firstChild;\r
25112         this.progressBar = Ext.get(inner.firstChild);\r
25113 \r
25114         if(this.textEl){\r
25115             //use an external text el\r
25116             this.textEl = Ext.get(this.textEl);\r
25117             delete this.textTopEl;\r
25118         }else{\r
25119             //setup our internal layered text els\r
25120             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);\r
25121             var textBackEl = Ext.get(inner.childNodes[1]);\r
25122             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');\r
25123             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);\r
25124             this.textEl.setWidth(inner.offsetWidth);\r
25125         }\r
25126         this.progressBar.setHeight(inner.offsetHeight);\r
25127     },\r
25128     \r
25129     // private\r
25130     afterRender : function(){\r
25131         Ext.ProgressBar.superclass.afterRender.call(this);\r
25132         if(this.value){\r
25133             this.updateProgress(this.value, this.text);\r
25134         }else{\r
25135             this.updateText(this.text);\r
25136         }\r
25137     },\r
25138 \r
25139     /**\r
25140      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,\r
25141      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even\r
25142      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for\r
25143      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.\r
25144      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
25145      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
25146      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is\r
25147      * not specified, the default for the class is used (default to false)\r
25148      * @return {Ext.ProgressBar} this\r
25149      */\r
25150     updateProgress : function(value, text, animate){\r
25151         this.value = value || 0;\r
25152         if(text){\r
25153             this.updateText(text);\r
25154         }\r
25155         if(this.rendered){\r
25156             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);\r
25157             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));\r
25158             if(this.textTopEl){\r
25159                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves\r
25160                 this.textTopEl.removeClass('x-hidden').setWidth(w);\r
25161             }\r
25162         }\r
25163         this.fireEvent('update', this, value, text);\r
25164         return this;\r
25165     },\r
25166 \r
25167     /**\r
25168      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress\r
25169      * bar will automatically reset after a fixed amount of time and optionally call a callback function\r
25170      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must\r
25171      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with\r
25172      * the following properties:\r
25173      * <pre>\r
25174 Property   Type          Description\r
25175 ---------- ------------  ----------------------------------------------------------------------\r
25176 duration   Number        The length of time in milliseconds that the progress bar should\r
25177                          run before resetting itself (defaults to undefined, in which case it\r
25178                          will run indefinitely until reset is called)\r
25179 interval   Number        The length of time in milliseconds between each progress update\r
25180                          (defaults to 1000 ms)\r
25181 animate    Boolean       Whether to animate the transition of the progress bar. If this value is\r
25182                          not specified, the default for the class is used.                                                   \r
25183 increment  Number        The number of progress update segments to display within the progress\r
25184                          bar (defaults to 10).  If the bar reaches the end and is still\r
25185                          updating, it will automatically wrap back to the beginning.\r
25186 text       String        Optional text to display in the progress bar element (defaults to '').\r
25187 fn         Function      A callback function to execute after the progress bar finishes auto-\r
25188                          updating.  The function will be called with no arguments.  This function\r
25189                          will be ignored if duration is not specified since in that case the\r
25190                          progress bar can only be stopped programmatically, so any required function\r
25191                          should be called by the same code after it resets the progress bar.\r
25192 scope      Object        The scope that is passed to the callback function (only applies when\r
25193                          duration and fn are both passed).\r
25194 </pre>\r
25195          *\r
25196          * Example usage:\r
25197          * <pre><code>\r
25198 var p = new Ext.ProgressBar({\r
25199    renderTo: 'my-el'\r
25200 });\r
25201 \r
25202 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)\r
25203 p.wait({\r
25204    interval: 100, //bar will move fast!\r
25205    duration: 5000,\r
25206    increment: 15,\r
25207    text: 'Updating...',\r
25208    scope: this,\r
25209    fn: function(){\r
25210       Ext.fly('status').update('Done!');\r
25211    }\r
25212 });\r
25213 \r
25214 //Or update indefinitely until some async action completes, then reset manually\r
25215 p.wait();\r
25216 myAction.on('complete', function(){\r
25217     p.reset();\r
25218     Ext.fly('status').update('Done!');\r
25219 });\r
25220 </code></pre>\r
25221      * @param {Object} config (optional) Configuration options\r
25222      * @return {Ext.ProgressBar} this\r
25223      */\r
25224     wait : function(o){\r
25225         if(!this.waitTimer){\r
25226             var scope = this;\r
25227             o = o || {};\r
25228             this.updateText(o.text);\r
25229             this.waitTimer = Ext.TaskMgr.start({\r
25230                 run: function(i){\r
25231                     var inc = o.increment || 10;\r
25232                     i -= 1;\r
25233                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);\r
25234                 },\r
25235                 interval: o.interval || 1000,\r
25236                 duration: o.duration,\r
25237                 onStop: function(){\r
25238                     if(o.fn){\r
25239                         o.fn.apply(o.scope || this);\r
25240                     }\r
25241                     this.reset();\r
25242                 },\r
25243                 scope: scope\r
25244             });\r
25245         }\r
25246         return this;\r
25247     },\r
25248 \r
25249     /**\r
25250      * Returns true if the progress bar is currently in a {@link #wait} operation\r
25251      * @return {Boolean} True if waiting, else false\r
25252      */\r
25253     isWaiting : function(){\r
25254         return this.waitTimer !== null;\r
25255     },\r
25256 \r
25257     /**\r
25258      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress\r
25259      * bar itself will display the updated text.\r
25260      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
25261      * @return {Ext.ProgressBar} this\r
25262      */\r
25263     updateText : function(text){\r
25264         this.text = text || '&#160;';\r
25265         if(this.rendered){\r
25266             this.textEl.update(this.text);\r
25267         }\r
25268         return this;\r
25269     },\r
25270     \r
25271     /**\r
25272      * Synchronizes the inner bar width to the proper proportion of the total componet width based\r
25273      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar\r
25274      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
25275      * another resize handler to sync the ProgressBar if necessary.\r
25276      */\r
25277     syncProgressBar : function(){\r
25278         if(this.value){\r
25279             this.updateProgress(this.value, this.text);\r
25280         }\r
25281         return this;\r
25282     },\r
25283 \r
25284     /**\r
25285      * Sets the size of the progress bar.\r
25286      * @param {Number} width The new width in pixels\r
25287      * @param {Number} height The new height in pixels\r
25288      * @return {Ext.ProgressBar} this\r
25289      */\r
25290     setSize : function(w, h){\r
25291         Ext.ProgressBar.superclass.setSize.call(this, w, h);\r
25292         if(this.textTopEl){\r
25293             var inner = this.el.dom.firstChild;\r
25294             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);\r
25295         }\r
25296         this.syncProgressBar();\r
25297         return this;\r
25298     },\r
25299 \r
25300     /**\r
25301      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress\r
25302      * bar will also be hidden (using the {@link #hideMode} property internally).\r
25303      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)\r
25304      * @return {Ext.ProgressBar} this\r
25305      */\r
25306     reset : function(hide){\r
25307         this.updateProgress(0);\r
25308         if(this.textTopEl){\r
25309             this.textTopEl.addClass('x-hidden');\r
25310         }\r
25311         if(this.waitTimer){\r
25312             this.waitTimer.onStop = null; //prevent recursion\r
25313             Ext.TaskMgr.stop(this.waitTimer);\r
25314             this.waitTimer = null;\r
25315         }\r
25316         if(hide === true){\r
25317             this.hide();\r
25318         }\r
25319         return this;\r
25320     }\r
25321 });\r
25322 Ext.reg('progress', Ext.ProgressBar);/*
25323  * These classes are derivatives of the similarly named classes in the YUI Library.
25324  * The original license:
25325  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
25326  * Code licensed under the BSD License:
25327  * http://developer.yahoo.net/yui/license.txt
25328  */
25329
25330 (function() {
25331
25332 var Event=Ext.EventManager;
25333 var Dom=Ext.lib.Dom;
25334
25335 /**
25336  * @class Ext.dd.DragDrop
25337  * Defines the interface and base operation of items that that can be
25338  * dragged or can be drop targets.  It was designed to be extended, overriding
25339  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
25340  * Up to three html elements can be associated with a DragDrop instance:
25341  * <ul>
25342  * <li>linked element: the element that is passed into the constructor.
25343  * This is the element which defines the boundaries for interaction with
25344  * other DragDrop objects.</li>
25345  * <li>handle element(s): The drag operation only occurs if the element that
25346  * was clicked matches a handle element.  By default this is the linked
25347  * element, but there are times that you will want only a portion of the
25348  * linked element to initiate the drag operation, and the setHandleElId()
25349  * method provides a way to define this.</li>
25350  * <li>drag element: this represents the element that would be moved along
25351  * with the cursor during a drag operation.  By default, this is the linked
25352  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
25353  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
25354  * </li>
25355  * </ul>
25356  * This class should not be instantiated until the onload event to ensure that
25357  * the associated elements are available.
25358  * The following would define a DragDrop obj that would interact with any
25359  * other DragDrop obj in the "group1" group:
25360  * <pre>
25361  *  dd = new Ext.dd.DragDrop("div1", "group1");
25362  * </pre>
25363  * Since none of the event handlers have been implemented, nothing would
25364  * actually happen if you were to run the code above.  Normally you would
25365  * override this class or one of the default implementations, but you can
25366  * also override the methods you want on an instance of the class...
25367  * <pre>
25368  *  dd.onDragDrop = function(e, id) {
25369  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
25370  *  }
25371  * </pre>
25372  * @constructor
25373  * @param {String} id of the element that is linked to this instance
25374  * @param {String} sGroup the group of related DragDrop objects
25375  * @param {object} config an object containing configurable attributes
25376  *                Valid properties for DragDrop:
25377  *                    padding, isTarget, maintainOffset, primaryButtonOnly
25378  */
25379 Ext.dd.DragDrop = function(id, sGroup, config) {
25380     if(id) {
25381         this.init(id, sGroup, config);
25382     }
25383 };
25384
25385 Ext.dd.DragDrop.prototype = {
25386
25387     /**
25388      * Set to false to enable a DragDrop object to fire drag events while dragging
25389      * over its own Element. Defaults to true - DragDrop objects do not by default
25390      * fire drag events to themselves.
25391      * @property ignoreSelf
25392      * @type Boolean
25393      */
25394
25395     /**
25396      * The id of the element associated with this object.  This is what we
25397      * refer to as the "linked element" because the size and position of
25398      * this element is used to determine when the drag and drop objects have
25399      * interacted.
25400      * @property id
25401      * @type String
25402      */
25403     id: null,
25404
25405     /**
25406      * Configuration attributes passed into the constructor
25407      * @property config
25408      * @type object
25409      */
25410     config: null,
25411
25412     /**
25413      * The id of the element that will be dragged.  By default this is same
25414      * as the linked element , but could be changed to another element. Ex:
25415      * Ext.dd.DDProxy
25416      * @property dragElId
25417      * @type String
25418      * @private
25419      */
25420     dragElId: null,
25421
25422     /**
25423      * The ID of the element that initiates the drag operation.  By default
25424      * this is the linked element, but could be changed to be a child of this
25425      * element.  This lets us do things like only starting the drag when the
25426      * header element within the linked html element is clicked.
25427      * @property handleElId
25428      * @type String
25429      * @private
25430      */
25431     handleElId: null,
25432
25433     /**
25434      * An object who's property names identify HTML tags to be considered invalid as drag handles.
25435      * A non-null property value identifies the tag as invalid. Defaults to the 
25436      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
25437 {
25438     A: "A"
25439 }</code></pre>
25440      * @property invalidHandleTypes
25441      * @type Object
25442      */
25443     invalidHandleTypes: null,
25444
25445     /**
25446      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
25447      * A non-null property value identifies the ID as invalid. For example, to prevent
25448      * dragging from being initiated on element ID "foo", use:<pre><code>
25449 {
25450     foo: true
25451 }</code></pre>
25452      * @property invalidHandleIds
25453      * @type Object
25454      */
25455     invalidHandleIds: null,
25456
25457     /**
25458      * An Array of CSS class names for elements to be considered in valid as drag handles.
25459      * @property invalidHandleClasses
25460      * @type Array
25461      */
25462     invalidHandleClasses: null,
25463
25464     /**
25465      * The linked element's absolute X position at the time the drag was
25466      * started
25467      * @property startPageX
25468      * @type int
25469      * @private
25470      */
25471     startPageX: 0,
25472
25473     /**
25474      * The linked element's absolute X position at the time the drag was
25475      * started
25476      * @property startPageY
25477      * @type int
25478      * @private
25479      */
25480     startPageY: 0,
25481
25482     /**
25483      * The group defines a logical collection of DragDrop objects that are
25484      * related.  Instances only get events when interacting with other
25485      * DragDrop object in the same group.  This lets us define multiple
25486      * groups using a single DragDrop subclass if we want.
25487      * @property groups
25488      * @type object An object in the format {'group1':true, 'group2':true}
25489      */
25490     groups: null,
25491
25492     /**
25493      * Individual drag/drop instances can be locked.  This will prevent
25494      * onmousedown start drag.
25495      * @property locked
25496      * @type boolean
25497      * @private
25498      */
25499     locked: false,
25500
25501     /**
25502      * Lock this instance
25503      * @method lock
25504      */
25505     lock: function() { this.locked = true; },
25506
25507     /**
25508      * When set to true, other DD objects in cooperating DDGroups do not receive
25509      * notification events when this DD object is dragged over them. Defaults to false.
25510      * @property moveOnly
25511      * @type boolean
25512      */
25513     moveOnly: false,
25514
25515     /**
25516      * Unlock this instace
25517      * @method unlock
25518      */
25519     unlock: function() { this.locked = false; },
25520
25521     /**
25522      * By default, all instances can be a drop target.  This can be disabled by
25523      * setting isTarget to false.
25524      * @property isTarget
25525      * @type boolean
25526      */
25527     isTarget: true,
25528
25529     /**
25530      * The padding configured for this drag and drop object for calculating
25531      * the drop zone intersection with this object.
25532      * @property padding
25533      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
25534      */
25535     padding: null,
25536
25537     /**
25538      * Cached reference to the linked element
25539      * @property _domRef
25540      * @private
25541      */
25542     _domRef: null,
25543
25544     /**
25545      * Internal typeof flag
25546      * @property __ygDragDrop
25547      * @private
25548      */
25549     __ygDragDrop: true,
25550
25551     /**
25552      * Set to true when horizontal contraints are applied
25553      * @property constrainX
25554      * @type boolean
25555      * @private
25556      */
25557     constrainX: false,
25558
25559     /**
25560      * Set to true when vertical contraints are applied
25561      * @property constrainY
25562      * @type boolean
25563      * @private
25564      */
25565     constrainY: false,
25566
25567     /**
25568      * The left constraint
25569      * @property minX
25570      * @type int
25571      * @private
25572      */
25573     minX: 0,
25574
25575     /**
25576      * The right constraint
25577      * @property maxX
25578      * @type int
25579      * @private
25580      */
25581     maxX: 0,
25582
25583     /**
25584      * The up constraint
25585      * @property minY
25586      * @type int
25587      * @type int
25588      * @private
25589      */
25590     minY: 0,
25591
25592     /**
25593      * The down constraint
25594      * @property maxY
25595      * @type int
25596      * @private
25597      */
25598     maxY: 0,
25599
25600     /**
25601      * Maintain offsets when we resetconstraints.  Set to true when you want
25602      * the position of the element relative to its parent to stay the same
25603      * when the page changes
25604      *
25605      * @property maintainOffset
25606      * @type boolean
25607      */
25608     maintainOffset: false,
25609
25610     /**
25611      * Array of pixel locations the element will snap to if we specified a
25612      * horizontal graduation/interval.  This array is generated automatically
25613      * when you define a tick interval.
25614      * @property xTicks
25615      * @type int[]
25616      */
25617     xTicks: null,
25618
25619     /**
25620      * Array of pixel locations the element will snap to if we specified a
25621      * vertical graduation/interval.  This array is generated automatically
25622      * when you define a tick interval.
25623      * @property yTicks
25624      * @type int[]
25625      */
25626     yTicks: null,
25627
25628     /**
25629      * By default the drag and drop instance will only respond to the primary
25630      * button click (left button for a right-handed mouse).  Set to true to
25631      * allow drag and drop to start with any mouse click that is propogated
25632      * by the browser
25633      * @property primaryButtonOnly
25634      * @type boolean
25635      */
25636     primaryButtonOnly: true,
25637
25638     /**
25639      * The availabe property is false until the linked dom element is accessible.
25640      * @property available
25641      * @type boolean
25642      */
25643     available: false,
25644
25645     /**
25646      * By default, drags can only be initiated if the mousedown occurs in the
25647      * region the linked element is.  This is done in part to work around a
25648      * bug in some browsers that mis-report the mousedown if the previous
25649      * mouseup happened outside of the window.  This property is set to true
25650      * if outer handles are defined.
25651      *
25652      * @property hasOuterHandles
25653      * @type boolean
25654      * @default false
25655      */
25656     hasOuterHandles: false,
25657
25658     /**
25659      * Code that executes immediately before the startDrag event
25660      * @method b4StartDrag
25661      * @private
25662      */
25663     b4StartDrag: function(x, y) { },
25664
25665     /**
25666      * Abstract method called after a drag/drop object is clicked
25667      * and the drag or mousedown time thresholds have beeen met.
25668      * @method startDrag
25669      * @param {int} X click location
25670      * @param {int} Y click location
25671      */
25672     startDrag: function(x, y) { /* override this */ },
25673
25674     /**
25675      * Code that executes immediately before the onDrag event
25676      * @method b4Drag
25677      * @private
25678      */
25679     b4Drag: function(e) { },
25680
25681     /**
25682      * Abstract method called during the onMouseMove event while dragging an
25683      * object.
25684      * @method onDrag
25685      * @param {Event} e the mousemove event
25686      */
25687     onDrag: function(e) { /* override this */ },
25688
25689     /**
25690      * Abstract method called when this element fist begins hovering over
25691      * another DragDrop obj
25692      * @method onDragEnter
25693      * @param {Event} e the mousemove event
25694      * @param {String|DragDrop[]} id In POINT mode, the element
25695      * id this is hovering over.  In INTERSECT mode, an array of one or more
25696      * dragdrop items being hovered over.
25697      */
25698     onDragEnter: function(e, id) { /* override this */ },
25699
25700     /**
25701      * Code that executes immediately before the onDragOver event
25702      * @method b4DragOver
25703      * @private
25704      */
25705     b4DragOver: function(e) { },
25706
25707     /**
25708      * Abstract method called when this element is hovering over another
25709      * DragDrop obj
25710      * @method onDragOver
25711      * @param {Event} e the mousemove event
25712      * @param {String|DragDrop[]} id In POINT mode, the element
25713      * id this is hovering over.  In INTERSECT mode, an array of dd items
25714      * being hovered over.
25715      */
25716     onDragOver: function(e, id) { /* override this */ },
25717
25718     /**
25719      * Code that executes immediately before the onDragOut event
25720      * @method b4DragOut
25721      * @private
25722      */
25723     b4DragOut: function(e) { },
25724
25725     /**
25726      * Abstract method called when we are no longer hovering over an element
25727      * @method onDragOut
25728      * @param {Event} e the mousemove event
25729      * @param {String|DragDrop[]} id In POINT mode, the element
25730      * id this was hovering over.  In INTERSECT mode, an array of dd items
25731      * that the mouse is no longer over.
25732      */
25733     onDragOut: function(e, id) { /* override this */ },
25734
25735     /**
25736      * Code that executes immediately before the onDragDrop event
25737      * @method b4DragDrop
25738      * @private
25739      */
25740     b4DragDrop: function(e) { },
25741
25742     /**
25743      * Abstract method called when this item is dropped on another DragDrop
25744      * obj
25745      * @method onDragDrop
25746      * @param {Event} e the mouseup event
25747      * @param {String|DragDrop[]} id In POINT mode, the element
25748      * id this was dropped on.  In INTERSECT mode, an array of dd items this
25749      * was dropped on.
25750      */
25751     onDragDrop: function(e, id) { /* override this */ },
25752
25753     /**
25754      * Abstract method called when this item is dropped on an area with no
25755      * drop target
25756      * @method onInvalidDrop
25757      * @param {Event} e the mouseup event
25758      */
25759     onInvalidDrop: function(e) { /* override this */ },
25760
25761     /**
25762      * Code that executes immediately before the endDrag event
25763      * @method b4EndDrag
25764      * @private
25765      */
25766     b4EndDrag: function(e) { },
25767
25768     /**
25769      * Fired when we are done dragging the object
25770      * @method endDrag
25771      * @param {Event} e the mouseup event
25772      */
25773     endDrag: function(e) { /* override this */ },
25774
25775     /**
25776      * Code executed immediately before the onMouseDown event
25777      * @method b4MouseDown
25778      * @param {Event} e the mousedown event
25779      * @private
25780      */
25781     b4MouseDown: function(e) {  },
25782
25783     /**
25784      * Event handler that fires when a drag/drop obj gets a mousedown
25785      * @method onMouseDown
25786      * @param {Event} e the mousedown event
25787      */
25788     onMouseDown: function(e) { /* override this */ },
25789
25790     /**
25791      * Event handler that fires when a drag/drop obj gets a mouseup
25792      * @method onMouseUp
25793      * @param {Event} e the mouseup event
25794      */
25795     onMouseUp: function(e) { /* override this */ },
25796
25797     /**
25798      * Override the onAvailable method to do what is needed after the initial
25799      * position was determined.
25800      * @method onAvailable
25801      */
25802     onAvailable: function () {
25803     },
25804
25805     /**
25806      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
25807      * @type Object
25808      */
25809     defaultPadding : {left:0, right:0, top:0, bottom:0},
25810
25811     /**
25812      * Initializes the drag drop object's constraints to restrict movement to a certain element.
25813  *
25814  * Usage:
25815  <pre><code>
25816  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
25817                 { dragElId: "existingProxyDiv" });
25818  dd.startDrag = function(){
25819      this.constrainTo("parent-id");
25820  };
25821  </code></pre>
25822  * Or you can initalize it using the {@link Ext.Element} object:
25823  <pre><code>
25824  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
25825      startDrag : function(){
25826          this.constrainTo("parent-id");
25827      }
25828  });
25829  </code></pre>
25830      * @param {Mixed} constrainTo The element to constrain to.
25831      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
25832      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
25833      * an object containing the sides to pad. For example: {right:10, bottom:10}
25834      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
25835      */
25836     constrainTo : function(constrainTo, pad, inContent){
25837         if(typeof pad == "number"){
25838             pad = {left: pad, right:pad, top:pad, bottom:pad};
25839         }
25840         pad = pad || this.defaultPadding;
25841         var b = Ext.get(this.getEl()).getBox();
25842         var ce = Ext.get(constrainTo);
25843         var s = ce.getScroll();
25844         var c, cd = ce.dom;
25845         if(cd == document.body){
25846             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
25847         }else{
25848             var xy = ce.getXY();
25849             c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
25850         }
25851
25852
25853         var topSpace = b.y - c.y;
25854         var leftSpace = b.x - c.x;
25855
25856         this.resetConstraints();
25857         this.setXConstraint(leftSpace - (pad.left||0), // left
25858                 c.width - leftSpace - b.width - (pad.right||0), //right
25859                                 this.xTickSize
25860         );
25861         this.setYConstraint(topSpace - (pad.top||0), //top
25862                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
25863                                 this.yTickSize
25864         );
25865     },
25866
25867     /**
25868      * Returns a reference to the linked element
25869      * @method getEl
25870      * @return {HTMLElement} the html element
25871      */
25872     getEl: function() {
25873         if (!this._domRef) {
25874             this._domRef = Ext.getDom(this.id);
25875         }
25876
25877         return this._domRef;
25878     },
25879
25880     /**
25881      * Returns a reference to the actual element to drag.  By default this is
25882      * the same as the html element, but it can be assigned to another
25883      * element. An example of this can be found in Ext.dd.DDProxy
25884      * @method getDragEl
25885      * @return {HTMLElement} the html element
25886      */
25887     getDragEl: function() {
25888         return Ext.getDom(this.dragElId);
25889     },
25890
25891     /**
25892      * Sets up the DragDrop object.  Must be called in the constructor of any
25893      * Ext.dd.DragDrop subclass
25894      * @method init
25895      * @param id the id of the linked element
25896      * @param {String} sGroup the group of related items
25897      * @param {object} config configuration attributes
25898      */
25899     init: function(id, sGroup, config) {
25900         this.initTarget(id, sGroup, config);
25901         Event.on(this.id, "mousedown", this.handleMouseDown, this);
25902         // Event.on(this.id, "selectstart", Event.preventDefault);
25903     },
25904
25905     /**
25906      * Initializes Targeting functionality only... the object does not
25907      * get a mousedown handler.
25908      * @method initTarget
25909      * @param id the id of the linked element
25910      * @param {String} sGroup the group of related items
25911      * @param {object} config configuration attributes
25912      */
25913     initTarget: function(id, sGroup, config) {
25914
25915         // configuration attributes
25916         this.config = config || {};
25917
25918         // create a local reference to the drag and drop manager
25919         this.DDM = Ext.dd.DDM;
25920         // initialize the groups array
25921         this.groups = {};
25922
25923         // assume that we have an element reference instead of an id if the
25924         // parameter is not a string
25925         if (typeof id !== "string") {
25926             id = Ext.id(id);
25927         }
25928
25929         // set the id
25930         this.id = id;
25931
25932         // add to an interaction group
25933         this.addToGroup((sGroup) ? sGroup : "default");
25934
25935         // We don't want to register this as the handle with the manager
25936         // so we just set the id rather than calling the setter.
25937         this.handleElId = id;
25938
25939         // the linked element is the element that gets dragged by default
25940         this.setDragElId(id);
25941
25942         // by default, clicked anchors will not start drag operations.
25943         this.invalidHandleTypes = { A: "A" };
25944         this.invalidHandleIds = {};
25945         this.invalidHandleClasses = [];
25946
25947         this.applyConfig();
25948
25949         this.handleOnAvailable();
25950     },
25951
25952     /**
25953      * Applies the configuration parameters that were passed into the constructor.
25954      * This is supposed to happen at each level through the inheritance chain.  So
25955      * a DDProxy implentation will execute apply config on DDProxy, DD, and
25956      * DragDrop in order to get all of the parameters that are available in
25957      * each object.
25958      * @method applyConfig
25959      */
25960     applyConfig: function() {
25961
25962         // configurable properties:
25963         //    padding, isTarget, maintainOffset, primaryButtonOnly
25964         this.padding           = this.config.padding || [0, 0, 0, 0];
25965         this.isTarget          = (this.config.isTarget !== false);
25966         this.maintainOffset    = (this.config.maintainOffset);
25967         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
25968
25969     },
25970
25971     /**
25972      * Executed when the linked element is available
25973      * @method handleOnAvailable
25974      * @private
25975      */
25976     handleOnAvailable: function() {
25977         this.available = true;
25978         this.resetConstraints();
25979         this.onAvailable();
25980     },
25981
25982      /**
25983      * Configures the padding for the target zone in px.  Effectively expands
25984      * (or reduces) the virtual object size for targeting calculations.
25985      * Supports css-style shorthand; if only one parameter is passed, all sides
25986      * will have that padding, and if only two are passed, the top and bottom
25987      * will have the first param, the left and right the second.
25988      * @method setPadding
25989      * @param {int} iTop    Top pad
25990      * @param {int} iRight  Right pad
25991      * @param {int} iBot    Bot pad
25992      * @param {int} iLeft   Left pad
25993      */
25994     setPadding: function(iTop, iRight, iBot, iLeft) {
25995         // this.padding = [iLeft, iRight, iTop, iBot];
25996         if (!iRight && 0 !== iRight) {
25997             this.padding = [iTop, iTop, iTop, iTop];
25998         } else if (!iBot && 0 !== iBot) {
25999             this.padding = [iTop, iRight, iTop, iRight];
26000         } else {
26001             this.padding = [iTop, iRight, iBot, iLeft];
26002         }
26003     },
26004
26005     /**
26006      * Stores the initial placement of the linked element.
26007      * @method setInitPosition
26008      * @param {int} diffX   the X offset, default 0
26009      * @param {int} diffY   the Y offset, default 0
26010      */
26011     setInitPosition: function(diffX, diffY) {
26012         var el = this.getEl();
26013
26014         if (!this.DDM.verifyEl(el)) {
26015             return;
26016         }
26017
26018         var dx = diffX || 0;
26019         var dy = diffY || 0;
26020
26021         var p = Dom.getXY( el );
26022
26023         this.initPageX = p[0] - dx;
26024         this.initPageY = p[1] - dy;
26025
26026         this.lastPageX = p[0];
26027         this.lastPageY = p[1];
26028
26029
26030         this.setStartPosition(p);
26031     },
26032
26033     /**
26034      * Sets the start position of the element.  This is set when the obj
26035      * is initialized, the reset when a drag is started.
26036      * @method setStartPosition
26037      * @param pos current position (from previous lookup)
26038      * @private
26039      */
26040     setStartPosition: function(pos) {
26041         var p = pos || Dom.getXY( this.getEl() );
26042         this.deltaSetXY = null;
26043
26044         this.startPageX = p[0];
26045         this.startPageY = p[1];
26046     },
26047
26048     /**
26049      * Add this instance to a group of related drag/drop objects.  All
26050      * instances belong to at least one group, and can belong to as many
26051      * groups as needed.
26052      * @method addToGroup
26053      * @param sGroup {string} the name of the group
26054      */
26055     addToGroup: function(sGroup) {
26056         this.groups[sGroup] = true;
26057         this.DDM.regDragDrop(this, sGroup);
26058     },
26059
26060     /**
26061      * Remove's this instance from the supplied interaction group
26062      * @method removeFromGroup
26063      * @param {string}  sGroup  The group to drop
26064      */
26065     removeFromGroup: function(sGroup) {
26066         if (this.groups[sGroup]) {
26067             delete this.groups[sGroup];
26068         }
26069
26070         this.DDM.removeDDFromGroup(this, sGroup);
26071     },
26072
26073     /**
26074      * Allows you to specify that an element other than the linked element
26075      * will be moved with the cursor during a drag
26076      * @method setDragElId
26077      * @param id {string} the id of the element that will be used to initiate the drag
26078      */
26079     setDragElId: function(id) {
26080         this.dragElId = id;
26081     },
26082
26083     /**
26084      * Allows you to specify a child of the linked element that should be
26085      * used to initiate the drag operation.  An example of this would be if
26086      * you have a content div with text and links.  Clicking anywhere in the
26087      * content area would normally start the drag operation.  Use this method
26088      * to specify that an element inside of the content div is the element
26089      * that starts the drag operation.
26090      * @method setHandleElId
26091      * @param id {string} the id of the element that will be used to
26092      * initiate the drag.
26093      */
26094     setHandleElId: function(id) {
26095         if (typeof id !== "string") {
26096             id = Ext.id(id);
26097         }
26098         this.handleElId = id;
26099         this.DDM.regHandle(this.id, id);
26100     },
26101
26102     /**
26103      * Allows you to set an element outside of the linked element as a drag
26104      * handle
26105      * @method setOuterHandleElId
26106      * @param id the id of the element that will be used to initiate the drag
26107      */
26108     setOuterHandleElId: function(id) {
26109         if (typeof id !== "string") {
26110             id = Ext.id(id);
26111         }
26112         Event.on(id, "mousedown",
26113                 this.handleMouseDown, this);
26114         this.setHandleElId(id);
26115
26116         this.hasOuterHandles = true;
26117     },
26118
26119     /**
26120      * Remove all drag and drop hooks for this element
26121      * @method unreg
26122      */
26123     unreg: function() {
26124         Event.un(this.id, "mousedown",
26125                 this.handleMouseDown);
26126         this._domRef = null;
26127         this.DDM._remove(this);
26128     },
26129
26130     destroy : function(){
26131         this.unreg();
26132     },
26133
26134     /**
26135      * Returns true if this instance is locked, or the drag drop mgr is locked
26136      * (meaning that all drag/drop is disabled on the page.)
26137      * @method isLocked
26138      * @return {boolean} true if this obj or all drag/drop is locked, else
26139      * false
26140      */
26141     isLocked: function() {
26142         return (this.DDM.isLocked() || this.locked);
26143     },
26144
26145     /**
26146      * Fired when this object is clicked
26147      * @method handleMouseDown
26148      * @param {Event} e
26149      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
26150      * @private
26151      */
26152     handleMouseDown: function(e, oDD){
26153         if (this.primaryButtonOnly && e.button != 0) {
26154             return;
26155         }
26156
26157         if (this.isLocked()) {
26158             return;
26159         }
26160
26161         this.DDM.refreshCache(this.groups);
26162
26163         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
26164         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
26165         } else {
26166             if (this.clickValidator(e)) {
26167
26168                 // set the initial element position
26169                 this.setStartPosition();
26170
26171
26172                 this.b4MouseDown(e);
26173                 this.onMouseDown(e);
26174
26175                 this.DDM.handleMouseDown(e, this);
26176
26177                 this.DDM.stopEvent(e);
26178             } else {
26179
26180
26181             }
26182         }
26183     },
26184
26185     clickValidator: function(e) {
26186         var target = e.getTarget();
26187         return ( this.isValidHandleChild(target) &&
26188                     (this.id == this.handleElId ||
26189                         this.DDM.handleWasClicked(target, this.id)) );
26190     },
26191
26192     /**
26193      * Allows you to specify a tag name that should not start a drag operation
26194      * when clicked.  This is designed to facilitate embedding links within a
26195      * drag handle that do something other than start the drag.
26196      * @method addInvalidHandleType
26197      * @param {string} tagName the type of element to exclude
26198      */
26199     addInvalidHandleType: function(tagName) {
26200         var type = tagName.toUpperCase();
26201         this.invalidHandleTypes[type] = type;
26202     },
26203
26204     /**
26205      * Lets you to specify an element id for a child of a drag handle
26206      * that should not initiate a drag
26207      * @method addInvalidHandleId
26208      * @param {string} id the element id of the element you wish to ignore
26209      */
26210     addInvalidHandleId: function(id) {
26211         if (typeof id !== "string") {
26212             id = Ext.id(id);
26213         }
26214         this.invalidHandleIds[id] = id;
26215     },
26216
26217     /**
26218      * Lets you specify a css class of elements that will not initiate a drag
26219      * @method addInvalidHandleClass
26220      * @param {string} cssClass the class of the elements you wish to ignore
26221      */
26222     addInvalidHandleClass: function(cssClass) {
26223         this.invalidHandleClasses.push(cssClass);
26224     },
26225
26226     /**
26227      * Unsets an excluded tag name set by addInvalidHandleType
26228      * @method removeInvalidHandleType
26229      * @param {string} tagName the type of element to unexclude
26230      */
26231     removeInvalidHandleType: function(tagName) {
26232         var type = tagName.toUpperCase();
26233         // this.invalidHandleTypes[type] = null;
26234         delete this.invalidHandleTypes[type];
26235     },
26236
26237     /**
26238      * Unsets an invalid handle id
26239      * @method removeInvalidHandleId
26240      * @param {string} id the id of the element to re-enable
26241      */
26242     removeInvalidHandleId: function(id) {
26243         if (typeof id !== "string") {
26244             id = Ext.id(id);
26245         }
26246         delete this.invalidHandleIds[id];
26247     },
26248
26249     /**
26250      * Unsets an invalid css class
26251      * @method removeInvalidHandleClass
26252      * @param {string} cssClass the class of the element(s) you wish to
26253      * re-enable
26254      */
26255     removeInvalidHandleClass: function(cssClass) {
26256         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
26257             if (this.invalidHandleClasses[i] == cssClass) {
26258                 delete this.invalidHandleClasses[i];
26259             }
26260         }
26261     },
26262
26263     /**
26264      * Checks the tag exclusion list to see if this click should be ignored
26265      * @method isValidHandleChild
26266      * @param {HTMLElement} node the HTMLElement to evaluate
26267      * @return {boolean} true if this is a valid tag type, false if not
26268      */
26269     isValidHandleChild: function(node) {
26270
26271         var valid = true;
26272         // var n = (node.nodeName == "#text") ? node.parentNode : node;
26273         var nodeName;
26274         try {
26275             nodeName = node.nodeName.toUpperCase();
26276         } catch(e) {
26277             nodeName = node.nodeName;
26278         }
26279         valid = valid && !this.invalidHandleTypes[nodeName];
26280         valid = valid && !this.invalidHandleIds[node.id];
26281
26282         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
26283             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
26284         }
26285
26286
26287         return valid;
26288
26289     },
26290
26291     /**
26292      * Create the array of horizontal tick marks if an interval was specified
26293      * in setXConstraint().
26294      * @method setXTicks
26295      * @private
26296      */
26297     setXTicks: function(iStartX, iTickSize) {
26298         this.xTicks = [];
26299         this.xTickSize = iTickSize;
26300
26301         var tickMap = {};
26302
26303         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
26304             if (!tickMap[i]) {
26305                 this.xTicks[this.xTicks.length] = i;
26306                 tickMap[i] = true;
26307             }
26308         }
26309
26310         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
26311             if (!tickMap[i]) {
26312                 this.xTicks[this.xTicks.length] = i;
26313                 tickMap[i] = true;
26314             }
26315         }
26316
26317         this.xTicks.sort(this.DDM.numericSort) ;
26318     },
26319
26320     /**
26321      * Create the array of vertical tick marks if an interval was specified in
26322      * setYConstraint().
26323      * @method setYTicks
26324      * @private
26325      */
26326     setYTicks: function(iStartY, iTickSize) {
26327         this.yTicks = [];
26328         this.yTickSize = iTickSize;
26329
26330         var tickMap = {};
26331
26332         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
26333             if (!tickMap[i]) {
26334                 this.yTicks[this.yTicks.length] = i;
26335                 tickMap[i] = true;
26336             }
26337         }
26338
26339         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
26340             if (!tickMap[i]) {
26341                 this.yTicks[this.yTicks.length] = i;
26342                 tickMap[i] = true;
26343             }
26344         }
26345
26346         this.yTicks.sort(this.DDM.numericSort) ;
26347     },
26348
26349     /**
26350      * By default, the element can be dragged any place on the screen.  Use
26351      * this method to limit the horizontal travel of the element.  Pass in
26352      * 0,0 for the parameters if you want to lock the drag to the y axis.
26353      * @method setXConstraint
26354      * @param {int} iLeft the number of pixels the element can move to the left
26355      * @param {int} iRight the number of pixels the element can move to the
26356      * right
26357      * @param {int} iTickSize optional parameter for specifying that the
26358      * element
26359      * should move iTickSize pixels at a time.
26360      */
26361     setXConstraint: function(iLeft, iRight, iTickSize) {
26362         this.leftConstraint = iLeft;
26363         this.rightConstraint = iRight;
26364
26365         this.minX = this.initPageX - iLeft;
26366         this.maxX = this.initPageX + iRight;
26367         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
26368
26369         this.constrainX = true;
26370     },
26371
26372     /**
26373      * Clears any constraints applied to this instance.  Also clears ticks
26374      * since they can't exist independent of a constraint at this time.
26375      * @method clearConstraints
26376      */
26377     clearConstraints: function() {
26378         this.constrainX = false;
26379         this.constrainY = false;
26380         this.clearTicks();
26381     },
26382
26383     /**
26384      * Clears any tick interval defined for this instance
26385      * @method clearTicks
26386      */
26387     clearTicks: function() {
26388         this.xTicks = null;
26389         this.yTicks = null;
26390         this.xTickSize = 0;
26391         this.yTickSize = 0;
26392     },
26393
26394     /**
26395      * By default, the element can be dragged any place on the screen.  Set
26396      * this to limit the vertical travel of the element.  Pass in 0,0 for the
26397      * parameters if you want to lock the drag to the x axis.
26398      * @method setYConstraint
26399      * @param {int} iUp the number of pixels the element can move up
26400      * @param {int} iDown the number of pixels the element can move down
26401      * @param {int} iTickSize optional parameter for specifying that the
26402      * element should move iTickSize pixels at a time.
26403      */
26404     setYConstraint: function(iUp, iDown, iTickSize) {
26405         this.topConstraint = iUp;
26406         this.bottomConstraint = iDown;
26407
26408         this.minY = this.initPageY - iUp;
26409         this.maxY = this.initPageY + iDown;
26410         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
26411
26412         this.constrainY = true;
26413
26414     },
26415
26416     /**
26417      * resetConstraints must be called if you manually reposition a dd element.
26418      * @method resetConstraints
26419      * @param {boolean} maintainOffset
26420      */
26421     resetConstraints: function() {
26422
26423
26424         // Maintain offsets if necessary
26425         if (this.initPageX || this.initPageX === 0) {
26426             // figure out how much this thing has moved
26427             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
26428             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
26429
26430             this.setInitPosition(dx, dy);
26431
26432         // This is the first time we have detected the element's position
26433         } else {
26434             this.setInitPosition();
26435         }
26436
26437         if (this.constrainX) {
26438             this.setXConstraint( this.leftConstraint,
26439                                  this.rightConstraint,
26440                                  this.xTickSize        );
26441         }
26442
26443         if (this.constrainY) {
26444             this.setYConstraint( this.topConstraint,
26445                                  this.bottomConstraint,
26446                                  this.yTickSize         );
26447         }
26448     },
26449
26450     /**
26451      * Normally the drag element is moved pixel by pixel, but we can specify
26452      * that it move a number of pixels at a time.  This method resolves the
26453      * location when we have it set up like this.
26454      * @method getTick
26455      * @param {int} val where we want to place the object
26456      * @param {int[]} tickArray sorted array of valid points
26457      * @return {int} the closest tick
26458      * @private
26459      */
26460     getTick: function(val, tickArray) {
26461
26462         if (!tickArray) {
26463             // If tick interval is not defined, it is effectively 1 pixel,
26464             // so we return the value passed to us.
26465             return val;
26466         } else if (tickArray[0] >= val) {
26467             // The value is lower than the first tick, so we return the first
26468             // tick.
26469             return tickArray[0];
26470         } else {
26471             for (var i=0, len=tickArray.length; i<len; ++i) {
26472                 var next = i + 1;
26473                 if (tickArray[next] && tickArray[next] >= val) {
26474                     var diff1 = val - tickArray[i];
26475                     var diff2 = tickArray[next] - val;
26476                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
26477                 }
26478             }
26479
26480             // The value is larger than the last tick, so we return the last
26481             // tick.
26482             return tickArray[tickArray.length - 1];
26483         }
26484     },
26485
26486     /**
26487      * toString method
26488      * @method toString
26489      * @return {string} string representation of the dd obj
26490      */
26491     toString: function() {
26492         return ("DragDrop " + this.id);
26493     }
26494
26495 };
26496
26497 })();
26498 /**
26499  * The drag and drop utility provides a framework for building drag and drop
26500  * applications.  In addition to enabling drag and drop for specific elements,
26501  * the drag and drop elements are tracked by the manager class, and the
26502  * interactions between the various elements are tracked during the drag and
26503  * the implementing code is notified about these important moments.
26504  */
26505
26506 // Only load the library once.  Rewriting the manager class would orphan
26507 // existing drag and drop instances.
26508 if (!Ext.dd.DragDropMgr) {
26509
26510 /**
26511  * @class Ext.dd.DragDropMgr
26512  * DragDropMgr is a singleton that tracks the element interaction for
26513  * all DragDrop items in the window.  Generally, you will not call
26514  * this class directly, but it does have helper methods that could
26515  * be useful in your DragDrop implementations.
26516  * @singleton
26517  */
26518 Ext.dd.DragDropMgr = function() {
26519
26520     var Event = Ext.EventManager;
26521
26522     return {
26523
26524         /**
26525          * Two dimensional Array of registered DragDrop objects.  The first
26526          * dimension is the DragDrop item group, the second the DragDrop
26527          * object.
26528          * @property ids
26529          * @type {string: string}
26530          * @private
26531          * @static
26532          */
26533         ids: {},
26534
26535         /**
26536          * Array of element ids defined as drag handles.  Used to determine
26537          * if the element that generated the mousedown event is actually the
26538          * handle and not the html element itself.
26539          * @property handleIds
26540          * @type {string: string}
26541          * @private
26542          * @static
26543          */
26544         handleIds: {},
26545
26546         /**
26547          * the DragDrop object that is currently being dragged
26548          * @property dragCurrent
26549          * @type DragDrop
26550          * @private
26551          * @static
26552          **/
26553         dragCurrent: null,
26554
26555         /**
26556          * the DragDrop object(s) that are being hovered over
26557          * @property dragOvers
26558          * @type Array
26559          * @private
26560          * @static
26561          */
26562         dragOvers: {},
26563
26564         /**
26565          * the X distance between the cursor and the object being dragged
26566          * @property deltaX
26567          * @type int
26568          * @private
26569          * @static
26570          */
26571         deltaX: 0,
26572
26573         /**
26574          * the Y distance between the cursor and the object being dragged
26575          * @property deltaY
26576          * @type int
26577          * @private
26578          * @static
26579          */
26580         deltaY: 0,
26581
26582         /**
26583          * Flag to determine if we should prevent the default behavior of the
26584          * events we define. By default this is true, but this can be set to
26585          * false if you need the default behavior (not recommended)
26586          * @property preventDefault
26587          * @type boolean
26588          * @static
26589          */
26590         preventDefault: true,
26591
26592         /**
26593          * Flag to determine if we should stop the propagation of the events
26594          * we generate. This is true by default but you may want to set it to
26595          * false if the html element contains other features that require the
26596          * mouse click.
26597          * @property stopPropagation
26598          * @type boolean
26599          * @static
26600          */
26601         stopPropagation: true,
26602
26603         /**
26604          * Internal flag that is set to true when drag and drop has been
26605          * intialized
26606          * @property initialized
26607          * @private
26608          * @static
26609          */
26610         initialized: false,
26611
26612         /**
26613          * All drag and drop can be disabled.
26614          * @property locked
26615          * @private
26616          * @static
26617          */
26618         locked: false,
26619
26620         /**
26621          * Called the first time an element is registered.
26622          * @method init
26623          * @private
26624          * @static
26625          */
26626         init: function() {
26627             this.initialized = true;
26628         },
26629
26630         /**
26631          * In point mode, drag and drop interaction is defined by the
26632          * location of the cursor during the drag/drop
26633          * @property POINT
26634          * @type int
26635          * @static
26636          */
26637         POINT: 0,
26638
26639         /**
26640          * In intersect mode, drag and drop interaction is defined by the
26641          * overlap of two or more drag and drop objects.
26642          * @property INTERSECT
26643          * @type int
26644          * @static
26645          */
26646         INTERSECT: 1,
26647
26648         /**
26649          * The current drag and drop mode.  Default: POINT
26650          * @property mode
26651          * @type int
26652          * @static
26653          */
26654         mode: 0,
26655
26656         /**
26657          * Runs method on all drag and drop objects
26658          * @method _execOnAll
26659          * @private
26660          * @static
26661          */
26662         _execOnAll: function(sMethod, args) {
26663             for (var i in this.ids) {
26664                 for (var j in this.ids[i]) {
26665                     var oDD = this.ids[i][j];
26666                     if (! this.isTypeOfDD(oDD)) {
26667                         continue;
26668                     }
26669                     oDD[sMethod].apply(oDD, args);
26670                 }
26671             }
26672         },
26673
26674         /**
26675          * Drag and drop initialization.  Sets up the global event handlers
26676          * @method _onLoad
26677          * @private
26678          * @static
26679          */
26680         _onLoad: function() {
26681
26682             this.init();
26683
26684
26685             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
26686             Event.on(document, "mousemove", this.handleMouseMove, this, true);
26687             Event.on(window,   "unload",    this._onUnload, this, true);
26688             Event.on(window,   "resize",    this._onResize, this, true);
26689             // Event.on(window,   "mouseout",    this._test);
26690
26691         },
26692
26693         /**
26694          * Reset constraints on all drag and drop objs
26695          * @method _onResize
26696          * @private
26697          * @static
26698          */
26699         _onResize: function(e) {
26700             this._execOnAll("resetConstraints", []);
26701         },
26702
26703         /**
26704          * Lock all drag and drop functionality
26705          * @method lock
26706          * @static
26707          */
26708         lock: function() { this.locked = true; },
26709
26710         /**
26711          * Unlock all drag and drop functionality
26712          * @method unlock
26713          * @static
26714          */
26715         unlock: function() { this.locked = false; },
26716
26717         /**
26718          * Is drag and drop locked?
26719          * @method isLocked
26720          * @return {boolean} True if drag and drop is locked, false otherwise.
26721          * @static
26722          */
26723         isLocked: function() { return this.locked; },
26724
26725         /**
26726          * Location cache that is set for all drag drop objects when a drag is
26727          * initiated, cleared when the drag is finished.
26728          * @property locationCache
26729          * @private
26730          * @static
26731          */
26732         locationCache: {},
26733
26734         /**
26735          * Set useCache to false if you want to force object the lookup of each
26736          * drag and drop linked element constantly during a drag.
26737          * @property useCache
26738          * @type boolean
26739          * @static
26740          */
26741         useCache: true,
26742
26743         /**
26744          * The number of pixels that the mouse needs to move after the
26745          * mousedown before the drag is initiated.  Default=3;
26746          * @property clickPixelThresh
26747          * @type int
26748          * @static
26749          */
26750         clickPixelThresh: 3,
26751
26752         /**
26753          * The number of milliseconds after the mousedown event to initiate the
26754          * drag if we don't get a mouseup event. Default=1000
26755          * @property clickTimeThresh
26756          * @type int
26757          * @static
26758          */
26759         clickTimeThresh: 350,
26760
26761         /**
26762          * Flag that indicates that either the drag pixel threshold or the
26763          * mousdown time threshold has been met
26764          * @property dragThreshMet
26765          * @type boolean
26766          * @private
26767          * @static
26768          */
26769         dragThreshMet: false,
26770
26771         /**
26772          * Timeout used for the click time threshold
26773          * @property clickTimeout
26774          * @type Object
26775          * @private
26776          * @static
26777          */
26778         clickTimeout: null,
26779
26780         /**
26781          * The X position of the mousedown event stored for later use when a
26782          * drag threshold is met.
26783          * @property startX
26784          * @type int
26785          * @private
26786          * @static
26787          */
26788         startX: 0,
26789
26790         /**
26791          * The Y position of the mousedown event stored for later use when a
26792          * drag threshold is met.
26793          * @property startY
26794          * @type int
26795          * @private
26796          * @static
26797          */
26798         startY: 0,
26799
26800         /**
26801          * Each DragDrop instance must be registered with the DragDropMgr.
26802          * This is executed in DragDrop.init()
26803          * @method regDragDrop
26804          * @param {DragDrop} oDD the DragDrop object to register
26805          * @param {String} sGroup the name of the group this element belongs to
26806          * @static
26807          */
26808         regDragDrop: function(oDD, sGroup) {
26809             if (!this.initialized) { this.init(); }
26810
26811             if (!this.ids[sGroup]) {
26812                 this.ids[sGroup] = {};
26813             }
26814             this.ids[sGroup][oDD.id] = oDD;
26815         },
26816
26817         /**
26818          * Removes the supplied dd instance from the supplied group. Executed
26819          * by DragDrop.removeFromGroup, so don't call this function directly.
26820          * @method removeDDFromGroup
26821          * @private
26822          * @static
26823          */
26824         removeDDFromGroup: function(oDD, sGroup) {
26825             if (!this.ids[sGroup]) {
26826                 this.ids[sGroup] = {};
26827             }
26828
26829             var obj = this.ids[sGroup];
26830             if (obj && obj[oDD.id]) {
26831                 delete obj[oDD.id];
26832             }
26833         },
26834
26835         /**
26836          * Unregisters a drag and drop item.  This is executed in
26837          * DragDrop.unreg, use that method instead of calling this directly.
26838          * @method _remove
26839          * @private
26840          * @static
26841          */
26842         _remove: function(oDD) {
26843             for (var g in oDD.groups) {
26844                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
26845                     delete this.ids[g][oDD.id];
26846                 }
26847             }
26848             delete this.handleIds[oDD.id];
26849         },
26850
26851         /**
26852          * Each DragDrop handle element must be registered.  This is done
26853          * automatically when executing DragDrop.setHandleElId()
26854          * @method regHandle
26855          * @param {String} sDDId the DragDrop id this element is a handle for
26856          * @param {String} sHandleId the id of the element that is the drag
26857          * handle
26858          * @static
26859          */
26860         regHandle: function(sDDId, sHandleId) {
26861             if (!this.handleIds[sDDId]) {
26862                 this.handleIds[sDDId] = {};
26863             }
26864             this.handleIds[sDDId][sHandleId] = sHandleId;
26865         },
26866
26867         /**
26868          * Utility function to determine if a given element has been
26869          * registered as a drag drop item.
26870          * @method isDragDrop
26871          * @param {String} id the element id to check
26872          * @return {boolean} true if this element is a DragDrop item,
26873          * false otherwise
26874          * @static
26875          */
26876         isDragDrop: function(id) {
26877             return ( this.getDDById(id) ) ? true : false;
26878         },
26879
26880         /**
26881          * Returns the drag and drop instances that are in all groups the
26882          * passed in instance belongs to.
26883          * @method getRelated
26884          * @param {DragDrop} p_oDD the obj to get related data for
26885          * @param {boolean} bTargetsOnly if true, only return targetable objs
26886          * @return {DragDrop[]} the related instances
26887          * @static
26888          */
26889         getRelated: function(p_oDD, bTargetsOnly) {
26890             var oDDs = [];
26891             for (var i in p_oDD.groups) {
26892                 for (var j in this.ids[i]) {
26893                     var dd = this.ids[i][j];
26894                     if (! this.isTypeOfDD(dd)) {
26895                         continue;
26896                     }
26897                     if (!bTargetsOnly || dd.isTarget) {
26898                         oDDs[oDDs.length] = dd;
26899                     }
26900                 }
26901             }
26902
26903             return oDDs;
26904         },
26905
26906         /**
26907          * Returns true if the specified dd target is a legal target for
26908          * the specifice drag obj
26909          * @method isLegalTarget
26910          * @param {DragDrop} the drag obj
26911          * @param {DragDrop} the target
26912          * @return {boolean} true if the target is a legal target for the
26913          * dd obj
26914          * @static
26915          */
26916         isLegalTarget: function (oDD, oTargetDD) {
26917             var targets = this.getRelated(oDD, true);
26918             for (var i=0, len=targets.length;i<len;++i) {
26919                 if (targets[i].id == oTargetDD.id) {
26920                     return true;
26921                 }
26922             }
26923
26924             return false;
26925         },
26926
26927         /**
26928          * My goal is to be able to transparently determine if an object is
26929          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
26930          * returns "object", oDD.constructor.toString() always returns
26931          * "DragDrop" and not the name of the subclass.  So for now it just
26932          * evaluates a well-known variable in DragDrop.
26933          * @method isTypeOfDD
26934          * @param {Object} the object to evaluate
26935          * @return {boolean} true if typeof oDD = DragDrop
26936          * @static
26937          */
26938         isTypeOfDD: function (oDD) {
26939             return (oDD && oDD.__ygDragDrop);
26940         },
26941
26942         /**
26943          * Utility function to determine if a given element has been
26944          * registered as a drag drop handle for the given Drag Drop object.
26945          * @method isHandle
26946          * @param {String} id the element id to check
26947          * @return {boolean} true if this element is a DragDrop handle, false
26948          * otherwise
26949          * @static
26950          */
26951         isHandle: function(sDDId, sHandleId) {
26952             return ( this.handleIds[sDDId] &&
26953                             this.handleIds[sDDId][sHandleId] );
26954         },
26955
26956         /**
26957          * Returns the DragDrop instance for a given id
26958          * @method getDDById
26959          * @param {String} id the id of the DragDrop object
26960          * @return {DragDrop} the drag drop object, null if it is not found
26961          * @static
26962          */
26963         getDDById: function(id) {
26964             for (var i in this.ids) {
26965                 if (this.ids[i][id]) {
26966                     return this.ids[i][id];
26967                 }
26968             }
26969             return null;
26970         },
26971
26972         /**
26973          * Fired after a registered DragDrop object gets the mousedown event.
26974          * Sets up the events required to track the object being dragged
26975          * @method handleMouseDown
26976          * @param {Event} e the event
26977          * @param oDD the DragDrop object being dragged
26978          * @private
26979          * @static
26980          */
26981         handleMouseDown: function(e, oDD) {
26982             if(Ext.QuickTips){
26983                 Ext.QuickTips.disable();
26984             }
26985             if(this.dragCurrent){
26986                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
26987                 // so clean up first to avoid breaking the next drag
26988                 this.handleMouseUp(e);
26989             }
26990             
26991             this.currentTarget = e.getTarget();
26992             this.dragCurrent = oDD;
26993
26994             var el = oDD.getEl();
26995
26996             // track start position
26997             this.startX = e.getPageX();
26998             this.startY = e.getPageY();
26999
27000             this.deltaX = this.startX - el.offsetLeft;
27001             this.deltaY = this.startY - el.offsetTop;
27002
27003             this.dragThreshMet = false;
27004
27005             this.clickTimeout = setTimeout(
27006                     function() {
27007                         var DDM = Ext.dd.DDM;
27008                         DDM.startDrag(DDM.startX, DDM.startY);
27009                     },
27010                     this.clickTimeThresh );
27011         },
27012
27013         /**
27014          * Fired when either the drag pixel threshol or the mousedown hold
27015          * time threshold has been met.
27016          * @method startDrag
27017          * @param x {int} the X position of the original mousedown
27018          * @param y {int} the Y position of the original mousedown
27019          * @static
27020          */
27021         startDrag: function(x, y) {
27022             clearTimeout(this.clickTimeout);
27023             if (this.dragCurrent) {
27024                 this.dragCurrent.b4StartDrag(x, y);
27025                 this.dragCurrent.startDrag(x, y);
27026             }
27027             this.dragThreshMet = true;
27028         },
27029
27030         /**
27031          * Internal function to handle the mouseup event.  Will be invoked
27032          * from the context of the document.
27033          * @method handleMouseUp
27034          * @param {Event} e the event
27035          * @private
27036          * @static
27037          */
27038         handleMouseUp: function(e) {
27039
27040             if(Ext.QuickTips){
27041                 Ext.QuickTips.enable();
27042             }
27043             if (! this.dragCurrent) {
27044                 return;
27045             }
27046
27047             clearTimeout(this.clickTimeout);
27048
27049             if (this.dragThreshMet) {
27050                 this.fireEvents(e, true);
27051             } else {
27052             }
27053
27054             this.stopDrag(e);
27055
27056             this.stopEvent(e);
27057         },
27058
27059         /**
27060          * Utility to stop event propagation and event default, if these
27061          * features are turned on.
27062          * @method stopEvent
27063          * @param {Event} e the event as returned by this.getEvent()
27064          * @static
27065          */
27066         stopEvent: function(e){
27067             if(this.stopPropagation) {
27068                 e.stopPropagation();
27069             }
27070
27071             if (this.preventDefault) {
27072                 e.preventDefault();
27073             }
27074         },
27075
27076         /**
27077          * Internal function to clean up event handlers after the drag
27078          * operation is complete
27079          * @method stopDrag
27080          * @param {Event} e the event
27081          * @private
27082          * @static
27083          */
27084         stopDrag: function(e) {
27085             // Fire the drag end event for the item that was dragged
27086             if (this.dragCurrent) {
27087                 if (this.dragThreshMet) {
27088                     this.dragCurrent.b4EndDrag(e);
27089                     this.dragCurrent.endDrag(e);
27090                 }
27091
27092                 this.dragCurrent.onMouseUp(e);
27093             }
27094
27095             this.dragCurrent = null;
27096             this.dragOvers = {};
27097         },
27098
27099         /**
27100          * Internal function to handle the mousemove event.  Will be invoked
27101          * from the context of the html element.
27102          *
27103          * @TODO figure out what we can do about mouse events lost when the
27104          * user drags objects beyond the window boundary.  Currently we can
27105          * detect this in internet explorer by verifying that the mouse is
27106          * down during the mousemove event.  Firefox doesn't give us the
27107          * button state on the mousemove event.
27108          * @method handleMouseMove
27109          * @param {Event} e the event
27110          * @private
27111          * @static
27112          */
27113         handleMouseMove: function(e) {
27114             if (! this.dragCurrent) {
27115                 return true;
27116             }
27117             // var button = e.which || e.button;
27118
27119             // check for IE mouseup outside of page boundary
27120             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
27121                 this.stopEvent(e);
27122                 return this.handleMouseUp(e);
27123             }
27124
27125             if (!this.dragThreshMet) {
27126                 var diffX = Math.abs(this.startX - e.getPageX());
27127                 var diffY = Math.abs(this.startY - e.getPageY());
27128                 if (diffX > this.clickPixelThresh ||
27129                             diffY > this.clickPixelThresh) {
27130                     this.startDrag(this.startX, this.startY);
27131                 }
27132             }
27133
27134             if (this.dragThreshMet) {
27135                 this.dragCurrent.b4Drag(e);
27136                 this.dragCurrent.onDrag(e);
27137                 if(!this.dragCurrent.moveOnly){
27138                     this.fireEvents(e, false);
27139                 }
27140             }
27141
27142             this.stopEvent(e);
27143
27144             return true;
27145         },
27146
27147         /**
27148          * Iterates over all of the DragDrop elements to find ones we are
27149          * hovering over or dropping on
27150          * @method fireEvents
27151          * @param {Event} e the event
27152          * @param {boolean} isDrop is this a drop op or a mouseover op?
27153          * @private
27154          * @static
27155          */
27156         fireEvents: function(e, isDrop) {
27157             var dc = this.dragCurrent;
27158
27159             // If the user did the mouse up outside of the window, we could
27160             // get here even though we have ended the drag.
27161             if (!dc || dc.isLocked()) {
27162                 return;
27163             }
27164
27165             var pt = e.getPoint();
27166
27167             // cache the previous dragOver array
27168             var oldOvers = [];
27169
27170             var outEvts   = [];
27171             var overEvts  = [];
27172             var dropEvts  = [];
27173             var enterEvts = [];
27174
27175             // Check to see if the object(s) we were hovering over is no longer
27176             // being hovered over so we can fire the onDragOut event
27177             for (var i in this.dragOvers) {
27178
27179                 var ddo = this.dragOvers[i];
27180
27181                 if (! this.isTypeOfDD(ddo)) {
27182                     continue;
27183                 }
27184
27185                 if (! this.isOverTarget(pt, ddo, this.mode)) {
27186                     outEvts.push( ddo );
27187                 }
27188
27189                 oldOvers[i] = true;
27190                 delete this.dragOvers[i];
27191             }
27192
27193             for (var sGroup in dc.groups) {
27194
27195                 if ("string" != typeof sGroup) {
27196                     continue;
27197                 }
27198
27199                 for (i in this.ids[sGroup]) {
27200                     var oDD = this.ids[sGroup][i];
27201                     if (! this.isTypeOfDD(oDD)) {
27202                         continue;
27203                     }
27204
27205                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
27206                         if (this.isOverTarget(pt, oDD, this.mode)) {
27207                             // look for drop interactions
27208                             if (isDrop) {
27209                                 dropEvts.push( oDD );
27210                             // look for drag enter and drag over interactions
27211                             } else {
27212
27213                                 // initial drag over: dragEnter fires
27214                                 if (!oldOvers[oDD.id]) {
27215                                     enterEvts.push( oDD );
27216                                 // subsequent drag overs: dragOver fires
27217                                 } else {
27218                                     overEvts.push( oDD );
27219                                 }
27220
27221                                 this.dragOvers[oDD.id] = oDD;
27222                             }
27223                         }
27224                     }
27225                 }
27226             }
27227
27228             if (this.mode) {
27229                 if (outEvts.length) {
27230                     dc.b4DragOut(e, outEvts);
27231                     dc.onDragOut(e, outEvts);
27232                 }
27233
27234                 if (enterEvts.length) {
27235                     dc.onDragEnter(e, enterEvts);
27236                 }
27237
27238                 if (overEvts.length) {
27239                     dc.b4DragOver(e, overEvts);
27240                     dc.onDragOver(e, overEvts);
27241                 }
27242
27243                 if (dropEvts.length) {
27244                     dc.b4DragDrop(e, dropEvts);
27245                     dc.onDragDrop(e, dropEvts);
27246                 }
27247
27248             } else {
27249                 // fire dragout events
27250                 var len = 0;
27251                 for (i=0, len=outEvts.length; i<len; ++i) {
27252                     dc.b4DragOut(e, outEvts[i].id);
27253                     dc.onDragOut(e, outEvts[i].id);
27254                 }
27255
27256                 // fire enter events
27257                 for (i=0,len=enterEvts.length; i<len; ++i) {
27258                     // dc.b4DragEnter(e, oDD.id);
27259                     dc.onDragEnter(e, enterEvts[i].id);
27260                 }
27261
27262                 // fire over events
27263                 for (i=0,len=overEvts.length; i<len; ++i) {
27264                     dc.b4DragOver(e, overEvts[i].id);
27265                     dc.onDragOver(e, overEvts[i].id);
27266                 }
27267
27268                 // fire drop events
27269                 for (i=0, len=dropEvts.length; i<len; ++i) {
27270                     dc.b4DragDrop(e, dropEvts[i].id);
27271                     dc.onDragDrop(e, dropEvts[i].id);
27272                 }
27273
27274             }
27275
27276             // notify about a drop that did not find a target
27277             if (isDrop && !dropEvts.length) {
27278                 dc.onInvalidDrop(e);
27279             }
27280
27281         },
27282
27283         /**
27284          * Helper function for getting the best match from the list of drag
27285          * and drop objects returned by the drag and drop events when we are
27286          * in INTERSECT mode.  It returns either the first object that the
27287          * cursor is over, or the object that has the greatest overlap with
27288          * the dragged element.
27289          * @method getBestMatch
27290          * @param  {DragDrop[]} dds The array of drag and drop objects
27291          * targeted
27292          * @return {DragDrop}       The best single match
27293          * @static
27294          */
27295         getBestMatch: function(dds) {
27296             var winner = null;
27297             // Return null if the input is not what we expect
27298             //if (!dds || !dds.length || dds.length == 0) {
27299                // winner = null;
27300             // If there is only one item, it wins
27301             //} else if (dds.length == 1) {
27302
27303             var len = dds.length;
27304
27305             if (len == 1) {
27306                 winner = dds[0];
27307             } else {
27308                 // Loop through the targeted items
27309                 for (var i=0; i<len; ++i) {
27310                     var dd = dds[i];
27311                     // If the cursor is over the object, it wins.  If the
27312                     // cursor is over multiple matches, the first one we come
27313                     // to wins.
27314                     if (dd.cursorIsOver) {
27315                         winner = dd;
27316                         break;
27317                     // Otherwise the object with the most overlap wins
27318                     } else {
27319                         if (!winner ||
27320                             winner.overlap.getArea() < dd.overlap.getArea()) {
27321                             winner = dd;
27322                         }
27323                     }
27324                 }
27325             }
27326
27327             return winner;
27328         },
27329
27330         /**
27331          * Refreshes the cache of the top-left and bottom-right points of the
27332          * drag and drop objects in the specified group(s).  This is in the
27333          * format that is stored in the drag and drop instance, so typical
27334          * usage is:
27335          * <code>
27336          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
27337          * </code>
27338          * Alternatively:
27339          * <code>
27340          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
27341          * </code>
27342          * @TODO this really should be an indexed array.  Alternatively this
27343          * method could accept both.
27344          * @method refreshCache
27345          * @param {Object} groups an associative array of groups to refresh
27346          * @static
27347          */
27348         refreshCache: function(groups) {
27349             for (var sGroup in groups) {
27350                 if ("string" != typeof sGroup) {
27351                     continue;
27352                 }
27353                 for (var i in this.ids[sGroup]) {
27354                     var oDD = this.ids[sGroup][i];
27355
27356                     if (this.isTypeOfDD(oDD)) {
27357                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
27358                         var loc = this.getLocation(oDD);
27359                         if (loc) {
27360                             this.locationCache[oDD.id] = loc;
27361                         } else {
27362                             delete this.locationCache[oDD.id];
27363                             // this will unregister the drag and drop object if
27364                             // the element is not in a usable state
27365                             // oDD.unreg();
27366                         }
27367                     }
27368                 }
27369             }
27370         },
27371
27372         /**
27373          * This checks to make sure an element exists and is in the DOM.  The
27374          * main purpose is to handle cases where innerHTML is used to remove
27375          * drag and drop objects from the DOM.  IE provides an 'unspecified
27376          * error' when trying to access the offsetParent of such an element
27377          * @method verifyEl
27378          * @param {HTMLElement} el the element to check
27379          * @return {boolean} true if the element looks usable
27380          * @static
27381          */
27382         verifyEl: function(el) {
27383             if (el) {
27384                 var parent;
27385                 if(Ext.isIE){
27386                     try{
27387                         parent = el.offsetParent;
27388                     }catch(e){}
27389                 }else{
27390                     parent = el.offsetParent;
27391                 }
27392                 if (parent) {
27393                     return true;
27394                 }
27395             }
27396
27397             return false;
27398         },
27399
27400         /**
27401          * Returns a Region object containing the drag and drop element's position
27402          * and size, including the padding configured for it
27403          * @method getLocation
27404          * @param {DragDrop} oDD the drag and drop object to get the
27405          *                       location for
27406          * @return {Ext.lib.Region} a Region object representing the total area
27407          *                             the element occupies, including any padding
27408          *                             the instance is configured for.
27409          * @static
27410          */
27411         getLocation: function(oDD) {
27412             if (! this.isTypeOfDD(oDD)) {
27413                 return null;
27414             }
27415
27416             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
27417
27418             try {
27419                 pos= Ext.lib.Dom.getXY(el);
27420             } catch (e) { }
27421
27422             if (!pos) {
27423                 return null;
27424             }
27425
27426             x1 = pos[0];
27427             x2 = x1 + el.offsetWidth;
27428             y1 = pos[1];
27429             y2 = y1 + el.offsetHeight;
27430
27431             t = y1 - oDD.padding[0];
27432             r = x2 + oDD.padding[1];
27433             b = y2 + oDD.padding[2];
27434             l = x1 - oDD.padding[3];
27435
27436             return new Ext.lib.Region( t, r, b, l );
27437         },
27438
27439         /**
27440          * Checks the cursor location to see if it over the target
27441          * @method isOverTarget
27442          * @param {Ext.lib.Point} pt The point to evaluate
27443          * @param {DragDrop} oTarget the DragDrop object we are inspecting
27444          * @return {boolean} true if the mouse is over the target
27445          * @private
27446          * @static
27447          */
27448         isOverTarget: function(pt, oTarget, intersect) {
27449             // use cache if available
27450             var loc = this.locationCache[oTarget.id];
27451             if (!loc || !this.useCache) {
27452                 loc = this.getLocation(oTarget);
27453                 this.locationCache[oTarget.id] = loc;
27454
27455             }
27456
27457             if (!loc) {
27458                 return false;
27459             }
27460
27461             oTarget.cursorIsOver = loc.contains( pt );
27462
27463             // DragDrop is using this as a sanity check for the initial mousedown
27464             // in this case we are done.  In POINT mode, if the drag obj has no
27465             // contraints, we are also done. Otherwise we need to evaluate the
27466             // location of the target as related to the actual location of the
27467             // dragged element.
27468             var dc = this.dragCurrent;
27469             if (!dc || !dc.getTargetCoord ||
27470                     (!intersect && !dc.constrainX && !dc.constrainY)) {
27471                 return oTarget.cursorIsOver;
27472             }
27473
27474             oTarget.overlap = null;
27475
27476             // Get the current location of the drag element, this is the
27477             // location of the mouse event less the delta that represents
27478             // where the original mousedown happened on the element.  We
27479             // need to consider constraints and ticks as well.
27480             var pos = dc.getTargetCoord(pt.x, pt.y);
27481
27482             var el = dc.getDragEl();
27483             var curRegion = new Ext.lib.Region( pos.y,
27484                                                    pos.x + el.offsetWidth,
27485                                                    pos.y + el.offsetHeight,
27486                                                    pos.x );
27487
27488             var overlap = curRegion.intersect(loc);
27489
27490             if (overlap) {
27491                 oTarget.overlap = overlap;
27492                 return (intersect) ? true : oTarget.cursorIsOver;
27493             } else {
27494                 return false;
27495             }
27496         },
27497
27498         /**
27499          * unload event handler
27500          * @method _onUnload
27501          * @private
27502          * @static
27503          */
27504         _onUnload: function(e, me) {
27505             Ext.dd.DragDropMgr.unregAll();
27506         },
27507
27508         /**
27509          * Cleans up the drag and drop events and objects.
27510          * @method unregAll
27511          * @private
27512          * @static
27513          */
27514         unregAll: function() {
27515
27516             if (this.dragCurrent) {
27517                 this.stopDrag();
27518                 this.dragCurrent = null;
27519             }
27520
27521             this._execOnAll("unreg", []);
27522
27523             for (var i in this.elementCache) {
27524                 delete this.elementCache[i];
27525             }
27526
27527             this.elementCache = {};
27528             this.ids = {};
27529         },
27530
27531         /**
27532          * A cache of DOM elements
27533          * @property elementCache
27534          * @private
27535          * @static
27536          */
27537         elementCache: {},
27538
27539         /**
27540          * Get the wrapper for the DOM element specified
27541          * @method getElWrapper
27542          * @param {String} id the id of the element to get
27543          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
27544          * @private
27545          * @deprecated This wrapper isn't that useful
27546          * @static
27547          */
27548         getElWrapper: function(id) {
27549             var oWrapper = this.elementCache[id];
27550             if (!oWrapper || !oWrapper.el) {
27551                 oWrapper = this.elementCache[id] =
27552                     new this.ElementWrapper(Ext.getDom(id));
27553             }
27554             return oWrapper;
27555         },
27556
27557         /**
27558          * Returns the actual DOM element
27559          * @method getElement
27560          * @param {String} id the id of the elment to get
27561          * @return {Object} The element
27562          * @deprecated use Ext.lib.Ext.getDom instead
27563          * @static
27564          */
27565         getElement: function(id) {
27566             return Ext.getDom(id);
27567         },
27568
27569         /**
27570          * Returns the style property for the DOM element (i.e.,
27571          * document.getElById(id).style)
27572          * @method getCss
27573          * @param {String} id the id of the elment to get
27574          * @return {Object} The style property of the element
27575          * @deprecated use Ext.lib.Dom instead
27576          * @static
27577          */
27578         getCss: function(id) {
27579             var el = Ext.getDom(id);
27580             return (el) ? el.style : null;
27581         },
27582
27583         /**
27584          * Inner class for cached elements
27585          * @class DragDropMgr.ElementWrapper
27586          * @for DragDropMgr
27587          * @private
27588          * @deprecated
27589          */
27590         ElementWrapper: function(el) {
27591                 /**
27592                  * The element
27593                  * @property el
27594                  */
27595                 this.el = el || null;
27596                 /**
27597                  * The element id
27598                  * @property id
27599                  */
27600                 this.id = this.el && el.id;
27601                 /**
27602                  * A reference to the style property
27603                  * @property css
27604                  */
27605                 this.css = this.el && el.style;
27606             },
27607
27608         /**
27609          * Returns the X position of an html element
27610          * @method getPosX
27611          * @param el the element for which to get the position
27612          * @return {int} the X coordinate
27613          * @for DragDropMgr
27614          * @deprecated use Ext.lib.Dom.getX instead
27615          * @static
27616          */
27617         getPosX: function(el) {
27618             return Ext.lib.Dom.getX(el);
27619         },
27620
27621         /**
27622          * Returns the Y position of an html element
27623          * @method getPosY
27624          * @param el the element for which to get the position
27625          * @return {int} the Y coordinate
27626          * @deprecated use Ext.lib.Dom.getY instead
27627          * @static
27628          */
27629         getPosY: function(el) {
27630             return Ext.lib.Dom.getY(el);
27631         },
27632
27633         /**
27634          * Swap two nodes.  In IE, we use the native method, for others we
27635          * emulate the IE behavior
27636          * @method swapNode
27637          * @param n1 the first node to swap
27638          * @param n2 the other node to swap
27639          * @static
27640          */
27641         swapNode: function(n1, n2) {
27642             if (n1.swapNode) {
27643                 n1.swapNode(n2);
27644             } else {
27645                 var p = n2.parentNode;
27646                 var s = n2.nextSibling;
27647
27648                 if (s == n1) {
27649                     p.insertBefore(n1, n2);
27650                 } else if (n2 == n1.nextSibling) {
27651                     p.insertBefore(n2, n1);
27652                 } else {
27653                     n1.parentNode.replaceChild(n2, n1);
27654                     p.insertBefore(n1, s);
27655                 }
27656             }
27657         },
27658
27659         /**
27660          * Returns the current scroll position
27661          * @method getScroll
27662          * @private
27663          * @static
27664          */
27665         getScroll: function () {
27666             var t, l, dde=document.documentElement, db=document.body;
27667             if (dde && (dde.scrollTop || dde.scrollLeft)) {
27668                 t = dde.scrollTop;
27669                 l = dde.scrollLeft;
27670             } else if (db) {
27671                 t = db.scrollTop;
27672                 l = db.scrollLeft;
27673             } else {
27674
27675             }
27676             return { top: t, left: l };
27677         },
27678
27679         /**
27680          * Returns the specified element style property
27681          * @method getStyle
27682          * @param {HTMLElement} el          the element
27683          * @param {string}      styleProp   the style property
27684          * @return {string} The value of the style property
27685          * @deprecated use Ext.lib.Dom.getStyle
27686          * @static
27687          */
27688         getStyle: function(el, styleProp) {
27689             return Ext.fly(el).getStyle(styleProp);
27690         },
27691
27692         /**
27693          * Gets the scrollTop
27694          * @method getScrollTop
27695          * @return {int} the document's scrollTop
27696          * @static
27697          */
27698         getScrollTop: function () { return this.getScroll().top; },
27699
27700         /**
27701          * Gets the scrollLeft
27702          * @method getScrollLeft
27703          * @return {int} the document's scrollTop
27704          * @static
27705          */
27706         getScrollLeft: function () { return this.getScroll().left; },
27707
27708         /**
27709          * Sets the x/y position of an element to the location of the
27710          * target element.
27711          * @method moveToEl
27712          * @param {HTMLElement} moveEl      The element to move
27713          * @param {HTMLElement} targetEl    The position reference element
27714          * @static
27715          */
27716         moveToEl: function (moveEl, targetEl) {
27717             var aCoord = Ext.lib.Dom.getXY(targetEl);
27718             Ext.lib.Dom.setXY(moveEl, aCoord);
27719         },
27720
27721         /**
27722          * Numeric array sort function
27723          * @method numericSort
27724          * @static
27725          */
27726         numericSort: function(a, b) { return (a - b); },
27727
27728         /**
27729          * Internal counter
27730          * @property _timeoutCount
27731          * @private
27732          * @static
27733          */
27734         _timeoutCount: 0,
27735
27736         /**
27737          * Trying to make the load order less important.  Without this we get
27738          * an error if this file is loaded before the Event Utility.
27739          * @method _addListeners
27740          * @private
27741          * @static
27742          */
27743         _addListeners: function() {
27744             var DDM = Ext.dd.DDM;
27745             if ( Ext.lib.Event && document ) {
27746                 DDM._onLoad();
27747             } else {
27748                 if (DDM._timeoutCount > 2000) {
27749                 } else {
27750                     setTimeout(DDM._addListeners, 10);
27751                     if (document && document.body) {
27752                         DDM._timeoutCount += 1;
27753                     }
27754                 }
27755             }
27756         },
27757
27758         /**
27759          * Recursively searches the immediate parent and all child nodes for
27760          * the handle element in order to determine wheter or not it was
27761          * clicked.
27762          * @method handleWasClicked
27763          * @param node the html element to inspect
27764          * @static
27765          */
27766         handleWasClicked: function(node, id) {
27767             if (this.isHandle(id, node.id)) {
27768                 return true;
27769             } else {
27770                 // check to see if this is a text node child of the one we want
27771                 var p = node.parentNode;
27772
27773                 while (p) {
27774                     if (this.isHandle(id, p.id)) {
27775                         return true;
27776                     } else {
27777                         p = p.parentNode;
27778                     }
27779                 }
27780             }
27781
27782             return false;
27783         }
27784
27785     };
27786
27787 }();
27788
27789 // shorter alias, save a few bytes
27790 Ext.dd.DDM = Ext.dd.DragDropMgr;
27791 Ext.dd.DDM._addListeners();
27792
27793 }
27794
27795 /**
27796  * @class Ext.dd.DD
27797  * A DragDrop implementation where the linked element follows the
27798  * mouse cursor during a drag.
27799  * @extends Ext.dd.DragDrop
27800  * @constructor
27801  * @param {String} id the id of the linked element
27802  * @param {String} sGroup the group of related DragDrop items
27803  * @param {object} config an object containing configurable attributes
27804  *                Valid properties for DD:
27805  *                    scroll
27806  */
27807 Ext.dd.DD = function(id, sGroup, config) {
27808     if (id) {
27809         this.init(id, sGroup, config);
27810     }
27811 };
27812
27813 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
27814
27815     /**
27816      * When set to true, the utility automatically tries to scroll the browser
27817      * window when a drag and drop element is dragged near the viewport boundary.
27818      * Defaults to true.
27819      * @property scroll
27820      * @type boolean
27821      */
27822     scroll: true,
27823
27824     /**
27825      * Sets the pointer offset to the distance between the linked element's top
27826      * left corner and the location the element was clicked
27827      * @method autoOffset
27828      * @param {int} iPageX the X coordinate of the click
27829      * @param {int} iPageY the Y coordinate of the click
27830      */
27831     autoOffset: function(iPageX, iPageY) {
27832         var x = iPageX - this.startPageX;
27833         var y = iPageY - this.startPageY;
27834         this.setDelta(x, y);
27835     },
27836
27837     /**
27838      * Sets the pointer offset.  You can call this directly to force the
27839      * offset to be in a particular location (e.g., pass in 0,0 to set it
27840      * to the center of the object)
27841      * @method setDelta
27842      * @param {int} iDeltaX the distance from the left
27843      * @param {int} iDeltaY the distance from the top
27844      */
27845     setDelta: function(iDeltaX, iDeltaY) {
27846         this.deltaX = iDeltaX;
27847         this.deltaY = iDeltaY;
27848     },
27849
27850     /**
27851      * Sets the drag element to the location of the mousedown or click event,
27852      * maintaining the cursor location relative to the location on the element
27853      * that was clicked.  Override this if you want to place the element in a
27854      * location other than where the cursor is.
27855      * @method setDragElPos
27856      * @param {int} iPageX the X coordinate of the mousedown or drag event
27857      * @param {int} iPageY the Y coordinate of the mousedown or drag event
27858      */
27859     setDragElPos: function(iPageX, iPageY) {
27860         // the first time we do this, we are going to check to make sure
27861         // the element has css positioning
27862
27863         var el = this.getDragEl();
27864         this.alignElWithMouse(el, iPageX, iPageY);
27865     },
27866
27867     /**
27868      * Sets the element to the location of the mousedown or click event,
27869      * maintaining the cursor location relative to the location on the element
27870      * that was clicked.  Override this if you want to place the element in a
27871      * location other than where the cursor is.
27872      * @method alignElWithMouse
27873      * @param {HTMLElement} el the element to move
27874      * @param {int} iPageX the X coordinate of the mousedown or drag event
27875      * @param {int} iPageY the Y coordinate of the mousedown or drag event
27876      */
27877     alignElWithMouse: function(el, iPageX, iPageY) {
27878         var oCoord = this.getTargetCoord(iPageX, iPageY);
27879         var fly = el.dom ? el : Ext.fly(el, '_dd');
27880         if (!this.deltaSetXY) {
27881             var aCoord = [oCoord.x, oCoord.y];
27882             fly.setXY(aCoord);
27883             var newLeft = fly.getLeft(true);
27884             var newTop  = fly.getTop(true);
27885             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
27886         } else {
27887             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
27888         }
27889
27890         this.cachePosition(oCoord.x, oCoord.y);
27891         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
27892         return oCoord;
27893     },
27894
27895     /**
27896      * Saves the most recent position so that we can reset the constraints and
27897      * tick marks on-demand.  We need to know this so that we can calculate the
27898      * number of pixels the element is offset from its original position.
27899      * @method cachePosition
27900      * @param iPageX the current x position (optional, this just makes it so we
27901      * don't have to look it up again)
27902      * @param iPageY the current y position (optional, this just makes it so we
27903      * don't have to look it up again)
27904      */
27905     cachePosition: function(iPageX, iPageY) {
27906         if (iPageX) {
27907             this.lastPageX = iPageX;
27908             this.lastPageY = iPageY;
27909         } else {
27910             var aCoord = Ext.lib.Dom.getXY(this.getEl());
27911             this.lastPageX = aCoord[0];
27912             this.lastPageY = aCoord[1];
27913         }
27914     },
27915
27916     /**
27917      * Auto-scroll the window if the dragged object has been moved beyond the
27918      * visible window boundary.
27919      * @method autoScroll
27920      * @param {int} x the drag element's x position
27921      * @param {int} y the drag element's y position
27922      * @param {int} h the height of the drag element
27923      * @param {int} w the width of the drag element
27924      * @private
27925      */
27926     autoScroll: function(x, y, h, w) {
27927
27928         if (this.scroll) {
27929             // The client height
27930             var clientH = Ext.lib.Dom.getViewHeight();
27931
27932             // The client width
27933             var clientW = Ext.lib.Dom.getViewWidth();
27934
27935             // The amt scrolled down
27936             var st = this.DDM.getScrollTop();
27937
27938             // The amt scrolled right
27939             var sl = this.DDM.getScrollLeft();
27940
27941             // Location of the bottom of the element
27942             var bot = h + y;
27943
27944             // Location of the right of the element
27945             var right = w + x;
27946
27947             // The distance from the cursor to the bottom of the visible area,
27948             // adjusted so that we don't scroll if the cursor is beyond the
27949             // element drag constraints
27950             var toBot = (clientH + st - y - this.deltaY);
27951
27952             // The distance from the cursor to the right of the visible area
27953             var toRight = (clientW + sl - x - this.deltaX);
27954
27955
27956             // How close to the edge the cursor must be before we scroll
27957             // var thresh = (document.all) ? 100 : 40;
27958             var thresh = 40;
27959
27960             // How many pixels to scroll per autoscroll op.  This helps to reduce
27961             // clunky scrolling. IE is more sensitive about this ... it needs this
27962             // value to be higher.
27963             var scrAmt = (document.all) ? 80 : 30;
27964
27965             // Scroll down if we are near the bottom of the visible page and the
27966             // obj extends below the crease
27967             if ( bot > clientH && toBot < thresh ) {
27968                 window.scrollTo(sl, st + scrAmt);
27969             }
27970
27971             // Scroll up if the window is scrolled down and the top of the object
27972             // goes above the top border
27973             if ( y < st && st > 0 && y - st < thresh ) {
27974                 window.scrollTo(sl, st - scrAmt);
27975             }
27976
27977             // Scroll right if the obj is beyond the right border and the cursor is
27978             // near the border.
27979             if ( right > clientW && toRight < thresh ) {
27980                 window.scrollTo(sl + scrAmt, st);
27981             }
27982
27983             // Scroll left if the window has been scrolled to the right and the obj
27984             // extends past the left border
27985             if ( x < sl && sl > 0 && x - sl < thresh ) {
27986                 window.scrollTo(sl - scrAmt, st);
27987             }
27988         }
27989     },
27990
27991     /**
27992      * Finds the location the element should be placed if we want to move
27993      * it to where the mouse location less the click offset would place us.
27994      * @method getTargetCoord
27995      * @param {int} iPageX the X coordinate of the click
27996      * @param {int} iPageY the Y coordinate of the click
27997      * @return an object that contains the coordinates (Object.x and Object.y)
27998      * @private
27999      */
28000     getTargetCoord: function(iPageX, iPageY) {
28001
28002
28003         var x = iPageX - this.deltaX;
28004         var y = iPageY - this.deltaY;
28005
28006         if (this.constrainX) {
28007             if (x < this.minX) { x = this.minX; }
28008             if (x > this.maxX) { x = this.maxX; }
28009         }
28010
28011         if (this.constrainY) {
28012             if (y < this.minY) { y = this.minY; }
28013             if (y > this.maxY) { y = this.maxY; }
28014         }
28015
28016         x = this.getTick(x, this.xTicks);
28017         y = this.getTick(y, this.yTicks);
28018
28019
28020         return {x:x, y:y};
28021     },
28022
28023     /**
28024      * Sets up config options specific to this class. Overrides
28025      * Ext.dd.DragDrop, but all versions of this method through the
28026      * inheritance chain are called
28027      */
28028     applyConfig: function() {
28029         Ext.dd.DD.superclass.applyConfig.call(this);
28030         this.scroll = (this.config.scroll !== false);
28031     },
28032
28033     /**
28034      * Event that fires prior to the onMouseDown event.  Overrides
28035      * Ext.dd.DragDrop.
28036      */
28037     b4MouseDown: function(e) {
28038         // this.resetConstraints();
28039         this.autoOffset(e.getPageX(),
28040                             e.getPageY());
28041     },
28042
28043     /**
28044      * Event that fires prior to the onDrag event.  Overrides
28045      * Ext.dd.DragDrop.
28046      */
28047     b4Drag: function(e) {
28048         this.setDragElPos(e.getPageX(),
28049                             e.getPageY());
28050     },
28051
28052     toString: function() {
28053         return ("DD " + this.id);
28054     }
28055
28056     //////////////////////////////////////////////////////////////////////////
28057     // Debugging ygDragDrop events that can be overridden
28058     //////////////////////////////////////////////////////////////////////////
28059     /*
28060     startDrag: function(x, y) {
28061     },
28062
28063     onDrag: function(e) {
28064     },
28065
28066     onDragEnter: function(e, id) {
28067     },
28068
28069     onDragOver: function(e, id) {
28070     },
28071
28072     onDragOut: function(e, id) {
28073     },
28074
28075     onDragDrop: function(e, id) {
28076     },
28077
28078     endDrag: function(e) {
28079     }
28080
28081     */
28082
28083 });
28084 /**
28085  * @class Ext.dd.DDProxy
28086  * A DragDrop implementation that inserts an empty, bordered div into
28087  * the document that follows the cursor during drag operations.  At the time of
28088  * the click, the frame div is resized to the dimensions of the linked html
28089  * element, and moved to the exact location of the linked element.
28090  *
28091  * References to the "frame" element refer to the single proxy element that
28092  * was created to be dragged in place of all DDProxy elements on the
28093  * page.
28094  *
28095  * @extends Ext.dd.DD
28096  * @constructor
28097  * @param {String} id the id of the linked html element
28098  * @param {String} sGroup the group of related DragDrop objects
28099  * @param {object} config an object containing configurable attributes
28100  *                Valid properties for DDProxy in addition to those in DragDrop:
28101  *                   resizeFrame, centerFrame, dragElId
28102  */
28103 Ext.dd.DDProxy = function(id, sGroup, config) {
28104     if (id) {
28105         this.init(id, sGroup, config);
28106         this.initFrame();
28107     }
28108 };
28109
28110 /**
28111  * The default drag frame div id
28112  * @property Ext.dd.DDProxy.dragElId
28113  * @type String
28114  * @static
28115  */
28116 Ext.dd.DDProxy.dragElId = "ygddfdiv";
28117
28118 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
28119
28120     /**
28121      * By default we resize the drag frame to be the same size as the element
28122      * we want to drag (this is to get the frame effect).  We can turn it off
28123      * if we want a different behavior.
28124      * @property resizeFrame
28125      * @type boolean
28126      */
28127     resizeFrame: true,
28128
28129     /**
28130      * By default the frame is positioned exactly where the drag element is, so
28131      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
28132      * you do not have constraints on the obj is to have the drag frame centered
28133      * around the cursor.  Set centerFrame to true for this effect.
28134      * @property centerFrame
28135      * @type boolean
28136      */
28137     centerFrame: false,
28138
28139     /**
28140      * Creates the proxy element if it does not yet exist
28141      * @method createFrame
28142      */
28143     createFrame: function() {
28144         var self = this;
28145         var body = document.body;
28146
28147         if (!body || !body.firstChild) {
28148             setTimeout( function() { self.createFrame(); }, 50 );
28149             return;
28150         }
28151
28152         var div = this.getDragEl();
28153
28154         if (!div) {
28155             div    = document.createElement("div");
28156             div.id = this.dragElId;
28157             var s  = div.style;
28158
28159             s.position   = "absolute";
28160             s.visibility = "hidden";
28161             s.cursor     = "move";
28162             s.border     = "2px solid #aaa";
28163             s.zIndex     = 999;
28164
28165             // appendChild can blow up IE if invoked prior to the window load event
28166             // while rendering a table.  It is possible there are other scenarios
28167             // that would cause this to happen as well.
28168             body.insertBefore(div, body.firstChild);
28169         }
28170     },
28171
28172     /**
28173      * Initialization for the drag frame element.  Must be called in the
28174      * constructor of all subclasses
28175      * @method initFrame
28176      */
28177     initFrame: function() {
28178         this.createFrame();
28179     },
28180
28181     applyConfig: function() {
28182         Ext.dd.DDProxy.superclass.applyConfig.call(this);
28183
28184         this.resizeFrame = (this.config.resizeFrame !== false);
28185         this.centerFrame = (this.config.centerFrame);
28186         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
28187     },
28188
28189     /**
28190      * Resizes the drag frame to the dimensions of the clicked object, positions
28191      * it over the object, and finally displays it
28192      * @method showFrame
28193      * @param {int} iPageX X click position
28194      * @param {int} iPageY Y click position
28195      * @private
28196      */
28197     showFrame: function(iPageX, iPageY) {
28198         var el = this.getEl();
28199         var dragEl = this.getDragEl();
28200         var s = dragEl.style;
28201
28202         this._resizeProxy();
28203
28204         if (this.centerFrame) {
28205             this.setDelta( Math.round(parseInt(s.width,  10)/2),
28206                            Math.round(parseInt(s.height, 10)/2) );
28207         }
28208
28209         this.setDragElPos(iPageX, iPageY);
28210
28211         Ext.fly(dragEl).show();
28212     },
28213
28214     /**
28215      * The proxy is automatically resized to the dimensions of the linked
28216      * element when a drag is initiated, unless resizeFrame is set to false
28217      * @method _resizeProxy
28218      * @private
28219      */
28220     _resizeProxy: function() {
28221         if (this.resizeFrame) {
28222             var el = this.getEl();
28223             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
28224         }
28225     },
28226
28227     // overrides Ext.dd.DragDrop
28228     b4MouseDown: function(e) {
28229         var x = e.getPageX();
28230         var y = e.getPageY();
28231         this.autoOffset(x, y);
28232         this.setDragElPos(x, y);
28233     },
28234
28235     // overrides Ext.dd.DragDrop
28236     b4StartDrag: function(x, y) {
28237         // show the drag frame
28238         this.showFrame(x, y);
28239     },
28240
28241     // overrides Ext.dd.DragDrop
28242     b4EndDrag: function(e) {
28243         Ext.fly(this.getDragEl()).hide();
28244     },
28245
28246     // overrides Ext.dd.DragDrop
28247     // By default we try to move the element to the last location of the frame.
28248     // This is so that the default behavior mirrors that of Ext.dd.DD.
28249     endDrag: function(e) {
28250
28251         var lel = this.getEl();
28252         var del = this.getDragEl();
28253
28254         // Show the drag frame briefly so we can get its position
28255         del.style.visibility = "";
28256
28257         this.beforeMove();
28258         // Hide the linked element before the move to get around a Safari
28259         // rendering bug.
28260         lel.style.visibility = "hidden";
28261         Ext.dd.DDM.moveToEl(lel, del);
28262         del.style.visibility = "hidden";
28263         lel.style.visibility = "";
28264
28265         this.afterDrag();
28266     },
28267
28268     beforeMove : function(){
28269
28270     },
28271
28272     afterDrag : function(){
28273
28274     },
28275
28276     toString: function() {
28277         return ("DDProxy " + this.id);
28278     }
28279
28280 });
28281 /**
28282  * @class Ext.dd.DDTarget
28283  * A DragDrop implementation that does not move, but can be a drop
28284  * target.  You would get the same result by simply omitting implementation
28285  * for the event callbacks, but this way we reduce the processing cost of the
28286  * event listener and the callbacks.
28287  * @extends Ext.dd.DragDrop
28288  * @constructor
28289  * @param {String} id the id of the element that is a drop target
28290  * @param {String} sGroup the group of related DragDrop objects
28291  * @param {object} config an object containing configurable attributes
28292  *                 Valid properties for DDTarget in addition to those in
28293  *                 DragDrop:
28294  *                    none
28295  */
28296 Ext.dd.DDTarget = function(id, sGroup, config) {
28297     if (id) {
28298         this.initTarget(id, sGroup, config);
28299     }
28300 };
28301
28302 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
28303 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
28304     toString: function() {
28305         return ("DDTarget " + this.id);
28306     }
28307 });
28308 /**\r
28309  * @class Ext.dd.DragTracker\r
28310  * @extends Ext.util.Observable\r
28311  */\r
28312 Ext.dd.DragTracker = function(config){\r
28313     Ext.apply(this, config);\r
28314     this.addEvents(\r
28315         /**\r
28316          * @event mousedown\r
28317          * @param {Object} this\r
28318          * @param {Object} e event object\r
28319          */\r
28320         'mousedown',\r
28321         /**\r
28322          * @event mouseup\r
28323          * @param {Object} this\r
28324          * @param {Object} e event object\r
28325          */\r
28326         'mouseup',\r
28327         /**\r
28328          * @event mousemove\r
28329          * @param {Object} this\r
28330          * @param {Object} e event object\r
28331          */\r
28332         'mousemove',\r
28333         /**\r
28334          * @event dragstart\r
28335          * @param {Object} this\r
28336          * @param {Object} startXY the page coordinates of the event\r
28337          */\r
28338         'dragstart',\r
28339         /**\r
28340          * @event dragend\r
28341          * @param {Object} this\r
28342          * @param {Object} e event object\r
28343          */\r
28344         'dragend',\r
28345         /**\r
28346          * @event drag\r
28347          * @param {Object} this\r
28348          * @param {Object} e event object\r
28349          */\r
28350         'drag'\r
28351     );\r
28352 \r
28353     this.dragRegion = new Ext.lib.Region(0,0,0,0);\r
28354 \r
28355     if(this.el){\r
28356         this.initEl(this.el);\r
28357     }\r
28358 }\r
28359 \r
28360 Ext.extend(Ext.dd.DragTracker, Ext.util.Observable,  {\r
28361     /**\r
28362      * @cfg {Boolean} active\r
28363          * Defaults to <tt>false</tt>.\r
28364          */     \r
28365     active: false,\r
28366     /**\r
28367      * @cfg {Number} tolerance\r
28368          * Defaults to <tt>5</tt>.\r
28369          */     \r
28370     tolerance: 5,\r
28371     /**\r
28372      * @cfg {Boolean/Number} autoStart\r
28373          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.\r
28374          * Specify a Number for the number of milliseconds to defer trigger start.\r
28375          */     \r
28376     autoStart: false,\r
28377 \r
28378     initEl: function(el){\r
28379         this.el = Ext.get(el);\r
28380         el.on('mousedown', this.onMouseDown, this,\r
28381                 this.delegate ? {delegate: this.delegate} : undefined);\r
28382     },\r
28383 \r
28384     destroy : function(){\r
28385         this.el.un('mousedown', this.onMouseDown, this);\r
28386     },\r
28387 \r
28388     onMouseDown: function(e, target){\r
28389         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){\r
28390             this.startXY = this.lastXY = e.getXY();\r
28391             this.dragTarget = this.delegate ? target : this.el.dom;\r
28392             if(this.preventDefault !== false){\r
28393                 e.preventDefault();\r
28394             }\r
28395             var doc = Ext.getDoc();\r
28396             doc.on('mouseup', this.onMouseUp, this);\r
28397             doc.on('mousemove', this.onMouseMove, this);\r
28398             doc.on('selectstart', this.stopSelect, this);\r
28399             if(this.autoStart){\r
28400                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this);\r
28401             }\r
28402         }\r
28403     },\r
28404 \r
28405     onMouseMove: function(e, target){\r
28406         // HACK: IE hack to see if button was released outside of window. */\r
28407         if(this.active && Ext.isIE && !e.browserEvent.button){\r
28408             e.preventDefault();\r
28409             this.onMouseUp(e);\r
28410             return;\r
28411         }\r
28412 \r
28413         e.preventDefault();\r
28414         var xy = e.getXY(), s = this.startXY;\r
28415         this.lastXY = xy;\r
28416         if(!this.active){\r
28417             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){\r
28418                 this.triggerStart();\r
28419             }else{\r
28420                 return;\r
28421             }\r
28422         }\r
28423         this.fireEvent('mousemove', this, e);\r
28424         this.onDrag(e);\r
28425         this.fireEvent('drag', this, e);\r
28426     },\r
28427 \r
28428     onMouseUp: function(e){\r
28429         var doc = Ext.getDoc();\r
28430         doc.un('mousemove', this.onMouseMove, this);\r
28431         doc.un('mouseup', this.onMouseUp, this);\r
28432         doc.un('selectstart', this.stopSelect, this);\r
28433         e.preventDefault();\r
28434         this.clearStart();\r
28435         var wasActive = this.active;\r
28436         this.active = false;\r
28437         delete this.elRegion;\r
28438         this.fireEvent('mouseup', this, e);\r
28439         if(wasActive){\r
28440             this.onEnd(e);\r
28441             this.fireEvent('dragend', this, e);\r
28442         }\r
28443     },\r
28444 \r
28445     triggerStart: function(isTimer){\r
28446         this.clearStart();\r
28447         this.active = true;\r
28448         this.onStart(this.startXY);\r
28449         this.fireEvent('dragstart', this, this.startXY);\r
28450     },\r
28451 \r
28452     clearStart : function(){\r
28453         if(this.timer){\r
28454             clearTimeout(this.timer);\r
28455             delete this.timer;\r
28456         }\r
28457     },\r
28458 \r
28459     stopSelect : function(e){\r
28460         e.stopEvent();\r
28461         return false;\r
28462     },\r
28463 \r
28464     onBeforeStart : function(e){\r
28465 \r
28466     },\r
28467 \r
28468     onStart : function(xy){\r
28469 \r
28470     },\r
28471 \r
28472     onDrag : function(e){\r
28473 \r
28474     },\r
28475 \r
28476     onEnd : function(e){\r
28477 \r
28478     },\r
28479 \r
28480     getDragTarget : function(){\r
28481         return this.dragTarget;\r
28482     },\r
28483 \r
28484     getDragCt : function(){\r
28485         return this.el;\r
28486     },\r
28487 \r
28488     getXY : function(constrain){\r
28489         return constrain ?\r
28490                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;\r
28491     },\r
28492 \r
28493     getOffset : function(constrain){\r
28494         var xy = this.getXY(constrain);\r
28495         var s = this.startXY;\r
28496         return [s[0]-xy[0], s[1]-xy[1]];\r
28497     },\r
28498 \r
28499     constrainModes: {\r
28500         'point' : function(xy){\r
28501 \r
28502             if(!this.elRegion){\r
28503                 this.elRegion = this.getDragCt().getRegion();\r
28504             }\r
28505 \r
28506             var dr = this.dragRegion;\r
28507 \r
28508             dr.left = xy[0];\r
28509             dr.top = xy[1];\r
28510             dr.right = xy[0];\r
28511             dr.bottom = xy[1];\r
28512 \r
28513             dr.constrainTo(this.elRegion);\r
28514 \r
28515             return [dr.left, dr.top];\r
28516         }\r
28517     }\r
28518 });/**\r
28519  * @class Ext.dd.ScrollManager\r
28520  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>\r
28521  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,\r
28522  * but you can also override most of the configs per scroll container by adding a \r
28523  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},\r
28524  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:\r
28525  * <pre><code>\r
28526 var el = Ext.get('scroll-ct');\r
28527 el.ddScrollConfig = {\r
28528     vthresh: 50,\r
28529     hthresh: -1,\r
28530     frequency: 100,\r
28531     increment: 200\r
28532 };\r
28533 Ext.dd.ScrollManager.register(el);\r
28534 </code></pre>\r
28535  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>\r
28536  * @singleton\r
28537  */\r
28538 Ext.dd.ScrollManager = function(){\r
28539     var ddm = Ext.dd.DragDropMgr;\r
28540     var els = {};\r
28541     var dragEl = null;\r
28542     var proc = {};\r
28543     \r
28544     var onStop = function(e){\r
28545         dragEl = null;\r
28546         clearProc();\r
28547     };\r
28548     \r
28549     var triggerRefresh = function(){\r
28550         if(ddm.dragCurrent){\r
28551              ddm.refreshCache(ddm.dragCurrent.groups);\r
28552         }\r
28553     };\r
28554     \r
28555     var doScroll = function(){\r
28556         if(ddm.dragCurrent){\r
28557             var dds = Ext.dd.ScrollManager;\r
28558             var inc = proc.el.ddScrollConfig ?\r
28559                       proc.el.ddScrollConfig.increment : dds.increment;\r
28560             if(!dds.animate){\r
28561                 if(proc.el.scroll(proc.dir, inc)){\r
28562                     triggerRefresh();\r
28563                 }\r
28564             }else{\r
28565                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);\r
28566             }\r
28567         }\r
28568     };\r
28569     \r
28570     var clearProc = function(){\r
28571         if(proc.id){\r
28572             clearInterval(proc.id);\r
28573         }\r
28574         proc.id = 0;\r
28575         proc.el = null;\r
28576         proc.dir = "";\r
28577     };\r
28578     \r
28579     var startProc = function(el, dir){\r
28580         clearProc();\r
28581         proc.el = el;\r
28582         proc.dir = dir;\r
28583         var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? \r
28584                 el.ddScrollConfig.frequency : Ext.dd.ScrollManager.frequency;\r
28585         proc.id = setInterval(doScroll, freq);\r
28586     };\r
28587     \r
28588     var onFire = function(e, isDrop){\r
28589         if(isDrop || !ddm.dragCurrent){ return; }\r
28590         var dds = Ext.dd.ScrollManager;\r
28591         if(!dragEl || dragEl != ddm.dragCurrent){\r
28592             dragEl = ddm.dragCurrent;\r
28593             // refresh regions on drag start\r
28594             dds.refreshCache();\r
28595         }\r
28596         \r
28597         var xy = Ext.lib.Event.getXY(e);\r
28598         var pt = new Ext.lib.Point(xy[0], xy[1]);\r
28599         for(var id in els){\r
28600             var el = els[id], r = el._region;\r
28601             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;\r
28602             if(r && r.contains(pt) && el.isScrollable()){\r
28603                 if(r.bottom - pt.y <= c.vthresh){\r
28604                     if(proc.el != el){\r
28605                         startProc(el, "down");\r
28606                     }\r
28607                     return;\r
28608                 }else if(r.right - pt.x <= c.hthresh){\r
28609                     if(proc.el != el){\r
28610                         startProc(el, "left");\r
28611                     }\r
28612                     return;\r
28613                 }else if(pt.y - r.top <= c.vthresh){\r
28614                     if(proc.el != el){\r
28615                         startProc(el, "up");\r
28616                     }\r
28617                     return;\r
28618                 }else if(pt.x - r.left <= c.hthresh){\r
28619                     if(proc.el != el){\r
28620                         startProc(el, "right");\r
28621                     }\r
28622                     return;\r
28623                 }\r
28624             }\r
28625         }\r
28626         clearProc();\r
28627     };\r
28628     \r
28629     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);\r
28630     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);\r
28631     \r
28632     return {\r
28633         /**\r
28634          * Registers new overflow element(s) to auto scroll\r
28635          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either\r
28636          */\r
28637         register : function(el){\r
28638             if(Ext.isArray(el)){\r
28639                 for(var i = 0, len = el.length; i < len; i++) {\r
28640                         this.register(el[i]);\r
28641                 }\r
28642             }else{\r
28643                 el = Ext.get(el);\r
28644                 els[el.id] = el;\r
28645             }\r
28646         },\r
28647         \r
28648         /**\r
28649          * Unregisters overflow element(s) so they are no longer scrolled\r
28650          * @param {Mixed/Array} el The id of or the element to be removed or an array of either\r
28651          */\r
28652         unregister : function(el){\r
28653             if(Ext.isArray(el)){\r
28654                 for(var i = 0, len = el.length; i < len; i++) {\r
28655                         this.unregister(el[i]);\r
28656                 }\r
28657             }else{\r
28658                 el = Ext.get(el);\r
28659                 delete els[el.id];\r
28660             }\r
28661         },\r
28662         \r
28663         /**\r
28664          * The number of pixels from the top or bottom edge of a container the pointer needs to be to\r
28665          * trigger scrolling (defaults to 25)\r
28666          * @type Number\r
28667          */\r
28668         vthresh : 25,\r
28669         /**\r
28670          * The number of pixels from the right or left edge of a container the pointer needs to be to\r
28671          * trigger scrolling (defaults to 25)\r
28672          * @type Number\r
28673          */\r
28674         hthresh : 25,\r
28675 \r
28676         /**\r
28677          * The number of pixels to scroll in each scroll increment (defaults to 50)\r
28678          * @type Number\r
28679          */\r
28680         increment : 100,\r
28681         \r
28682         /**\r
28683          * The frequency of scrolls in milliseconds (defaults to 500)\r
28684          * @type Number\r
28685          */\r
28686         frequency : 500,\r
28687         \r
28688         /**\r
28689          * True to animate the scroll (defaults to true)\r
28690          * @type Boolean\r
28691          */\r
28692         animate: true,\r
28693         \r
28694         /**\r
28695          * The animation duration in seconds - \r
28696          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)\r
28697          * @type Number\r
28698          */\r
28699         animDuration: .4,\r
28700         \r
28701         /**\r
28702          * Manually trigger a cache refresh.\r
28703          */\r
28704         refreshCache : function(){\r
28705             for(var id in els){\r
28706                 if(typeof els[id] == 'object'){ // for people extending the object prototype\r
28707                     els[id]._region = els[id].getRegion();\r
28708                 }\r
28709             }\r
28710         }\r
28711     };\r
28712 }();/**\r
28713  * @class Ext.dd.Registry\r
28714  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either\r
28715  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.\r
28716  * @singleton\r
28717  */\r
28718 Ext.dd.Registry = function(){\r
28719     var elements = {}; \r
28720     var handles = {}; \r
28721     var autoIdSeed = 0;\r
28722 \r
28723     var getId = function(el, autogen){\r
28724         if(typeof el == "string"){\r
28725             return el;\r
28726         }\r
28727         var id = el.id;\r
28728         if(!id && autogen !== false){\r
28729             id = "extdd-" + (++autoIdSeed);\r
28730             el.id = id;\r
28731         }\r
28732         return id;\r
28733     };\r
28734     \r
28735     return {\r
28736     /**\r
28737      * Resgister a drag drop element\r
28738      * @param {String/HTMLElement) element The id or DOM node to register\r
28739      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved\r
28740      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code\r
28741      * knows how to interpret, plus there are some specific properties known to the Registry that should be\r
28742      * populated in the data object (if applicable):\r
28743      * <pre>\r
28744 Value      Description<br />\r
28745 ---------  ------------------------------------------<br />\r
28746 handles    Array of DOM nodes that trigger dragging<br />\r
28747            for the element being registered<br />\r
28748 isHandle   True if the element passed in triggers<br />\r
28749            dragging itself, else false\r
28750 </pre>\r
28751      */\r
28752         register : function(el, data){\r
28753             data = data || {};\r
28754             if(typeof el == "string"){\r
28755                 el = document.getElementById(el);\r
28756             }\r
28757             data.ddel = el;\r
28758             elements[getId(el)] = data;\r
28759             if(data.isHandle !== false){\r
28760                 handles[data.ddel.id] = data;\r
28761             }\r
28762             if(data.handles){\r
28763                 var hs = data.handles;\r
28764                 for(var i = 0, len = hs.length; i < len; i++){\r
28765                         handles[getId(hs[i])] = data;\r
28766                 }\r
28767             }\r
28768         },\r
28769 \r
28770     /**\r
28771      * Unregister a drag drop element\r
28772      * @param {String/HTMLElement) element The id or DOM node to unregister\r
28773      */\r
28774         unregister : function(el){\r
28775             var id = getId(el, false);\r
28776             var data = elements[id];\r
28777             if(data){\r
28778                 delete elements[id];\r
28779                 if(data.handles){\r
28780                     var hs = data.handles;\r
28781                     for(var i = 0, len = hs.length; i < len; i++){\r
28782                         delete handles[getId(hs[i], false)];\r
28783                     }\r
28784                 }\r
28785             }\r
28786         },\r
28787 \r
28788     /**\r
28789      * Returns the handle registered for a DOM Node by id\r
28790      * @param {String/HTMLElement} id The DOM node or id to look up\r
28791      * @return {Object} handle The custom handle data\r
28792      */\r
28793         getHandle : function(id){\r
28794             if(typeof id != "string"){ // must be element?\r
28795                 id = id.id;\r
28796             }\r
28797             return handles[id];\r
28798         },\r
28799 \r
28800     /**\r
28801      * Returns the handle that is registered for the DOM node that is the target of the event\r
28802      * @param {Event} e The event\r
28803      * @return {Object} handle The custom handle data\r
28804      */\r
28805         getHandleFromEvent : function(e){\r
28806             var t = Ext.lib.Event.getTarget(e);\r
28807             return t ? handles[t.id] : null;\r
28808         },\r
28809 \r
28810     /**\r
28811      * Returns a custom data object that is registered for a DOM node by id\r
28812      * @param {String/HTMLElement} id The DOM node or id to look up\r
28813      * @return {Object} data The custom data\r
28814      */\r
28815         getTarget : function(id){\r
28816             if(typeof id != "string"){ // must be element?\r
28817                 id = id.id;\r
28818             }\r
28819             return elements[id];\r
28820         },\r
28821 \r
28822     /**\r
28823      * Returns a custom data object that is registered for the DOM node that is the target of the event\r
28824      * @param {Event} e The event\r
28825      * @return {Object} data The custom data\r
28826      */\r
28827         getTargetFromEvent : function(e){\r
28828             var t = Ext.lib.Event.getTarget(e);\r
28829             return t ? elements[t.id] || handles[t.id] : null;\r
28830         }\r
28831     };\r
28832 }();/**\r
28833  * @class Ext.dd.StatusProxy\r
28834  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the\r
28835  * default drag proxy used by all Ext.dd components.\r
28836  * @constructor\r
28837  * @param {Object} config\r
28838  */\r
28839 Ext.dd.StatusProxy = function(config){\r
28840     Ext.apply(this, config);\r
28841     this.id = this.id || Ext.id();\r
28842     this.el = new Ext.Layer({\r
28843         dh: {\r
28844             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [\r
28845                 {tag: "div", cls: "x-dd-drop-icon"},\r
28846                 {tag: "div", cls: "x-dd-drag-ghost"}\r
28847             ]\r
28848         }, \r
28849         shadow: !config || config.shadow !== false\r
28850     });\r
28851     this.ghost = Ext.get(this.el.dom.childNodes[1]);\r
28852     this.dropStatus = this.dropNotAllowed;\r
28853 };\r
28854 \r
28855 Ext.dd.StatusProxy.prototype = {\r
28856     /**\r
28857      * @cfg {String} dropAllowed\r
28858      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").\r
28859      */\r
28860     dropAllowed : "x-dd-drop-ok",\r
28861     /**\r
28862      * @cfg {String} dropNotAllowed\r
28863      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
28864      */\r
28865     dropNotAllowed : "x-dd-drop-nodrop",\r
28866 \r
28867     /**\r
28868      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed\r
28869      * over the current target element.\r
28870      * @param {String} cssClass The css class for the new drop status indicator image\r
28871      */\r
28872     setStatus : function(cssClass){\r
28873         cssClass = cssClass || this.dropNotAllowed;\r
28874         if(this.dropStatus != cssClass){\r
28875             this.el.replaceClass(this.dropStatus, cssClass);\r
28876             this.dropStatus = cssClass;\r
28877         }\r
28878     },\r
28879 \r
28880     /**\r
28881      * Resets the status indicator to the default dropNotAllowed value\r
28882      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it\r
28883      */\r
28884     reset : function(clearGhost){\r
28885         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;\r
28886         this.dropStatus = this.dropNotAllowed;\r
28887         if(clearGhost){\r
28888             this.ghost.update("");\r
28889         }\r
28890     },\r
28891 \r
28892     /**\r
28893      * Updates the contents of the ghost element\r
28894      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a\r
28895      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).\r
28896      */\r
28897     update : function(html){\r
28898         if(typeof html == "string"){\r
28899             this.ghost.update(html);\r
28900         }else{\r
28901             this.ghost.update("");\r
28902             html.style.margin = "0";\r
28903             this.ghost.dom.appendChild(html);\r
28904         }\r
28905         var el = this.ghost.dom.firstChild; \r
28906         if(el){\r
28907             Ext.fly(el).setStyle('float', 'none');\r
28908         }\r
28909     },\r
28910 \r
28911     /**\r
28912      * Returns the underlying proxy {@link Ext.Layer}\r
28913      * @return {Ext.Layer} el\r
28914     */\r
28915     getEl : function(){\r
28916         return this.el;\r
28917     },\r
28918 \r
28919     /**\r
28920      * Returns the ghost element\r
28921      * @return {Ext.Element} el\r
28922      */\r
28923     getGhost : function(){\r
28924         return this.ghost;\r
28925     },\r
28926 \r
28927     /**\r
28928      * Hides the proxy\r
28929      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them\r
28930      */\r
28931     hide : function(clear){\r
28932         this.el.hide();\r
28933         if(clear){\r
28934             this.reset(true);\r
28935         }\r
28936     },\r
28937 \r
28938     /**\r
28939      * Stops the repair animation if it's currently running\r
28940      */\r
28941     stop : function(){\r
28942         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){\r
28943             this.anim.stop();\r
28944         }\r
28945     },\r
28946 \r
28947     /**\r
28948      * Displays this proxy\r
28949      */\r
28950     show : function(){\r
28951         this.el.show();\r
28952     },\r
28953 \r
28954     /**\r
28955      * Force the Layer to sync its shadow and shim positions to the element\r
28956      */\r
28957     sync : function(){\r
28958         this.el.sync();\r
28959     },\r
28960 \r
28961     /**\r
28962      * Causes the proxy to return to its position of origin via an animation.  Should be called after an\r
28963      * invalid drop operation by the item being dragged.\r
28964      * @param {Array} xy The XY position of the element ([x, y])\r
28965      * @param {Function} callback The function to call after the repair is complete\r
28966      * @param {Object} scope The scope in which to execute the callback\r
28967      */\r
28968     repair : function(xy, callback, scope){\r
28969         this.callback = callback;\r
28970         this.scope = scope;\r
28971         if(xy && this.animRepair !== false){\r
28972             this.el.addClass("x-dd-drag-repair");\r
28973             this.el.hideUnders(true);\r
28974             this.anim = this.el.shift({\r
28975                 duration: this.repairDuration || .5,\r
28976                 easing: 'easeOut',\r
28977                 xy: xy,\r
28978                 stopFx: true,\r
28979                 callback: this.afterRepair,\r
28980                 scope: this\r
28981             });\r
28982         }else{\r
28983             this.afterRepair();\r
28984         }\r
28985     },\r
28986 \r
28987     // private\r
28988     afterRepair : function(){\r
28989         this.hide(true);\r
28990         if(typeof this.callback == "function"){\r
28991             this.callback.call(this.scope || this);\r
28992         }\r
28993         this.callback = null;\r
28994         this.scope = null;\r
28995     }\r
28996 };/**\r
28997  * @class Ext.dd.DragSource\r
28998  * @extends Ext.dd.DDProxy\r
28999  * A simple class that provides the basic implementation needed to make any element draggable.\r
29000  * @constructor\r
29001  * @param {Mixed} el The container element\r
29002  * @param {Object} config\r
29003  */\r
29004 Ext.dd.DragSource = function(el, config){\r
29005     this.el = Ext.get(el);\r
29006     if(!this.dragData){\r
29007         this.dragData = {};\r
29008     }\r
29009     \r
29010     Ext.apply(this, config);\r
29011     \r
29012     if(!this.proxy){\r
29013         this.proxy = new Ext.dd.StatusProxy();\r
29014     }\r
29015     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
29016           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});\r
29017     \r
29018     this.dragging = false;\r
29019 };\r
29020 \r
29021 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {\r
29022     /**\r
29023      * @cfg {String} ddGroup\r
29024      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
29025      * interact with other drag drop objects in the same group (defaults to undefined).\r
29026      */\r
29027     /**\r
29028      * @cfg {String} dropAllowed\r
29029      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
29030      */\r
29031     dropAllowed : "x-dd-drop-ok",\r
29032     /**\r
29033      * @cfg {String} dropNotAllowed\r
29034      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
29035      */\r
29036     dropNotAllowed : "x-dd-drop-nodrop",\r
29037 \r
29038     /**\r
29039      * Returns the data object associated with this drag source\r
29040      * @return {Object} data An object containing arbitrary data\r
29041      */\r
29042     getDragData : function(e){\r
29043         return this.dragData;\r
29044     },\r
29045 \r
29046     // private\r
29047     onDragEnter : function(e, id){\r
29048         var target = Ext.dd.DragDropMgr.getDDById(id);\r
29049         this.cachedTarget = target;\r
29050         if(this.beforeDragEnter(target, e, id) !== false){\r
29051             if(target.isNotifyTarget){\r
29052                 var status = target.notifyEnter(this, e, this.dragData);\r
29053                 this.proxy.setStatus(status);\r
29054             }else{\r
29055                 this.proxy.setStatus(this.dropAllowed);\r
29056             }\r
29057             \r
29058             if(this.afterDragEnter){\r
29059                 /**\r
29060                  * An empty function by default, but provided so that you can perform a custom action\r
29061                  * when the dragged item enters the drop target by providing an implementation.\r
29062                  * @param {Ext.dd.DragDrop} target The drop target\r
29063                  * @param {Event} e The event object\r
29064                  * @param {String} id The id of the dragged element\r
29065                  * @method afterDragEnter\r
29066                  */\r
29067                 this.afterDragEnter(target, e, id);\r
29068             }\r
29069         }\r
29070     },\r
29071 \r
29072     /**\r
29073      * An empty function by default, but provided so that you can perform a custom action\r
29074      * before the dragged item enters the drop target and optionally cancel the onDragEnter.\r
29075      * @param {Ext.dd.DragDrop} target The drop target\r
29076      * @param {Event} e The event object\r
29077      * @param {String} id The id of the dragged element\r
29078      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
29079      */\r
29080     beforeDragEnter : function(target, e, id){\r
29081         return true;\r
29082     },\r
29083 \r
29084     // private\r
29085     alignElWithMouse: function() {\r
29086         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);\r
29087         this.proxy.sync();\r
29088     },\r
29089 \r
29090     // private\r
29091     onDragOver : function(e, id){\r
29092         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
29093         if(this.beforeDragOver(target, e, id) !== false){\r
29094             if(target.isNotifyTarget){\r
29095                 var status = target.notifyOver(this, e, this.dragData);\r
29096                 this.proxy.setStatus(status);\r
29097             }\r
29098 \r
29099             if(this.afterDragOver){\r
29100                 /**\r
29101                  * An empty function by default, but provided so that you can perform a custom action\r
29102                  * while the dragged item is over the drop target by providing an implementation.\r
29103                  * @param {Ext.dd.DragDrop} target The drop target\r
29104                  * @param {Event} e The event object\r
29105                  * @param {String} id The id of the dragged element\r
29106                  * @method afterDragOver\r
29107                  */\r
29108                 this.afterDragOver(target, e, id);\r
29109             }\r
29110         }\r
29111     },\r
29112 \r
29113     /**\r
29114      * An empty function by default, but provided so that you can perform a custom action\r
29115      * while the dragged item is over the drop target and optionally cancel the onDragOver.\r
29116      * @param {Ext.dd.DragDrop} target The drop target\r
29117      * @param {Event} e The event object\r
29118      * @param {String} id The id of the dragged element\r
29119      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
29120      */\r
29121     beforeDragOver : function(target, e, id){\r
29122         return true;\r
29123     },\r
29124 \r
29125     // private\r
29126     onDragOut : function(e, id){\r
29127         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
29128         if(this.beforeDragOut(target, e, id) !== false){\r
29129             if(target.isNotifyTarget){\r
29130                 target.notifyOut(this, e, this.dragData);\r
29131             }\r
29132             this.proxy.reset();\r
29133             if(this.afterDragOut){\r
29134                 /**\r
29135                  * An empty function by default, but provided so that you can perform a custom action\r
29136                  * after the dragged item is dragged out of the target without dropping.\r
29137                  * @param {Ext.dd.DragDrop} target The drop target\r
29138                  * @param {Event} e The event object\r
29139                  * @param {String} id The id of the dragged element\r
29140                  * @method afterDragOut\r
29141                  */\r
29142                 this.afterDragOut(target, e, id);\r
29143             }\r
29144         }\r
29145         this.cachedTarget = null;\r
29146     },\r
29147 \r
29148     /**\r
29149      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
29150      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.\r
29151      * @param {Ext.dd.DragDrop} target The drop target\r
29152      * @param {Event} e The event object\r
29153      * @param {String} id The id of the dragged element\r
29154      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
29155      */\r
29156     beforeDragOut : function(target, e, id){\r
29157         return true;\r
29158     },\r
29159     \r
29160     // private\r
29161     onDragDrop : function(e, id){\r
29162         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
29163         if(this.beforeDragDrop(target, e, id) !== false){\r
29164             if(target.isNotifyTarget){\r
29165                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?\r
29166                     this.onValidDrop(target, e, id);\r
29167                 }else{\r
29168                     this.onInvalidDrop(target, e, id);\r
29169                 }\r
29170             }else{\r
29171                 this.onValidDrop(target, e, id);\r
29172             }\r
29173             \r
29174             if(this.afterDragDrop){\r
29175                 /**\r
29176                  * An empty function by default, but provided so that you can perform a custom action\r
29177                  * after a valid drag drop has occurred by providing an implementation.\r
29178                  * @param {Ext.dd.DragDrop} target The drop target\r
29179                  * @param {Event} e The event object\r
29180                  * @param {String} id The id of the dropped element\r
29181                  * @method afterDragDrop\r
29182                  */\r
29183                 this.afterDragDrop(target, e, id);\r
29184             }\r
29185         }\r
29186         delete this.cachedTarget;\r
29187     },\r
29188 \r
29189     /**\r
29190      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
29191      * item is dropped onto the target and optionally cancel the onDragDrop.\r
29192      * @param {Ext.dd.DragDrop} target The drop target\r
29193      * @param {Event} e The event object\r
29194      * @param {String} id The id of the dragged element\r
29195      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel\r
29196      */\r
29197     beforeDragDrop : function(target, e, id){\r
29198         return true;\r
29199     },\r
29200 \r
29201     // private\r
29202     onValidDrop : function(target, e, id){\r
29203         this.hideProxy();\r
29204         if(this.afterValidDrop){\r
29205             /**\r
29206              * An empty function by default, but provided so that you can perform a custom action\r
29207              * after a valid drop has occurred by providing an implementation.\r
29208              * @param {Object} target The target DD \r
29209              * @param {Event} e The event object\r
29210              * @param {String} id The id of the dropped element\r
29211              * @method afterInvalidDrop\r
29212              */\r
29213             this.afterValidDrop(target, e, id);\r
29214         }\r
29215     },\r
29216 \r
29217     // private\r
29218     getRepairXY : function(e, data){\r
29219         return this.el.getXY();  \r
29220     },\r
29221 \r
29222     // private\r
29223     onInvalidDrop : function(target, e, id){\r
29224         this.beforeInvalidDrop(target, e, id);\r
29225         if(this.cachedTarget){\r
29226             if(this.cachedTarget.isNotifyTarget){\r
29227                 this.cachedTarget.notifyOut(this, e, this.dragData);\r
29228             }\r
29229             this.cacheTarget = null;\r
29230         }\r
29231         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);\r
29232 \r
29233         if(this.afterInvalidDrop){\r
29234             /**\r
29235              * An empty function by default, but provided so that you can perform a custom action\r
29236              * after an invalid drop has occurred by providing an implementation.\r
29237              * @param {Event} e The event object\r
29238              * @param {String} id The id of the dropped element\r
29239              * @method afterInvalidDrop\r
29240              */\r
29241             this.afterInvalidDrop(e, id);\r
29242         }\r
29243     },\r
29244 \r
29245     // private\r
29246     afterRepair : function(){\r
29247         if(Ext.enableFx){\r
29248             this.el.highlight(this.hlColor || "c3daf9");\r
29249         }\r
29250         this.dragging = false;\r
29251     },\r
29252 \r
29253     /**\r
29254      * An empty function by default, but provided so that you can perform a custom action after an invalid\r
29255      * drop has occurred.\r
29256      * @param {Ext.dd.DragDrop} target The drop target\r
29257      * @param {Event} e The event object\r
29258      * @param {String} id The id of the dragged element\r
29259      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel\r
29260      */\r
29261     beforeInvalidDrop : function(target, e, id){\r
29262         return true;\r
29263     },\r
29264 \r
29265     // private\r
29266     handleMouseDown : function(e){\r
29267         if(this.dragging) {\r
29268             return;\r
29269         }\r
29270         var data = this.getDragData(e);\r
29271         if(data && this.onBeforeDrag(data, e) !== false){\r
29272             this.dragData = data;\r
29273             this.proxy.stop();\r
29274             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);\r
29275         } \r
29276     },\r
29277 \r
29278     /**\r
29279      * An empty function by default, but provided so that you can perform a custom action before the initial\r
29280      * drag event begins and optionally cancel it.\r
29281      * @param {Object} data An object containing arbitrary data to be shared with drop targets\r
29282      * @param {Event} e The event object\r
29283      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
29284      */\r
29285     onBeforeDrag : function(data, e){\r
29286         return true;\r
29287     },\r
29288 \r
29289     /**\r
29290      * An empty function by default, but provided so that you can perform a custom action once the initial\r
29291      * drag event has begun.  The drag cannot be canceled from this function.\r
29292      * @param {Number} x The x position of the click on the dragged object\r
29293      * @param {Number} y The y position of the click on the dragged object\r
29294      */\r
29295     onStartDrag : Ext.emptyFn,\r
29296 \r
29297     // private override\r
29298     startDrag : function(x, y){\r
29299         this.proxy.reset();\r
29300         this.dragging = true;\r
29301         this.proxy.update("");\r
29302         this.onInitDrag(x, y);\r
29303         this.proxy.show();\r
29304     },\r
29305 \r
29306     // private\r
29307     onInitDrag : function(x, y){\r
29308         var clone = this.el.dom.cloneNode(true);\r
29309         clone.id = Ext.id(); // prevent duplicate ids\r
29310         this.proxy.update(clone);\r
29311         this.onStartDrag(x, y);\r
29312         return true;\r
29313     },\r
29314 \r
29315     /**\r
29316      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}\r
29317      * @return {Ext.dd.StatusProxy} proxy The StatusProxy\r
29318      */\r
29319     getProxy : function(){\r
29320         return this.proxy;  \r
29321     },\r
29322 \r
29323     /**\r
29324      * Hides the drag source's {@link Ext.dd.StatusProxy}\r
29325      */\r
29326     hideProxy : function(){\r
29327         this.proxy.hide();  \r
29328         this.proxy.reset(true);\r
29329         this.dragging = false;\r
29330     },\r
29331 \r
29332     // private\r
29333     triggerCacheRefresh : function(){\r
29334         Ext.dd.DDM.refreshCache(this.groups);\r
29335     },\r
29336 \r
29337     // private - override to prevent hiding\r
29338     b4EndDrag: function(e) {\r
29339     },\r
29340 \r
29341     // private - override to prevent moving\r
29342     endDrag : function(e){\r
29343         this.onEndDrag(this.dragData, e);\r
29344     },\r
29345 \r
29346     // private\r
29347     onEndDrag : function(data, e){\r
29348     },\r
29349     \r
29350     // private - pin to cursor\r
29351     autoOffset : function(x, y) {\r
29352         this.setDelta(-12, -20);\r
29353     }    \r
29354 });/**\r
29355  * @class Ext.dd.DropTarget\r
29356  * @extends Ext.dd.DDTarget\r
29357  * A simple class that provides the basic implementation needed to make any element a drop target that can have\r
29358  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.\r
29359  * @constructor\r
29360  * @param {Mixed} el The container element\r
29361  * @param {Object} config\r
29362  */\r
29363 Ext.dd.DropTarget = function(el, config){\r
29364     this.el = Ext.get(el);\r
29365     \r
29366     Ext.apply(this, config);\r
29367     \r
29368     if(this.containerScroll){\r
29369         Ext.dd.ScrollManager.register(this.el);\r
29370     }\r
29371     \r
29372     Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
29373           {isTarget: true});\r
29374 \r
29375 };\r
29376 \r
29377 Ext.extend(Ext.dd.DropTarget, Ext.dd.DDTarget, {\r
29378     /**\r
29379      * @cfg {String} ddGroup\r
29380      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
29381      * interact with other drag drop objects in the same group (defaults to undefined).\r
29382      */\r
29383     /**\r
29384      * @cfg {String} overClass\r
29385      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").\r
29386      */\r
29387     /**\r
29388      * @cfg {String} dropAllowed\r
29389      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
29390      */\r
29391     dropAllowed : "x-dd-drop-ok",\r
29392     /**\r
29393      * @cfg {String} dropNotAllowed\r
29394      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
29395      */\r
29396     dropNotAllowed : "x-dd-drop-nodrop",\r
29397 \r
29398     // private\r
29399     isTarget : true,\r
29400 \r
29401     // private\r
29402     isNotifyTarget : true,\r
29403 \r
29404     /**\r
29405      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the\r
29406      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element\r
29407      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.\r
29408      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29409      * @param {Event} e The event\r
29410      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29411      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29412      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29413      */\r
29414     notifyEnter : function(dd, e, data){\r
29415         if(this.overClass){\r
29416             this.el.addClass(this.overClass);\r
29417         }\r
29418         return this.dropAllowed;\r
29419     },\r
29420 \r
29421     /**\r
29422      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.\r
29423      * This method will be called on every mouse movement while the drag source is over the drop target.\r
29424      * This default implementation simply returns the dropAllowed config value.\r
29425      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29426      * @param {Event} e The event\r
29427      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29428      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29429      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29430      */\r
29431     notifyOver : function(dd, e, data){\r
29432         return this.dropAllowed;\r
29433     },\r
29434 \r
29435     /**\r
29436      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged\r
29437      * out of the target without dropping.  This default implementation simply removes the CSS class specified by\r
29438      * overClass (if any) from the drop element.\r
29439      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29440      * @param {Event} e The event\r
29441      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29442      */\r
29443     notifyOut : function(dd, e, data){\r
29444         if(this.overClass){\r
29445             this.el.removeClass(this.overClass);\r
29446         }\r
29447     },\r
29448 \r
29449     /**\r
29450      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has\r
29451      * been dropped on it.  This method has no default implementation and returns false, so you must provide an\r
29452      * implementation that does something to process the drop event and returns true so that the drag source's\r
29453      * repair action does not run.\r
29454      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29455      * @param {Event} e The event\r
29456      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29457      * @return {Boolean} True if the drop was valid, else false\r
29458      */\r
29459     notifyDrop : function(dd, e, data){\r
29460         return false;\r
29461     }\r
29462 });/**\r
29463  * @class Ext.dd.DragZone\r
29464  * @extends Ext.dd.DragSource\r
29465  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>\r
29466  * <p>This class does not move the drag target nodes, but a proxy element which may contain\r
29467  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a\r
29468  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>\r
29469  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some\r
29470  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class\r
29471  * is the most efficient way to "activate" those nodes.</p>\r
29472  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.\r
29473  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure\r
29474  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed\r
29475  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
29476  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
29477  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following\r
29478  * technique. Knowledge of the use of the DataView is required:</p><pre><code>\r
29479 myDataView.on('render', function() {\r
29480     myDataView.dragZone = new Ext.dd.DragZone(myDataView.getEl(), {\r
29481 \r
29482 //      On receipt of a mousedown event, see if it is within a DataView node.\r
29483 //      Return a drag data object if so.\r
29484         getDragData: function(e) {\r
29485 \r
29486 //          Use the DataView's own itemSelector (a mandatory property) to\r
29487 //          test if the mousedown is within one of the DataView's nodes.\r
29488             var sourceEl = e.getTarget(myDataView.itemSelector, 10);\r
29489 \r
29490 //          If the mousedown is within a DataView node, clone the node to produce\r
29491 //          a ddel element for use by the drag proxy. Also add application data\r
29492 //          to the returned data object.\r
29493             if (sourceEl) {\r
29494                 d = sourceEl.cloneNode(true);\r
29495                 d.id = Ext.id();\r
29496                 return {\r
29497                     ddel: d,\r
29498                     sourceEl: sourceEl,\r
29499                     repairXY: Ext.fly(sourceEl).getXY(),\r
29500                     sourceStore: myDataView.store,\r
29501                     draggedRecord: v.getRecord(sourceEl)\r
29502                 }\r
29503             }\r
29504         },\r
29505 \r
29506 //      Provide coordinates for the proxy to slide back to on failed drag.\r
29507 //      This is the original XY coordinates of the draggable element captured\r
29508 //      in the getDragData method.\r
29509         getRepairXY: function() {\r
29510             return this.dragData.repairXY;\r
29511         }\r
29512     });\r
29513 });</code></pre>\r
29514  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which\r
29515  * cooperates with this DragZone.\r
29516  * @constructor\r
29517  * @param {Mixed} el The container element\r
29518  * @param {Object} config\r
29519  */\r
29520 Ext.dd.DragZone = function(el, config){\r
29521     Ext.dd.DragZone.superclass.constructor.call(this, el, config);\r
29522     if(this.containerScroll){\r
29523         Ext.dd.ScrollManager.register(this.el);\r
29524     }\r
29525 };\r
29526 \r
29527 Ext.extend(Ext.dd.DragZone, Ext.dd.DragSource, {\r
29528     /**\r
29529      * This property contains the data representing the dragged object. This data is set up by the implementation\r
29530      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain\r
29531      * any other data according to the application's needs.\r
29532      * @type Object\r
29533      * @property dragData\r
29534      */\r
29535     /**\r
29536      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager\r
29537      * for auto scrolling during drag operations.\r
29538      */\r
29539     /**\r
29540      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair\r
29541      * method after a failed drop (defaults to "c3daf9" - light blue)\r
29542      */\r
29543 \r
29544     /**\r
29545      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}\r
29546      * for a valid target to drag based on the mouse down. Override this method\r
29547      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned\r
29548      * object has a "ddel" attribute (with an HTML Element) for other functions to work.\r
29549      * @param {EventObject} e The mouse down event\r
29550      * @return {Object} The dragData\r
29551      */\r
29552     getDragData : function(e){\r
29553         return Ext.dd.Registry.getHandleFromEvent(e);\r
29554     },\r
29555     \r
29556     /**\r
29557      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the\r
29558      * this.dragData.ddel\r
29559      * @param {Number} x The x position of the click on the dragged object\r
29560      * @param {Number} y The y position of the click on the dragged object\r
29561      * @return {Boolean} true to continue the drag, false to cancel\r
29562      */\r
29563     onInitDrag : function(x, y){\r
29564         this.proxy.update(this.dragData.ddel.cloneNode(true));\r
29565         this.onStartDrag(x, y);\r
29566         return true;\r
29567     },\r
29568     \r
29569     /**\r
29570      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel \r
29571      */\r
29572     afterRepair : function(){\r
29573         if(Ext.enableFx){\r
29574             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
29575         }\r
29576         this.dragging = false;\r
29577     },\r
29578 \r
29579     /**\r
29580      * Called before a repair of an invalid drop to get the XY to animate to. By default returns\r
29581      * the XY of this.dragData.ddel\r
29582      * @param {EventObject} e The mouse up event\r
29583      * @return {Array} The xy location (e.g. [100, 200])\r
29584      */\r
29585     getRepairXY : function(e){\r
29586         return Ext.Element.fly(this.dragData.ddel).getXY();  \r
29587     }\r
29588 });/**\r
29589  * @class Ext.dd.DropZone\r
29590  * @extends Ext.dd.DropTarget\r
29591  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>\r
29592  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.\r
29593  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the\r
29594  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed\r
29595  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
29596  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
29597  * {@link Ext.DomQuery} selector.</p>\r
29598  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over\r
29599  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},\r
29600  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations\r
29601  * of these methods to provide application-specific behaviour for these events to update both\r
29602  * application state, and UI state.</p>\r
29603  * <p>For example to make a GridPanel a cooperating target with the example illustrated in\r
29604  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>\r
29605 myGridPanel.on('render', function() {\r
29606     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {\r
29607 \r
29608 //      If the mouse is over a grid row, return that node. This is\r
29609 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions\r
29610         getTargetFromEvent: function(e) {\r
29611             return e.getTarget(myGridPanel.getView().rowSelector);\r
29612         },\r
29613 \r
29614 //      On entry into a target node, highlight that node.\r
29615         onNodeEnter : function(target, dd, e, data){ \r
29616             Ext.fly(target).addClass('my-row-highlight-class');\r
29617         },\r
29618 \r
29619 //      On exit from a target node, unhighlight that node.\r
29620         onNodeOut : function(target, dd, e, data){ \r
29621             Ext.fly(target).removeClass('my-row-highlight-class');\r
29622         },\r
29623 \r
29624 //      While over a target node, return the default drop allowed class which\r
29625 //      places a "tick" icon into the drag proxy.\r
29626         onNodeOver : function(target, dd, e, data){ \r
29627             return Ext.dd.DropZone.prototype.dropAllowed;\r
29628         },\r
29629 \r
29630 //      On node drop we can interrogate the target to find the underlying\r
29631 //      application object that is the real target of the dragged data.\r
29632 //      In this case, it is a Record in the GridPanel's Store.\r
29633 //      We can use the data set up by the DragZone's getDragData method to read\r
29634 //      any data we decided to attach in the DragZone's getDragData method.\r
29635         onNodeDrop : function(target, dd, e, data){\r
29636             var rowIndex = myGridPanel.getView().findRowIndex(target);\r
29637             var r = myGridPanel.getStore().getAt(rowIndex);\r
29638             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +\r
29639                 ' on Record id ' + r.id);\r
29640             return true;\r
29641         }\r
29642     });\r
29643 }\r
29644 </code></pre>\r
29645  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which\r
29646  * cooperates with this DropZone.\r
29647  * @constructor\r
29648  * @param {Mixed} el The container element\r
29649  * @param {Object} config\r
29650  */\r
29651 Ext.dd.DropZone = function(el, config){\r
29652     Ext.dd.DropZone.superclass.constructor.call(this, el, config);\r
29653 };\r
29654 \r
29655 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {\r
29656     /**\r
29657      * Returns a custom data object associated with the DOM node that is the target of the event.  By default\r
29658      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to\r
29659      * provide your own custom lookup.\r
29660      * @param {Event} e The event\r
29661      * @return {Object} data The custom data\r
29662      */\r
29663     getTargetFromEvent : function(e){\r
29664         return Ext.dd.Registry.getTargetFromEvent(e);\r
29665     },\r
29666 \r
29667     /**\r
29668      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node\r
29669      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
29670      * This method has no default implementation and should be overridden to provide\r
29671      * node-specific processing if necessary.\r
29672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from \r
29673      * {@link #getTargetFromEvent} for this node)\r
29674      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29675      * @param {Event} e The event\r
29676      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29677      */\r
29678     onNodeEnter : function(n, dd, e, data){\r
29679         \r
29680     },\r
29681 \r
29682     /**\r
29683      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node\r
29684      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
29685      * The default implementation returns this.dropNotAllowed, so it should be\r
29686      * overridden to provide the proper feedback.\r
29687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29688      * {@link #getTargetFromEvent} for this node)\r
29689      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29690      * @param {Event} e The event\r
29691      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29692      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29693      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29694      */\r
29695     onNodeOver : function(n, dd, e, data){\r
29696         return this.dropAllowed;\r
29697     },\r
29698 \r
29699     /**\r
29700      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of\r
29701      * the drop node without dropping.  This method has no default implementation and should be overridden to provide\r
29702      * node-specific processing if necessary.\r
29703      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29704      * {@link #getTargetFromEvent} for this node)\r
29705      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29706      * @param {Event} e The event\r
29707      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29708      */\r
29709     onNodeOut : function(n, dd, e, data){\r
29710         \r
29711     },\r
29712 \r
29713     /**\r
29714      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto\r
29715      * the drop node.  The default implementation returns false, so it should be overridden to provide the\r
29716      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.\r
29717      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
29718      * {@link #getTargetFromEvent} for this node)\r
29719      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29720      * @param {Event} e The event\r
29721      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29722      * @return {Boolean} True if the drop was valid, else false\r
29723      */\r
29724     onNodeDrop : function(n, dd, e, data){\r
29725         return false;\r
29726     },\r
29727 \r
29728     /**\r
29729      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,\r
29730      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so\r
29731      * it should be overridden to provide the proper feedback if necessary.\r
29732      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29733      * @param {Event} e The event\r
29734      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29735      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29736      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29737      */\r
29738     onContainerOver : function(dd, e, data){\r
29739         return this.dropNotAllowed;\r
29740     },\r
29741 \r
29742     /**\r
29743      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,\r
29744      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be\r
29745      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to\r
29746      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.\r
29747      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29748      * @param {Event} e The event\r
29749      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29750      * @return {Boolean} True if the drop was valid, else false\r
29751      */\r
29752     onContainerDrop : function(dd, e, data){\r
29753         return false;\r
29754     },\r
29755 \r
29756     /**\r
29757      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over\r
29758      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop\r
29759      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops\r
29760      * you should override this method and provide a custom implementation.\r
29761      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29762      * @param {Event} e The event\r
29763      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29764      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29765      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29766      */\r
29767     notifyEnter : function(dd, e, data){\r
29768         return this.dropNotAllowed;\r
29769     },\r
29770 \r
29771     /**\r
29772      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.\r
29773      * This method will be called on every mouse movement while the drag source is over the drop zone.\r
29774      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically\r
29775      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits\r
29776      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a\r
29777      * registered node, it will call {@link #onContainerOver}.\r
29778      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29779      * @param {Event} e The event\r
29780      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29781      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
29782      * underlying {@link Ext.dd.StatusProxy} can be updated\r
29783      */\r
29784     notifyOver : function(dd, e, data){\r
29785         var n = this.getTargetFromEvent(e);\r
29786         if(!n){ // not over valid drop target\r
29787             if(this.lastOverNode){\r
29788                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
29789                 this.lastOverNode = null;\r
29790             }\r
29791             return this.onContainerOver(dd, e, data);\r
29792         }\r
29793         if(this.lastOverNode != n){\r
29794             if(this.lastOverNode){\r
29795                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
29796             }\r
29797             this.onNodeEnter(n, dd, e, data);\r
29798             this.lastOverNode = n;\r
29799         }\r
29800         return this.onNodeOver(n, dd, e, data);\r
29801     },\r
29802 \r
29803     /**\r
29804      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged\r
29805      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification\r
29806      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.\r
29807      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
29808      * @param {Event} e The event\r
29809      * @param {Object} data An object containing arbitrary data supplied by the drag zone\r
29810      */\r
29811     notifyOut : function(dd, e, data){\r
29812         if(this.lastOverNode){\r
29813             this.onNodeOut(this.lastOverNode, dd, e, data);\r
29814             this.lastOverNode = null;\r
29815         }\r
29816     },\r
29817 \r
29818     /**\r
29819      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has\r
29820      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there\r
29821      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,\r
29822      * otherwise it will call {@link #onContainerDrop}.\r
29823      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
29824      * @param {Event} e The event\r
29825      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
29826      * @return {Boolean} True if the drop was valid, else false\r
29827      */\r
29828     notifyDrop : function(dd, e, data){\r
29829         if(this.lastOverNode){\r
29830             this.onNodeOut(this.lastOverNode, dd, e, data);\r
29831             this.lastOverNode = null;\r
29832         }\r
29833         var n = this.getTargetFromEvent(e);\r
29834         return n ?\r
29835             this.onNodeDrop(n, dd, e, data) :\r
29836             this.onContainerDrop(dd, e, data);\r
29837     },\r
29838 \r
29839     // private\r
29840     triggerCacheRefresh : function(){\r
29841         Ext.dd.DDM.refreshCache(this.groups);\r
29842     }  \r
29843 });/**\r
29844  * @class Ext.Element\r
29845  */\r
29846 Ext.Element.addMethods({\r
29847     /**\r
29848      * Initializes a {@link Ext.dd.DD} drag drop object for this element.\r
29849      * @param {String} group The group the DD object is member of\r
29850      * @param {Object} config The DD config object\r
29851      * @param {Object} overrides An object containing methods to override/implement on the DD object\r
29852      * @return {Ext.dd.DD} The DD object\r
29853      */\r
29854     initDD : function(group, config, overrides){\r
29855         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);\r
29856         return Ext.apply(dd, overrides);\r
29857     },\r
29858 \r
29859     /**\r
29860      * Initializes a {@link Ext.dd.DDProxy} object for this element.\r
29861      * @param {String} group The group the DDProxy object is member of\r
29862      * @param {Object} config The DDProxy config object\r
29863      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object\r
29864      * @return {Ext.dd.DDProxy} The DDProxy object\r
29865      */\r
29866     initDDProxy : function(group, config, overrides){\r
29867         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);\r
29868         return Ext.apply(dd, overrides);\r
29869     },\r
29870 \r
29871     /**\r
29872      * Initializes a {@link Ext.dd.DDTarget} object for this element.\r
29873      * @param {String} group The group the DDTarget object is member of\r
29874      * @param {Object} config The DDTarget config object\r
29875      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object\r
29876      * @return {Ext.dd.DDTarget} The DDTarget object\r
29877      */\r
29878     initDDTarget : function(group, config, overrides){\r
29879         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);\r
29880         return Ext.apply(dd, overrides);\r
29881     }\r
29882 });
29883 /**
29884  * @class Ext.data.Api
29885  * @extends Object
29886  * Ext.data.Api is a singleton designed to manage the data API including methods
29887  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
29888  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
29889  * GET, POST, PUT and DELETE to CRUD actions.
29890  * @singleton
29891  */
29892 Ext.data.Api = (function() {
29893
29894     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
29895     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
29896     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
29897     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
29898     var validActions = {};
29899
29900     return {
29901         /**
29902          * Defined actions corresponding to remote actions:
29903          * <pre><code>
29904 actions: {
29905     create  : 'create',  // Text representing the remote-action to create records on server.
29906     read    : 'read',    // Text representing the remote-action to read/load data from server.
29907     update  : 'update',  // Text representing the remote-action to update records on server.
29908     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
29909 }
29910          * </code></pre>
29911          * @property actions
29912          * @type Object
29913          */
29914         actions : {
29915             create  : 'create',
29916             read    : 'read',
29917             update  : 'update',
29918             destroy : 'destroy'
29919         },
29920
29921         /**
29922          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
29923          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
29924          * Defaults to:
29925          * <pre><code>
29926 restActions : {
29927     create  : 'POST',
29928     read    : 'GET',
29929     update  : 'PUT',
29930     destroy : 'DELETE'
29931 },
29932          * </code></pre>
29933          */
29934         restActions : {
29935             create  : 'POST',
29936             read    : 'GET',
29937             update  : 'PUT',
29938             destroy : 'DELETE'
29939         },
29940
29941         /**
29942          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
29943          * @param {String} action
29944          * @param {String[]}(Optional) List of available CRUD actions.  Pass in list when executing multiple times for efficiency.
29945          * @return {Boolean}
29946          */
29947         isAction : function(action) {
29948             return (Ext.data.Api.actions[action]) ? true : false;
29949         },
29950
29951         /**
29952          * 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
29953          * 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
29954          * 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
29955          * required.  This method will cache discovered KEYS into the private validActions hash.
29956          * @param {String} name The runtime name of the action.
29957          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
29958          * @nodoc
29959          */
29960         getVerb : function(name) {
29961             if (validActions[name]) {
29962                 return validActions[name];  // <-- found in cache.  return immediately.
29963             }
29964             for (var verb in this.actions) {
29965                 if (this.actions[verb] === name) {
29966                     validActions[name] = verb;
29967                     break;
29968                 }
29969             }
29970             return (validActions[name] !== undefined) ? validActions[name] : null;
29971         },
29972
29973         /**
29974          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
29975          * otherwise returns an array of mistakes.
29976          * @return {String[]||true}
29977          */
29978         isValid : function(api){
29979             var invalid = [];
29980             var crud = this.actions; // <-- cache a copy of the actions.
29981             for (var action in api) {
29982                 if (!(action in crud)) {
29983                     invalid.push(action);
29984                 }
29985             }
29986             return (!invalid.length) ? true : invalid;
29987         },
29988
29989         /**
29990          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
29991          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
29992          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
29993          * @param {Ext.data.DataProxy} proxy
29994          * @param {String} verb
29995          * @return {Boolean}
29996          */
29997         hasUniqueUrl : function(proxy, verb) {
29998             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
29999             var unique = true;
30000             for (var action in proxy.api) {
30001                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
30002                     break;
30003                 }
30004             }
30005             return unique;
30006         },
30007
30008         /**
30009          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
30010          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
30011          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
30012          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
30013          * be used.  If the method configuration parameter is not specified, POST will be used.
30014          <pre><code>
30015 new Ext.data.HttpProxy({
30016     method: "POST",     // <-- default HTTP method when not specified.
30017     api: {
30018         create: 'create.php',
30019         load: 'read.php',
30020         save: 'save.php',
30021         destroy: 'destroy.php'
30022     }
30023 });
30024
30025 // Alternatively, one can use the object-form to specify the API
30026 new Ext.data.HttpProxy({
30027     api: {
30028         load: {url: 'read.php', method: 'GET'},
30029         create: 'create.php',
30030         destroy: 'destroy.php',
30031         save: 'update.php'
30032     }
30033 });
30034         </code></pre>
30035          *
30036          * @param {Ext.data.DataProxy} proxy
30037          */
30038         prepare : function(proxy) {
30039             if (!proxy.api) {
30040                 proxy.api = {}; // <-- No api?  create a blank one.
30041             }
30042             for (var verb in this.actions) {
30043                 var action = this.actions[verb];
30044                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
30045                 if (typeof(proxy.api[action]) == 'string') {
30046                     proxy.api[action] = {
30047                         url: proxy.api[action]
30048                     };
30049                 }
30050             }
30051         },
30052
30053         /**
30054          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
30055          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
30056          * @param {Ext.data.DataProxy} proxy
30057          */
30058         restify : function(proxy) {
30059             proxy.restful = true;
30060             for (var verb in this.restActions) {
30061                 proxy.api[this.actions[verb]].method = this.restActions[verb];
30062             }
30063             // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
30064             // to satisfy initial 3.0 final release of REST features.
30065             proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
30066                 var reader = o.reader;
30067                 var res = new Ext.data.Response({
30068                     action: action,
30069                     raw: response
30070                 });
30071
30072                 switch (response.status) {
30073                     case 200:   // standard 200 response, send control back to HttpProxy#onWrite
30074                         return true;
30075                         break;
30076                     case 201:   // entity created but no response returned
30077                         //res[reader.meta.successProperty] = true;
30078                         res.success = true;
30079                         break;
30080                     case 204:  // no-content.  Create a fake response.
30081                         //res[reader.meta.successProperty] = true;
30082                         //res[reader.meta.root] = null;
30083                         res.success = true;
30084                         res.data = null;
30085                         break;
30086                     default:
30087                         return true;
30088                         break;
30089                 }
30090                 /*
30091                 if (res[reader.meta.successProperty] === true) {
30092                     this.fireEvent("write", this, action, res[reader.meta.root], res, rs, o.request.arg);
30093                 } else {
30094                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
30095                 }
30096                 */
30097                 if (res.success === true) {
30098                     this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
30099                 } else {
30100                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
30101                 }
30102                 o.request.callback.call(o.request.scope, res.data, res, res.success);
30103
30104                 return false;   // <-- false to prevent intercepted function from running.
30105             }, proxy);
30106         }
30107     };
30108 })();
30109
30110 /**
30111  * Ext.data.Response
30112  * Experimental.  Do not use directly.
30113  */
30114 Ext.data.Response = function(params, response) {
30115     Ext.apply(this, params, {
30116         raw: response
30117     });
30118 };
30119 Ext.data.Response.prototype = {
30120     message : null,
30121     success : false,
30122     status : null,
30123     root : null,
30124     raw : null,
30125
30126     getMessage : function() {
30127         return this.message;
30128     },
30129     getSuccess : function() {
30130         return this.success;
30131     },
30132     getStatus : function() {
30133         return this.status
30134     },
30135     getRoot : function() {
30136         return this.root;
30137     },
30138     getRawResponse : function() {
30139         return this.raw;
30140     }
30141 };
30142
30143 /**
30144  * @class Ext.data.Api.Error
30145  * @extends Ext.Error
30146  * Error class for Ext.data.Api errors
30147  */
30148 Ext.data.Api.Error = Ext.extend(Ext.Error, {
30149     constructor : function(message, arg) {
30150         this.arg = arg;
30151         Ext.Error.call(this, message);
30152     },
30153     name: 'Ext.data.Api'
30154 });
30155 Ext.apply(Ext.data.Api.Error.prototype, {
30156     lang: {
30157         '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.',
30158         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
30159         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
30160         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
30161     }
30162 });
30163
30164
30165 \r
30166 /**\r
30167  * @class Ext.data.SortTypes\r
30168  * @singleton\r
30169  * Defines the default sorting (casting?) comparison functions used when sorting data.\r
30170  */\r
30171 Ext.data.SortTypes = {\r
30172     /**\r
30173      * Default sort that does nothing\r
30174      * @param {Mixed} s The value being converted\r
30175      * @return {Mixed} The comparison value\r
30176      */\r
30177     none : function(s){\r
30178         return s;\r
30179     },\r
30180     \r
30181     /**\r
30182      * The regular expression used to strip tags\r
30183      * @type {RegExp}\r
30184      * @property\r
30185      */\r
30186     stripTagsRE : /<\/?[^>]+>/gi,\r
30187     \r
30188     /**\r
30189      * Strips all HTML tags to sort on text only\r
30190      * @param {Mixed} s The value being converted\r
30191      * @return {String} The comparison value\r
30192      */\r
30193     asText : function(s){\r
30194         return String(s).replace(this.stripTagsRE, "");\r
30195     },\r
30196     \r
30197     /**\r
30198      * Strips all HTML tags to sort on text only - Case insensitive\r
30199      * @param {Mixed} s The value being converted\r
30200      * @return {String} The comparison value\r
30201      */\r
30202     asUCText : function(s){\r
30203         return String(s).toUpperCase().replace(this.stripTagsRE, "");\r
30204     },\r
30205     \r
30206     /**\r
30207      * Case insensitive string\r
30208      * @param {Mixed} s The value being converted\r
30209      * @return {String} The comparison value\r
30210      */\r
30211     asUCString : function(s) {\r
30212         return String(s).toUpperCase();\r
30213     },\r
30214     \r
30215     /**\r
30216      * Date sorting\r
30217      * @param {Mixed} s The value being converted\r
30218      * @return {Number} The comparison value\r
30219      */\r
30220     asDate : function(s) {\r
30221         if(!s){\r
30222             return 0;\r
30223         }\r
30224         if(Ext.isDate(s)){\r
30225             return s.getTime();\r
30226         }\r
30227         return Date.parse(String(s));\r
30228     },\r
30229     \r
30230     /**\r
30231      * Float sorting\r
30232      * @param {Mixed} s The value being converted\r
30233      * @return {Float} The comparison value\r
30234      */\r
30235     asFloat : function(s) {\r
30236         var val = parseFloat(String(s).replace(/,/g, ""));\r
30237         return isNaN(val) ? 0 : val;\r
30238     },\r
30239     \r
30240     /**\r
30241      * Integer sorting\r
30242      * @param {Mixed} s The value being converted\r
30243      * @return {Number} The comparison value\r
30244      */\r
30245     asInt : function(s) {\r
30246         var val = parseInt(String(s).replace(/,/g, ""), 10);\r
30247         return isNaN(val) ? 0 : val;\r
30248     }\r
30249 };/**
30250  * @class Ext.data.Record
30251  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
30252  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
30253  * to access Records cached in an {@link Ext.data.Store} object.</p>
30254  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
30255  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
30256  * objects.</p>
30257  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
30258  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
30259  * copy of the Record, and insert the new instance into the other Store.</p>
30260  * <p>When serializing a Record for submission to the server, be aware that it contains many private
30261  * properties, and also a reference to its owning Store which in turn holds references to its Records.
30262  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
30263  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
30264  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
30265  * @constructor
30266  * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
30267  * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
30268  * <p><b>The generated constructor has the same signature as this constructor.</b></p>
30269  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
30270  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
30271  * for each field will be assigned.
30272  * @param {Object} id (Optional) The id of the Record. This id should be unique, and is used by the
30273  * {@link Ext.data.Store} object which owns the Record to index its collection of Records. If
30274  * an <code>id</code> is not specified a <b><code>{@link #phantom}</code></b> Record will be created
30275  * with an {@link #Record.id automatically generated id}.
30276  */
30277 Ext.data.Record = function(data, id){
30278     // if no id, call the auto id method
30279     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
30280     this.data = data || {};
30281 };
30282
30283 /**
30284  * Generate a constructor for a specific Record layout.
30285  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
30286  * The constructor generated by this method may be used to create new Record instances. The data
30287  * object must contain properties named after the {@link Ext.data.Field field}
30288  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
30289 // create a Record constructor from a description of the fields
30290 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
30291     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
30292     {name: 'author', mapping: 'username', allowBlank: false},
30293     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
30294     {name: 'lastPost', mapping: 'post_time', type: 'date'},
30295     {name: 'lastPoster', mapping: 'user2'},
30296     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
30297     // In the simplest case, if no properties other than <tt>name</tt> are required,
30298     // a field definition may consist of just a String for the field name.
30299     'signature'
30300 ]);
30301
30302 // create Record instance
30303 var myNewRecord = new TopicRecord(
30304     {
30305         title: 'Do my job please',
30306         author: 'noobie',
30307         totalPosts: 1,
30308         lastPost: new Date(),
30309         lastPoster: 'Animal',
30310         excerpt: 'No way dude!',
30311         signature: ''
30312     },
30313     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
30314 );
30315 myStore.{@link Ext.data.Store#add add}(myNewRecord);
30316 </code></pre>
30317  * @method create
30318  * @return {function} A constructor which is used to create new Records according
30319  * to the definition. The constructor has the same signature as {@link #Record}.
30320  * @static
30321  */
30322 Ext.data.Record.create = function(o){
30323     var f = Ext.extend(Ext.data.Record, {});
30324     var p = f.prototype;
30325     p.fields = new Ext.util.MixedCollection(false, function(field){
30326         return field.name;
30327     });
30328     for(var i = 0, len = o.length; i < len; i++){
30329         p.fields.add(new Ext.data.Field(o[i]));
30330     }
30331     f.getField = function(name){
30332         return p.fields.get(name);
30333     };
30334     return f;
30335 };
30336
30337 Ext.data.Record.PREFIX = 'ext-record';
30338 Ext.data.Record.AUTO_ID = 1;
30339 Ext.data.Record.EDIT = 'edit';
30340 Ext.data.Record.REJECT = 'reject';
30341 Ext.data.Record.COMMIT = 'commit';
30342
30343
30344 /**
30345  * Generates a sequential id. This method is typically called when a record is {@link #create}d
30346  * and {@link #Record no id has been specified}. The returned id takes the form:
30347  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
30348  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
30349  * (defaults to <tt>'ext-record'</tt>)</p></li>
30350  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
30351  * (defaults to <tt>1</tt> initially)</p></li>
30352  * </ul></div>
30353  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
30354  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
30355  */
30356 Ext.data.Record.id = function(rec) {
30357     rec.phantom = true;
30358     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
30359 };
30360
30361 Ext.data.Record.prototype = {
30362     /**
30363      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
30364      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
30365      * @property fields
30366      * @type Ext.util.MixedCollection
30367      */
30368     /**
30369      * An object hash representing the data for this Record. Every field name in the Record definition
30370      * is represented by a property of that name in this object. Note that unless you specified a field
30371      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
30372      * an <tt>id</tt> property.
30373      * @property data
30374      * @type {Object}
30375      */
30376     /**
30377      * The unique ID of the Record {@link #Record as specified at construction time}.
30378      * @property id
30379      * @type {Object}
30380      */
30381     /**
30382      * Readonly flag - true if this Record has been modified.
30383      * @type Boolean
30384      */
30385     dirty : false,
30386     editing : false,
30387     error: null,
30388     /**
30389      * This object contains a key and value storing the original values of all modified
30390      * fields or is null if no fields have been modified.
30391      * @property modified
30392      * @type {Object}
30393      */
30394     modified: null,
30395     /**
30396      * <tt>false</tt> when the record does not yet exist in a server-side database (see
30397      * {@link #markDirty}).  Any record which has a real database pk set as its id property
30398      * is NOT a phantom -- it's real.
30399      * @property phantom
30400      * @type {Boolean}
30401      */
30402     phantom : false,
30403
30404     // private
30405     join : function(store){
30406         /**
30407          * The {@link Ext.data.Store} to which this Record belongs.
30408          * @property store
30409          * @type {Ext.data.Store}
30410          */
30411         this.store = store;
30412     },
30413
30414     /**
30415      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
30416      * <pre><code>
30417 // record has a field named 'firstname'
30418 var Employee = Ext.data.Record.{@link #create}([
30419     {name: 'firstname'},
30420     ...
30421 ]);
30422
30423 // update the 2nd record in the store:
30424 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
30425
30426 // set the value (shows dirty flag):
30427 rec.set('firstname', 'Betty');
30428
30429 // commit the change (removes dirty flag):
30430 rec.{@link #commit}();
30431
30432 // update the record in the store, bypass setting dirty flag,
30433 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
30434 rec.{@link #data}['firstname'] = 'Wilma'); // updates record, but not the view
30435 rec.{@link #commit}(); // updates the view
30436      * </code></pre>
30437      * <b>Notes</b>:<div class="mdetail-params"><ul>
30438      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
30439      * will execute an XHR to the server.</li>
30440      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
30441      * event firing while using set().</li>
30442      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
30443      * event fire.</li>
30444      * </ul></div>
30445      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
30446      * @param {Object} value The value to set the field to.
30447      */
30448     set : function(name, value){
30449         var isObj = (typeof value === 'object');
30450         if(!isObj && String(this.data[name]) === String(value)){
30451             return;
30452         } else if (isObj && Ext.encode(this.data[name]) === Ext.encode(value)) {
30453             return;
30454         }
30455         this.dirty = true;
30456         if(!this.modified){
30457             this.modified = {};
30458         }
30459         if(typeof this.modified[name] == 'undefined'){
30460             this.modified[name] = this.data[name];
30461         }
30462         this.data[name] = value;
30463         if(!this.editing){
30464             this.afterEdit();
30465         }
30466     },
30467
30468     // private
30469     afterEdit: function(){
30470         if(this.store){
30471             this.store.afterEdit(this);
30472         }
30473     },
30474
30475     // private
30476     afterReject: function(){
30477         if(this.store){
30478             this.store.afterReject(this);
30479         }
30480     },
30481
30482     // private
30483     afterCommit: function(){
30484         if(this.store){
30485             this.store.afterCommit(this);
30486         }
30487     },
30488
30489     /**
30490      * Get the value of the {@link Ext.data.Field#name named field}.
30491      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
30492      * @return {Object} The value of the field.
30493      */
30494     get : function(name){
30495         return this.data[name];
30496     },
30497
30498     /**
30499      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
30500      * are relayed to the containing store.
30501      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
30502      */
30503     beginEdit : function(){
30504         this.editing = true;
30505         this.modified = this.modified || {};
30506     },
30507
30508     /**
30509      * Cancels all changes made in the current edit operation.
30510      */
30511     cancelEdit : function(){
30512         this.editing = false;
30513         delete this.modified;
30514     },
30515
30516     /**
30517      * End an edit. If any data was modified, the containing store is notified
30518      * (ie, the store's <code>update</code> event will fire).
30519      */
30520     endEdit : function(){
30521         this.editing = false;
30522         if(this.dirty){
30523             this.afterEdit();
30524         }
30525     },
30526
30527     /**
30528      * Usually called by the {@link Ext.data.Store} which owns the Record.
30529      * Rejects all changes made to the Record since either creation, or the last commit operation.
30530      * Modified fields are reverted to their original values.
30531      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
30532      * to have their code notified of reject operations.</p>
30533      * @param {Boolean} silent (optional) True to skip notification of the owning
30534      * store of the change (defaults to false)
30535      */
30536     reject : function(silent){
30537         var m = this.modified;
30538         for(var n in m){
30539             if(typeof m[n] != "function"){
30540                 this.data[n] = m[n];
30541             }
30542         }
30543         this.dirty = false;
30544         delete this.modified;
30545         this.editing = false;
30546         if(silent !== true){
30547             this.afterReject();
30548         }
30549     },
30550
30551     /**
30552      * Usually called by the {@link Ext.data.Store} which owns the Record.
30553      * Commits all changes made to the Record since either creation, or the last commit operation.
30554      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
30555      * to have their code notified of commit operations.</p>
30556      * @param {Boolean} silent (optional) True to skip notification of the owning
30557      * store of the change (defaults to false)
30558      */
30559     commit : function(silent){
30560         this.dirty = false;
30561         delete this.modified;
30562         this.editing = false;
30563         if(silent !== true){
30564             this.afterCommit();
30565         }
30566     },
30567
30568     /**
30569      * Gets a hash of only the fields that have been modified since this Record was created or commited.
30570      * @return Object
30571      */
30572     getChanges : function(){
30573         var m = this.modified, cs = {};
30574         for(var n in m){
30575             if(m.hasOwnProperty(n)){
30576                 cs[n] = this.data[n];
30577             }
30578         }
30579         return cs;
30580     },
30581
30582     // private
30583     hasError : function(){
30584         return this.error !== null;
30585     },
30586
30587     // private
30588     clearError : function(){
30589         this.error = null;
30590     },
30591
30592     /**
30593      * Creates a copy of this Record.
30594      * @param {String} id (optional) A new Record id, defaults to {@link #Record.id autogenerating an id}.
30595      * Note: if an <code>id</code> is not specified the copy created will be a
30596      * <code>{@link #phantom}</code> Record.
30597      * @return {Record}
30598      */
30599     copy : function(newId) {
30600         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
30601     },
30602
30603     /**
30604      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
30605      * since the load or last commit.
30606      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
30607      * @return {Boolean}
30608      */
30609     isModified : function(fieldName){
30610         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
30611     },
30612
30613     /**
30614      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
30615      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
30616      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
30617      * @return {Boolean}
30618      */
30619     isValid : function() {
30620         return this.fields.find(function(f) {
30621             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
30622         },this) ? false : true;
30623     },
30624
30625     /**
30626      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
30627      * is used interally when adding <code>{@link #phantom}</code> records to a
30628      * {@link Ext.data.Store#writer writer enabled store}.</p>
30629      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
30630      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
30631      * have a create action composed for it during {@link Ext.data.Store#save store save}
30632      * operations.</p>
30633      */
30634     markDirty : function(){
30635         this.dirty = true;
30636         if(!this.modified){
30637             this.modified = {};
30638         }
30639         this.fields.each(function(f) {
30640             this.modified[f.name] = this.data[f.name];
30641         },this);
30642     }
30643 };/**
30644  * @class Ext.StoreMgr
30645  * @extends Ext.util.MixedCollection
30646  * The default global group of stores.
30647  * @singleton
30648  */
30649 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
30650     /**
30651      * @cfg {Object} listeners @hide
30652      */
30653
30654     /**
30655      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
30656      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
30657      * @param {Ext.data.Store} store1 A Store instance
30658      * @param {Ext.data.Store} store2 (optional)
30659      * @param {Ext.data.Store} etc... (optional)
30660      */
30661     register : function(){
30662         for(var i = 0, s; (s = arguments[i]); i++){
30663             this.add(s);
30664         }
30665     },
30666
30667     /**
30668      * Unregisters one or more Stores with the StoreMgr
30669      * @param {String/Object} id1 The id of the Store, or a Store instance
30670      * @param {String/Object} id2 (optional)
30671      * @param {String/Object} etc... (optional)
30672      */
30673     unregister : function(){
30674         for(var i = 0, s; (s = arguments[i]); i++){
30675             this.remove(this.lookup(s));
30676         }
30677     },
30678
30679     /**
30680      * Gets a registered Store by id
30681      * @param {String/Object} id The id of the Store, or a Store instance
30682      * @return {Ext.data.Store}
30683      */
30684     lookup : function(id){
30685         if(Ext.isArray(id)){
30686             var fields = ['field1'], expand = !Ext.isArray(id[0]);
30687             if(!expand){
30688                 for(var i = 2, len = id[0].length; i <= len; ++i){
30689                     fields.push('field' + i);
30690                 }
30691             }
30692             return new Ext.data.ArrayStore({
30693                 fields: fields,
30694                 data: id,
30695                 expandData: expand,
30696                 autoDestroy: true,
30697                 autoCreated: true
30698
30699             });
30700         }
30701         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
30702     },
30703
30704     // getKey implementation for MixedCollection
30705     getKey : function(o){
30706          return o.storeId;
30707     }
30708 });/**
30709  * @class Ext.data.Store
30710  * @extends Ext.util.Observable
30711  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
30712  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
30713  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
30714  * <p><u>Retrieving Data</u></p>
30715  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
30716  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
30717  * <li>{@link #data} to automatically pass in data</li>
30718  * <li>{@link #loadData} to manually pass in data</li>
30719  * </ul></div></p>
30720  * <p><u>Reading Data</u></p>
30721  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
30722  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
30723  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
30724  * object.</p>
30725  * <p><u>Store Types</u></p>
30726  * <p>There are several implementations of Store available which are customized for use with
30727  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
30728  * creates a reader commensurate to an Array data object.</p>
30729  * <pre><code>
30730 var myStore = new Ext.data.ArrayStore({
30731     fields: ['fullname', 'first'],
30732     idIndex: 0 // id for each record will be the first element
30733 });
30734  * </code></pre>
30735  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
30736  * <pre><code>
30737 // create a {@link Ext.data.Record Record} constructor:
30738 var rt = Ext.data.Record.create([
30739     {name: 'fullname'},
30740     {name: 'first'}
30741 ]);
30742 var myStore = new Ext.data.Store({
30743     // explicitly create reader
30744     reader: new Ext.data.ArrayReader(
30745         {
30746             idIndex: 0  // id for each record will be the first element
30747         },
30748         rt // recordType
30749     )
30750 });
30751  * </code></pre>
30752  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
30753  * <pre><code>
30754 var myData = [
30755     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
30756     [2, 'Barney Rubble', 'Barney']
30757 ];
30758 myStore.loadData(myData);
30759  * </code></pre>
30760  * <p>Records are cached and made available through accessor functions.  An example of adding
30761  * a record to the store:</p>
30762  * <pre><code>
30763 var defaultData = {
30764     fullname: 'Full Name',
30765     first: 'First Name'
30766 };
30767 var recId = 100; // provide unique id for the record
30768 var r = new myStore.recordType(defaultData, ++recId); // create new record
30769 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
30770  * </code></pre>
30771  * <p><u>Writing Data</u></p>
30772  * <p>And <b>new in Ext version 3</b>, use the new {@link Ext.data.DataWriter DataWriter} to create an automated, <a href="http://extjs.com/deploy/dev/examples/writer/writer.html">Writable Store</a>
30773  * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
30774  * @constructor
30775  * Creates a new Store.
30776  * @param {Object} config A config object containing the objects needed for the Store to access data,
30777  * and read the data into Records.
30778  * @xtype store
30779  */
30780 Ext.data.Store = function(config){
30781     this.data = new Ext.util.MixedCollection(false);
30782     this.data.getKey = function(o){
30783         return o.id;
30784     };
30785     /**
30786      * See the <code>{@link #baseParams corresponding configuration option}</code>
30787      * for a description of this property.
30788      * To modify this property see <code>{@link #setBaseParam}</code>.
30789      * @property
30790      */
30791     this.baseParams = {};
30792
30793     // temporary removed-records cache
30794     this.removed = [];
30795
30796     if(config && config.data){
30797         this.inlineData = config.data;
30798         delete config.data;
30799     }
30800
30801     Ext.apply(this, config);
30802
30803     this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
30804
30805     if(this.url && !this.proxy){
30806         this.proxy = new Ext.data.HttpProxy({url: this.url});
30807     }
30808     // If Store is RESTful, so too is the DataProxy
30809     if (this.restful === true && this.proxy) {
30810         // When operating RESTfully, a unique transaction is generated for each record.
30811         this.batch = false;
30812         Ext.data.Api.restify(this.proxy);
30813     }
30814
30815     if(this.reader){ // reader passed
30816         if(!this.recordType){
30817             this.recordType = this.reader.recordType;
30818         }
30819         if(this.reader.onMetaChange){
30820             //this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
30821             this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
30822         }
30823         if (this.writer) { // writer passed
30824             this.writer.meta = this.reader.meta;
30825             this.pruneModifiedRecords = true;
30826         }
30827     }
30828
30829     /**
30830      * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
30831      * {@link Ext.data.DataReader Reader}. Read-only.
30832      * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
30833      * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
30834      * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
30835      * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
30836 // create the data store
30837 var store = new Ext.data.ArrayStore({
30838     autoDestroy: true,
30839     fields: [
30840        {name: 'company'},
30841        {name: 'price', type: 'float'},
30842        {name: 'change', type: 'float'},
30843        {name: 'pctChange', type: 'float'},
30844        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
30845     ]
30846 });
30847 store.loadData(myData);
30848
30849 // create the Grid
30850 var grid = new Ext.grid.EditorGridPanel({
30851     store: store,
30852     colModel: new Ext.grid.ColumnModel({
30853         columns: [
30854             {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
30855             {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
30856             {header: 'Change', renderer: change, dataIndex: 'change'},
30857             {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
30858             {header: 'Last Updated', width: 85,
30859                 renderer: Ext.util.Format.dateRenderer('m/d/Y'),
30860                 dataIndex: 'lastChange'}
30861         ],
30862         defaults: {
30863             sortable: true,
30864             width: 75
30865         }
30866     }),
30867     autoExpandColumn: 'company', // match the id specified in the column model
30868     height:350,
30869     width:600,
30870     title:'Array Grid',
30871     tbar: [{
30872         text: 'Add Record',
30873         handler : function(){
30874             var defaultData = {
30875                 change: 0,
30876                 company: 'New Company',
30877                 lastChange: (new Date()).clearTime(),
30878                 pctChange: 0,
30879                 price: 10
30880             };
30881             var recId = 3; // provide unique id
30882             var p = new store.recordType(defaultData, recId); // create new record
30883             grid.stopEditing();
30884             store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
30885             grid.startEditing(0, 0);
30886         }
30887     }]
30888 });
30889      * </code></pre>
30890      * @property recordType
30891      * @type Function
30892      */
30893
30894     if(this.recordType){
30895         /**
30896          * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
30897          * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
30898          * @property fields
30899          * @type Ext.util.MixedCollection
30900          */
30901         this.fields = this.recordType.prototype.fields;
30902     }
30903     this.modified = [];
30904
30905     this.addEvents(
30906         /**
30907          * @event datachanged
30908          * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
30909          * widget that is using this Store as a Record cache should refresh its view.
30910          * @param {Store} this
30911          */
30912         'datachanged',
30913         /**
30914          * @event metachange
30915          * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
30916          * @param {Store} this
30917          * @param {Object} meta The JSON metadata
30918          */
30919         'metachange',
30920         /**
30921          * @event add
30922          * Fires when Records have been {@link #add}ed to the Store
30923          * @param {Store} this
30924          * @param {Ext.data.Record[]} records The array of Records added
30925          * @param {Number} index The index at which the record(s) were added
30926          */
30927         'add',
30928         /**
30929          * @event remove
30930          * Fires when a Record has been {@link #remove}d from the Store
30931          * @param {Store} this
30932          * @param {Ext.data.Record} record The Record that was removed
30933          * @param {Number} index The index at which the record was removed
30934          */
30935         'remove',
30936         /**
30937          * @event update
30938          * Fires when a Record has been updated
30939          * @param {Store} this
30940          * @param {Ext.data.Record} record The Record that was updated
30941          * @param {String} operation The update operation being performed.  Value may be one of:
30942          * <pre><code>
30943  Ext.data.Record.EDIT
30944  Ext.data.Record.REJECT
30945  Ext.data.Record.COMMIT
30946          * </code></pre>
30947          */
30948         'update',
30949         /**
30950          * @event clear
30951          * Fires when the data cache has been cleared.
30952          * @param {Store} this
30953          * @param {Record[]} The records that were cleared.
30954          */
30955         'clear',
30956         /**
30957          * @event exception
30958          * <p>Fires if an exception occurs in the Proxy during a remote request.
30959          * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
30960          * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
30961          * for additional details.
30962          * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
30963          * for description.
30964          */
30965         'exception',
30966         /**
30967          * @event beforeload
30968          * Fires before a request is made for a new data object.  If the beforeload handler returns
30969          * <tt>false</tt> the {@link #load} action will be canceled.
30970          * @param {Store} this
30971          * @param {Object} options The loading options that were specified (see {@link #load} for details)
30972          */
30973         'beforeload',
30974         /**
30975          * @event load
30976          * Fires after a new set of Records has been loaded.
30977          * @param {Store} this
30978          * @param {Ext.data.Record[]} records The Records that were loaded
30979          * @param {Object} options The loading options that were specified (see {@link #load} for details)
30980          */
30981         'load',
30982         /**
30983          * @event loadexception
30984          * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
30985          * event instead.</p>
30986          * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
30987          * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
30988          * for additional details.
30989          * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
30990          * for description.
30991          */
30992         'loadexception',
30993         /**
30994          * @event beforewrite
30995          * @param {Ext.data.Store} store
30996          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
30997          * @param {Record/Array[Record]} rs
30998          * @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)
30999          * @param {Object} arg The callback's arg object passed to the {@link #request} function
31000          */
31001         'beforewrite',
31002         /**
31003          * @event write
31004          * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
31005          * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
31006          * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
31007          * @param {Ext.data.Store} store
31008          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
31009          * @param {Object} result The 'data' picked-out out of the response for convenience.
31010          * @param {Ext.Direct.Transaction} res
31011          * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
31012          */
31013         'write'
31014     );
31015
31016     if(this.proxy){
31017         this.relayEvents(this.proxy,  ['loadexception', 'exception']);
31018     }
31019     // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
31020     if (this.writer) {
31021         this.on({
31022             scope: this,
31023             add: this.createRecords,
31024             remove: this.destroyRecord,
31025             update: this.updateRecord,
31026             clear: this.onClear
31027         });
31028     }
31029
31030     this.sortToggle = {};
31031     if(this.sortField){
31032         this.setDefaultSort(this.sortField, this.sortDir);
31033     }else if(this.sortInfo){
31034         this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
31035     }
31036
31037     Ext.data.Store.superclass.constructor.call(this);
31038
31039     if(this.id){
31040         this.storeId = this.id;
31041         delete this.id;
31042     }
31043     if(this.storeId){
31044         Ext.StoreMgr.register(this);
31045     }
31046     if(this.inlineData){
31047         this.loadData(this.inlineData);
31048         delete this.inlineData;
31049     }else if(this.autoLoad){
31050         this.load.defer(10, this, [
31051             typeof this.autoLoad == 'object' ?
31052                 this.autoLoad : undefined]);
31053     }
31054 };
31055 Ext.extend(Ext.data.Store, Ext.util.Observable, {
31056     /**
31057      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
31058      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
31059      * assignment.</p>
31060      */
31061     /**
31062      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
31063      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
31064      * Typically this option, or the <code>{@link #data}</code> option will be specified.
31065      */
31066     /**
31067      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
31068      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
31069      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
31070      * be passed to the store's {@link #load} method.
31071      */
31072     /**
31073      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
31074      * access to a data object.  See <code>{@link #url}</code>.
31075      */
31076     /**
31077      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
31078      * Typically this option, or the <code>{@link #url}</code> option will be specified.
31079      */
31080     /**
31081      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
31082      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
31083      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
31084      */
31085     /**
31086      * @cfg {Ext.data.DataWriter} writer
31087      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
31088      * to the server-side database.</p>
31089      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
31090      * events on the store are monitored in order to remotely {@link #createRecords create records},
31091      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
31092      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
31093      * <br><p>Sample implementation:
31094      * <pre><code>
31095 var writer = new {@link Ext.data.JsonWriter}({
31096     encode: true,
31097     writeAllFields: true // write all fields, not just those that changed
31098 });
31099
31100 // Typical Store collecting the Proxy, Reader and Writer together.
31101 var store = new Ext.data.Store({
31102     storeId: 'user',
31103     root: 'records',
31104     proxy: proxy,
31105     reader: reader,
31106     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
31107     paramsAsHash: true,
31108     autoSave: false    // <-- false to delay executing create, update, destroy requests
31109                         //     until specifically told to do so.
31110 });
31111      * </code></pre></p>
31112      */
31113     writer : undefined,
31114     /**
31115      * @cfg {Object} baseParams
31116      * <p>An object containing properties which are to be sent as parameters
31117      * for <i>every</i> HTTP request.</p>
31118      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
31119      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
31120      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
31121      * for more details.</p>
31122      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
31123      * method.
31124      * @property
31125      */
31126     /**
31127      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
31128      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
31129      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
31130      * For example:<pre><code>
31131 sortInfo: {
31132     field: 'fieldName',
31133     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
31134 }
31135 </code></pre>
31136      */
31137     /**
31138      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
31139      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
31140      * in place (defaults to <tt>false</tt>).
31141      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
31142      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
31143      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
31144      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
31145      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
31146      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
31147      * </ul></div></p>
31148      */
31149     remoteSort : false,
31150
31151     /**
31152      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
31153      * to is destroyed (defaults to <tt>false</tt>).
31154      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
31155      */
31156     autoDestroy : false,
31157
31158     /**
31159      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
31160      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
31161      * for the accessor method to retrieve the modified records.
31162      */
31163     pruneModifiedRecords : false,
31164
31165     /**
31166      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
31167      * for the details of what this may contain. This may be useful for accessing any params which were used
31168      * to load the current Record cache.
31169      * @property
31170      */
31171     lastOptions : null,
31172
31173     /**
31174      * @cfg {Boolean} autoSave
31175      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
31176      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
31177      * to send all modifiedRecords to the server.</p>
31178      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
31179      */
31180     autoSave : true,
31181
31182     /**
31183      * @cfg {Boolean} batch
31184      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
31185      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
31186      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
31187      * to <tt>false</tt>.</p>
31188      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
31189      * generated for each record.</p>
31190      */
31191     batch : true,
31192
31193     /**
31194      * @cfg {Boolean} restful
31195      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
31196      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
31197      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
31198      * action is described in {@link Ext.data.Api#restActions}.  For additional information
31199      * see {@link Ext.data.DataProxy#restful}.
31200      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
31201      * internally be set to <tt>false</tt>.</p>
31202      */
31203     restful: false,
31204
31205     /**
31206      * @cfg {Object} paramNames
31207      * <p>An object containing properties which specify the names of the paging and
31208      * sorting parameters passed to remote servers when loading blocks of data. By default, this
31209      * object takes the following form:</p><pre><code>
31210 {
31211     start : 'start',  // The parameter name which specifies the start row
31212     limit : 'limit',  // The parameter name which specifies number of rows to return
31213     sort : 'sort',    // The parameter name which specifies the column to sort on
31214     dir : 'dir'       // The parameter name which specifies the sort direction
31215 }
31216 </code></pre>
31217      * <p>The server must produce the requested data block upon receipt of these parameter names.
31218      * If different parameter names are required, this property can be overriden using a configuration
31219      * property.</p>
31220      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
31221      * the parameter names to use in its {@link #load requests}.
31222      */
31223     paramNames : undefined,
31224
31225     /**
31226      * @cfg {Object} defaultParamNames
31227      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
31228      * for all stores, this object should be changed on the store prototype.
31229      */
31230     defaultParamNames : {
31231         start : 'start',
31232         limit : 'limit',
31233         sort : 'sort',
31234         dir : 'dir'
31235     },
31236
31237     /**
31238      * Destroys the store.
31239      */
31240     destroy : function(){
31241         if(!this.isDestroyed){
31242             if(this.storeId){
31243                 Ext.StoreMgr.unregister(this);
31244             }
31245             this.clearData();
31246             this.data = null;
31247             Ext.destroy(this.proxy);
31248             this.reader = this.writer = null;
31249             this.purgeListeners();
31250             this.isDestroyed = true;
31251         }
31252     },
31253
31254     /**
31255      * Add Records to the Store and fires the {@link #add} event.  To add Records
31256      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
31257      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
31258      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
31259      * to add to the cache. See {@link #recordType}.
31260      */
31261     add : function(records){
31262         records = [].concat(records);
31263         if(records.length < 1){
31264             return;
31265         }
31266         for(var i = 0, len = records.length; i < len; i++){
31267             records[i].join(this);
31268         }
31269         var index = this.data.length;
31270         this.data.addAll(records);
31271         if(this.snapshot){
31272             this.snapshot.addAll(records);
31273         }
31274         this.fireEvent('add', this, records, index);
31275     },
31276
31277     /**
31278      * (Local sort only) Inserts the passed Record into the Store at the index where it
31279      * should go based on the current sort information.
31280      * @param {Ext.data.Record} record
31281      */
31282     addSorted : function(record){
31283         var index = this.findInsertIndex(record);
31284         this.insert(index, record);
31285     },
31286
31287     /**
31288      * Remove a Record from the Store and fires the {@link #remove} event.
31289      * @param {Ext.data.Record} record The Ext.data.Record object to remove from the cache.
31290      */
31291     remove : function(record){
31292         var index = this.data.indexOf(record);
31293         if(index > -1){
31294             record.join(null);
31295             this.data.removeAt(index);
31296             if(this.pruneModifiedRecords){
31297                 this.modified.remove(record);
31298             }
31299             if(this.snapshot){
31300                 this.snapshot.remove(record);
31301             }
31302             this.fireEvent('remove', this, record, index);
31303         }
31304     },
31305
31306     /**
31307      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
31308      * @param {Number} index The index of the record to remove.
31309      */
31310     removeAt : function(index){
31311         this.remove(this.getAt(index));
31312     },
31313
31314     /**
31315      * Remove all Records from the Store and fires the {@link #clear} event.
31316      */
31317     removeAll : function(){
31318         var items = [];
31319         this.each(function(rec){
31320             items.push(rec);
31321         });
31322         this.clearData();
31323         if(this.snapshot){
31324             this.snapshot.clear();
31325         }
31326         if(this.pruneModifiedRecords){
31327             this.modified = [];
31328         }
31329         this.fireEvent('clear', this, items);
31330     },
31331
31332     // private
31333     onClear: function(store, records){
31334         Ext.each(records, function(rec, index){
31335             this.destroyRecord(this, rec, index);
31336         }, this);
31337     },
31338
31339     /**
31340      * Inserts Records into the Store at the given index and fires the {@link #add} event.
31341      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
31342      * @param {Number} index The start index at which to insert the passed Records.
31343      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
31344      */
31345     insert : function(index, records){
31346         records = [].concat(records);
31347         for(var i = 0, len = records.length; i < len; i++){
31348             this.data.insert(index, records[i]);
31349             records[i].join(this);
31350         }
31351         this.fireEvent('add', this, records, index);
31352     },
31353
31354     /**
31355      * Get the index within the cache of the passed Record.
31356      * @param {Ext.data.Record} record The Ext.data.Record object to find.
31357      * @return {Number} The index of the passed Record. Returns -1 if not found.
31358      */
31359     indexOf : function(record){
31360         return this.data.indexOf(record);
31361     },
31362
31363     /**
31364      * Get the index within the cache of the Record with the passed id.
31365      * @param {String} id The id of the Record to find.
31366      * @return {Number} The index of the Record. Returns -1 if not found.
31367      */
31368     indexOfId : function(id){
31369         return this.data.indexOfKey(id);
31370     },
31371
31372     /**
31373      * Get the Record with the specified id.
31374      * @param {String} id The id of the Record to find.
31375      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
31376      */
31377     getById : function(id){
31378         return this.data.key(id);
31379     },
31380
31381     /**
31382      * Get the Record at the specified index.
31383      * @param {Number} index The index of the Record to find.
31384      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
31385      */
31386     getAt : function(index){
31387         return this.data.itemAt(index);
31388     },
31389
31390     /**
31391      * Returns a range of Records between specified indices.
31392      * @param {Number} startIndex (optional) The starting index (defaults to 0)
31393      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
31394      * @return {Ext.data.Record[]} An array of Records
31395      */
31396     getRange : function(start, end){
31397         return this.data.getRange(start, end);
31398     },
31399
31400     // private
31401     storeOptions : function(o){
31402         o = Ext.apply({}, o);
31403         delete o.callback;
31404         delete o.scope;
31405         this.lastOptions = o;
31406     },
31407
31408     // private
31409     clearData: function(){
31410         this.data.each(function(rec) {
31411             rec.join(null);
31412         });
31413         this.data.clear();
31414     },
31415
31416     /**
31417      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
31418      * <br><p>Notes:</p><div class="mdetail-params"><ul>
31419      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
31420      * loaded. To perform any post-processing where information from the load call is required, specify
31421      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
31422      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
31423      * properties in the <code>options.params</code> property to establish the initial position within the
31424      * dataset, and the number of Records to cache on each read from the Proxy.</li>
31425      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
31426      * will be automatically included with the posted parameters according to the specified
31427      * <code>{@link #paramNames}</code>.</li>
31428      * </ul></div>
31429      * @param {Object} options An object containing properties which control loading options:<ul>
31430      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
31431      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
31432      * <code>{@link #baseParams}</code> of the same name.</p>
31433      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
31434      * <li><b><tt>callback</tt></b> : Function<div class="sub-desc"><p>A function to be called after the Records
31435      * have been loaded. The <tt>callback</tt> is called after the load event and is passed the following arguments:<ul>
31436      * <li><tt>r</tt> : Ext.data.Record[]</li>
31437      * <li><tt>options</tt>: Options object from the load call</li>
31438      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div></li>
31439      * <li><b><tt>scope</tt></b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
31440      * to the Store object)</p></div></li>
31441      * <li><b><tt>add</tt></b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
31442      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
31443      * </ul>
31444      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
31445      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
31446      */
31447     load : function(options) {
31448         options = options || {};
31449         this.storeOptions(options);
31450         if(this.sortInfo && this.remoteSort){
31451             var pn = this.paramNames;
31452             options.params = options.params || {};
31453             options.params[pn.sort] = this.sortInfo.field;
31454             options.params[pn.dir] = this.sortInfo.direction;
31455         }
31456         try {
31457             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
31458         } catch(e) {
31459             this.handleException(e);
31460             return false;
31461         }
31462     },
31463
31464     /**
31465      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
31466      * Listens to 'update' event.
31467      * @param {Object} store
31468      * @param {Object} record
31469      * @param {Object} action
31470      * @private
31471      */
31472     updateRecord : function(store, record, action) {
31473         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid))) {
31474             this.save();
31475         }
31476     },
31477
31478     /**
31479      * Should not be used directly.  Store#add will call this automatically if a Writer is set
31480      * @param {Object} store
31481      * @param {Object} rs
31482      * @param {Object} index
31483      * @private
31484      */
31485     createRecords : function(store, rs, index) {
31486         for (var i = 0, len = rs.length; i < len; i++) {
31487             if (rs[i].phantom && rs[i].isValid()) {
31488                 rs[i].markDirty();  // <-- Mark new records dirty
31489                 this.modified.push(rs[i]);  // <-- add to modified
31490             }
31491         }
31492         if (this.autoSave === true) {
31493             this.save();
31494         }
31495     },
31496
31497     /**
31498      * Destroys a record or records.  Should not be used directly.  It's called by Store#remove if a Writer is set.
31499      * @param {Store} this
31500      * @param {Ext.data.Record/Ext.data.Record[]}
31501      * @param {Number} index
31502      * @private
31503      */
31504     destroyRecord : function(store, record, index) {
31505         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
31506             this.modified.remove(record);
31507         }
31508         if (!record.phantom) {
31509             this.removed.push(record);
31510
31511             // since the record has already been removed from the store but the server request has not yet been executed,
31512             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
31513             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
31514             record.lastIndex = index;
31515
31516             if (this.autoSave === true) {
31517                 this.save();
31518             }
31519         }
31520     },
31521
31522     /**
31523      * This method should generally not be used directly.  This method is called internally
31524      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
31525      * {@link #remove}, or {@link #update} events fire.
31526      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
31527      * @param {Record/Record[]} rs
31528      * @param {Object} options
31529      * @throws Error
31530      * @private
31531      */
31532     execute : function(action, rs, options) {
31533         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
31534         if (!Ext.data.Api.isAction(action)) {
31535             throw new Ext.data.Api.Error('execute', action);
31536         }
31537         // make sure options has a params key
31538         options = Ext.applyIf(options||{}, {
31539             params: {}
31540         });
31541
31542         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
31543         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
31544         var doRequest = true;
31545
31546         if (action === 'read') {
31547             Ext.applyIf(options.params, this.baseParams);
31548             doRequest = this.fireEvent('beforeload', this, options);
31549         }
31550         else {
31551             // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
31552             // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
31553             if (this.writer.listful === true && this.restful !== true) {
31554                 rs = (Ext.isArray(rs)) ? rs : [rs];
31555             }
31556             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
31557             else if (Ext.isArray(rs) && rs.length == 1) {
31558                 rs = rs.shift();
31559             }
31560             // Write the action to options.params
31561             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
31562                 this.writer.write(action, options.params, rs);
31563             }
31564         }
31565         if (doRequest !== false) {
31566             // Send request to proxy.
31567             var params = Ext.apply({}, options.params, this.baseParams);
31568             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
31569                 params.xaction = action;    // <-- really old, probaby unecessary.
31570             }
31571             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
31572             // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
31573             // the user's configured DataProxy#api
31574             this.proxy.request(Ext.data.Api.actions[action], rs, params, this.reader, this.createCallback(action, rs), this, options);
31575         }
31576         return doRequest;
31577     },
31578
31579     /**
31580      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
31581      * the configured <code>{@link #url}</code> will be used.
31582      * <pre>
31583      * change            url
31584      * ---------------   --------------------
31585      * removed records   Ext.data.Api.actions.destroy
31586      * phantom records   Ext.data.Api.actions.create
31587      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
31588      * </pre>
31589      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
31590      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
31591      */
31592     save : function() {
31593         if (!this.writer) {
31594             throw new Ext.data.Store.Error('writer-undefined');
31595         }
31596
31597         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
31598         if (this.removed.length) {
31599             this.doTransaction('destroy', this.removed);
31600         }
31601
31602         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
31603         var rs = [].concat(this.getModifiedRecords());
31604         if (!rs.length) { // Bail-out if empty...
31605             return true;
31606         }
31607
31608         // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
31609         var phantoms = [];
31610         for (var i = rs.length-1; i >= 0; i--) {
31611             if (rs[i].phantom === true) {
31612                 var rec = rs.splice(i, 1).shift();
31613                 if (rec.isValid()) {
31614                     phantoms.push(rec);
31615                 }
31616             } else if (!rs[i].isValid()) { // <-- while we're here, splice-off any !isValid real records
31617                 rs.splice(i,1);
31618             }
31619         }
31620         // If we have valid phantoms, create them...
31621         if (phantoms.length) {
31622             this.doTransaction('create', phantoms);
31623         }
31624
31625         // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
31626         if (rs.length) {
31627             this.doTransaction('update', rs);
31628         }
31629         return true;
31630     },
31631
31632     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
31633     doTransaction : function(action, rs) {
31634         function transaction(records) {
31635             try {
31636                 this.execute(action, records);
31637             } catch (e) {
31638                 this.handleException(e);
31639             }
31640         }
31641         if (this.batch === false) {
31642             for (var i = 0, len = rs.length; i < len; i++) {
31643                 transaction.call(this, rs[i]);
31644             }
31645         } else {
31646             transaction.call(this, rs);
31647         }
31648     },
31649
31650     // @private callback-handler for remote CRUD actions
31651     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
31652     createCallback : function(action, rs) {
31653         var actions = Ext.data.Api.actions;
31654         return (action == 'read') ? this.loadRecords : function(data, response, success) {
31655             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
31656             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
31657             // If success === false here, exception will have been called in DataProxy
31658             if (success === true) {
31659                 this.fireEvent('write', this, action, data, response, rs);
31660             }
31661         };
31662     },
31663
31664     // Clears records from modified array after an exception event.
31665     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
31666     // TODO remove this method?
31667     clearModified : function(rs) {
31668         if (Ext.isArray(rs)) {
31669             for (var n=rs.length-1;n>=0;n--) {
31670                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
31671             }
31672         } else {
31673             this.modified.splice(this.modified.indexOf(rs), 1);
31674         }
31675     },
31676
31677     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
31678     reMap : function(record) {
31679         if (Ext.isArray(record)) {
31680             for (var i = 0, len = record.length; i < len; i++) {
31681                 this.reMap(record[i]);
31682             }
31683         } else {
31684             delete this.data.map[record._phid];
31685             this.data.map[record.id] = record;
31686             var index = this.data.keys.indexOf(record._phid);
31687             this.data.keys.splice(index, 1, record.id);
31688             delete record._phid;
31689         }
31690     },
31691
31692     // @protected onCreateRecord proxy callback for create action
31693     onCreateRecords : function(success, rs, data) {
31694         if (success === true) {
31695             try {
31696                 this.reader.realize(rs, data);
31697                 this.reMap(rs);
31698             }
31699             catch (e) {
31700                 this.handleException(e);
31701                 if (Ext.isArray(rs)) {
31702                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
31703                     this.onCreateRecords(success, rs, data);
31704                 }
31705             }
31706         }
31707     },
31708
31709     // @protected, onUpdateRecords proxy callback for update action
31710     onUpdateRecords : function(success, rs, data) {
31711         if (success === true) {
31712             try {
31713                 this.reader.update(rs, data);
31714             } catch (e) {
31715                 this.handleException(e);
31716                 if (Ext.isArray(rs)) {
31717                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
31718                     this.onUpdateRecords(success, rs, data);
31719                 }
31720             }
31721         }
31722     },
31723
31724     // @protected onDestroyRecords proxy callback for destroy action
31725     onDestroyRecords : function(success, rs, data) {
31726         // splice each rec out of this.removed
31727         rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
31728         for (var i=0,len=rs.length;i<len;i++) {
31729             this.removed.splice(this.removed.indexOf(rs[i]), 1);
31730         }
31731         if (success === false) {
31732             // put records back into store if remote destroy fails.
31733             // @TODO: Might want to let developer decide.
31734             for (i=rs.length-1;i>=0;i--) {
31735                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
31736             }
31737         }
31738     },
31739
31740     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
31741     handleException : function(e) {
31742         // @see core/Error.js
31743         Ext.handleError(e);
31744     },
31745
31746     /**
31747      * <p>Reloads the Record cache from the configured Proxy using the configured {@link Ext.data.Reader Reader} and
31748      * the options from the last load operation performed.</p>
31749      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
31750      * @param {Object} options (optional) An <tt>Object</tt> containing {@link #load loading options} which may
31751      * override the options used in the last {@link #load} operation. See {@link #load} for details (defaults to
31752      * <tt>null</tt>, in which case the {@link #lastOptions} are used).
31753      */
31754     reload : function(options){
31755         this.load(Ext.applyIf(options||{}, this.lastOptions));
31756     },
31757
31758     // private
31759     // Called as a callback by the Reader during a load operation.
31760     loadRecords : function(o, options, success){
31761         if(!o || success === false){
31762             if(success !== false){
31763                 this.fireEvent('load', this, [], options);
31764             }
31765             if(options.callback){
31766                 options.callback.call(options.scope || this, [], options, false, o);
31767             }
31768             return;
31769         }
31770         var r = o.records, t = o.totalRecords || r.length;
31771         if(!options || options.add !== true){
31772             if(this.pruneModifiedRecords){
31773                 this.modified = [];
31774             }
31775             for(var i = 0, len = r.length; i < len; i++){
31776                 r[i].join(this);
31777             }
31778             if(this.snapshot){
31779                 this.data = this.snapshot;
31780                 delete this.snapshot;
31781             }
31782             this.clearData();
31783             this.data.addAll(r);
31784             this.totalLength = t;
31785             this.applySort();
31786             this.fireEvent('datachanged', this);
31787         }else{
31788             this.totalLength = Math.max(t, this.data.length+r.length);
31789             this.add(r);
31790         }
31791         this.fireEvent('load', this, r, options);
31792         if(options.callback){
31793             options.callback.call(options.scope || this, r, options, true);
31794         }
31795     },
31796
31797     /**
31798      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
31799      * which understands the format of the data must have been configured in the constructor.
31800      * @param {Object} data The data block from which to read the Records.  The format of the data expected
31801      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
31802      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
31803      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
31804      * the existing cache.
31805      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
31806      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
31807      * new, unique ids will be added.
31808      */
31809     loadData : function(o, append){
31810         var r = this.reader.readRecords(o);
31811         this.loadRecords(r, {add: append}, true);
31812     },
31813
31814     /**
31815      * Gets the number of cached records.
31816      * <p>If using paging, this may not be the total size of the dataset. If the data object
31817      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
31818      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
31819      * @return {Number} The number of Records in the Store's cache.
31820      */
31821     getCount : function(){
31822         return this.data.length || 0;
31823     },
31824
31825     /**
31826      * Gets the total number of records in the dataset as returned by the server.
31827      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
31828      * must contain the dataset size. For remote data sources, the value for this property
31829      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
31830      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
31831      * <b>Note</b>: see the Important note in {@link #load}.</p>
31832      * @return {Number} The number of Records as specified in the data object passed to the Reader
31833      * by the Proxy.
31834      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
31835      */
31836     getTotalCount : function(){
31837         return this.totalLength || 0;
31838     },
31839
31840     /**
31841      * Returns an object describing the current sort state of this Store.
31842      * @return {Object} The sort state of the Store. An object with two properties:<ul>
31843      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
31844      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
31845      * </ul>
31846      * See <tt>{@link #sortInfo}</tt> for additional details.
31847      */
31848     getSortState : function(){
31849         return this.sortInfo;
31850     },
31851
31852     // private
31853     applySort : function(){
31854         if(this.sortInfo && !this.remoteSort){
31855             var s = this.sortInfo, f = s.field;
31856             this.sortData(f, s.direction);
31857         }
31858     },
31859
31860     // private
31861     sortData : function(f, direction){
31862         direction = direction || 'ASC';
31863         var st = this.fields.get(f).sortType;
31864         var fn = function(r1, r2){
31865             var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
31866             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
31867         };
31868         this.data.sort(direction, fn);
31869         if(this.snapshot && this.snapshot != this.data){
31870             this.snapshot.sort(direction, fn);
31871         }
31872     },
31873
31874     /**
31875      * Sets the default sort column and order to be used by the next {@link #load} operation.
31876      * @param {String} fieldName The name of the field to sort by.
31877      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
31878      */
31879     setDefaultSort : function(field, dir){
31880         dir = dir ? dir.toUpperCase() : 'ASC';
31881         this.sortInfo = {field: field, direction: dir};
31882         this.sortToggle[field] = dir;
31883     },
31884
31885     /**
31886      * Sort the Records.
31887      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
31888      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
31889      * @param {String} fieldName The name of the field to sort by.
31890      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
31891      */
31892     sort : function(fieldName, dir){
31893         var f = this.fields.get(fieldName);
31894         if(!f){
31895             return false;
31896         }
31897         if(!dir){
31898             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
31899                 dir = (this.sortToggle[f.name] || 'ASC').toggle('ASC', 'DESC');
31900             }else{
31901                 dir = f.sortDir;
31902             }
31903         }
31904         var st = (this.sortToggle) ? this.sortToggle[f.name] : null;
31905         var si = (this.sortInfo) ? this.sortInfo : null;
31906
31907         this.sortToggle[f.name] = dir;
31908         this.sortInfo = {field: f.name, direction: dir};
31909         if(!this.remoteSort){
31910             this.applySort();
31911             this.fireEvent('datachanged', this);
31912         }else{
31913             if (!this.load(this.lastOptions)) {
31914                 if (st) {
31915                     this.sortToggle[f.name] = st;
31916                 }
31917                 if (si) {
31918                     this.sortInfo = si;
31919                 }
31920             }
31921         }
31922     },
31923
31924     /**
31925      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
31926      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
31927      * Returning <tt>false</tt> aborts and exits the iteration.
31928      * @param {Object} scope (optional) The scope in which to call the function (defaults to the {@link Ext.data.Record Record}).
31929      */
31930     each : function(fn, scope){
31931         this.data.each(fn, scope);
31932     },
31933
31934     /**
31935      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
31936      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
31937      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
31938      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
31939      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
31940      * modifications.  To obtain modified fields within a modified record see
31941      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
31942      */
31943     getModifiedRecords : function(){
31944         return this.modified;
31945     },
31946
31947     // private
31948     createFilterFn : function(property, value, anyMatch, caseSensitive){
31949         if(Ext.isEmpty(value, false)){
31950             return false;
31951         }
31952         value = this.data.createValueMatcher(value, anyMatch, caseSensitive);
31953         return function(r){
31954             return value.test(r.data[property]);
31955         };
31956     },
31957
31958     /**
31959      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
31960      * and <tt>end</tt> and returns the result.
31961      * @param {String} property A field in each record
31962      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
31963      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
31964      * @return {Number} The sum
31965      */
31966     sum : function(property, start, end){
31967         var rs = this.data.items, v = 0;
31968         start = start || 0;
31969         end = (end || end === 0) ? end : rs.length-1;
31970
31971         for(var i = start; i <= end; i++){
31972             v += (rs[i].data[property] || 0);
31973         }
31974         return v;
31975     },
31976
31977     /**
31978      * Filter the {@link Ext.data.Record records} by a specified property.
31979      * @param {String} field A field on your records
31980      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
31981      * against the field.
31982      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
31983      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
31984      */
31985     filter : function(property, value, anyMatch, caseSensitive){
31986         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
31987         return fn ? this.filterBy(fn) : this.clearFilter();
31988     },
31989
31990     /**
31991      * Filter by a function. The specified function will be called for each
31992      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
31993      * otherwise it is filtered out.
31994      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
31995      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
31996      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
31997      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
31998      * </ul>
31999      * @param {Object} scope (optional) The scope of the function (defaults to this)
32000      */
32001     filterBy : function(fn, scope){
32002         this.snapshot = this.snapshot || this.data;
32003         this.data = this.queryBy(fn, scope||this);
32004         this.fireEvent('datachanged', this);
32005     },
32006
32007     /**
32008      * Query the records by a specified property.
32009      * @param {String} field A field on your records
32010      * @param {String/RegExp} value Either a string that the field
32011      * should begin with, or a RegExp to test against the field.
32012      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
32013      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
32014      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
32015      */
32016     query : function(property, value, anyMatch, caseSensitive){
32017         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
32018         return fn ? this.queryBy(fn) : this.data.clone();
32019     },
32020
32021     /**
32022      * Query the cached records in this Store using a filtering function. The specified function
32023      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
32024      * included in the results.
32025      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
32026      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
32027      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
32028      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
32029      * </ul>
32030      * @param {Object} scope (optional) The scope of the function (defaults to this)
32031      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
32032      **/
32033     queryBy : function(fn, scope){
32034         var data = this.snapshot || this.data;
32035         return data.filterBy(fn, scope||this);
32036     },
32037
32038     /**
32039      * Finds the index of the first matching record in this store by a specific property/value.
32040      * @param {String} property A property on your objects
32041      * @param {String/RegExp} value Either a string that the property value
32042      * should begin with, or a RegExp to test against the property.
32043      * @param {Number} startIndex (optional) The index to start searching at
32044      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
32045      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
32046      * @return {Number} The matched index or -1
32047      */
32048     find : function(property, value, start, anyMatch, caseSensitive){
32049         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
32050         return fn ? this.data.findIndexBy(fn, null, start) : -1;
32051     },
32052
32053     /**
32054      * Finds the index of the first matching record in this store by a specific property/value.
32055      * @param {String} property A property on your objects
32056      * @param {String/RegExp} value The value to match against
32057      * @param {Number} startIndex (optional) The index to start searching at
32058      * @return {Number} The matched index or -1
32059      */
32060     findExact: function(property, value, start){
32061         return this.data.findIndexBy(function(rec){
32062             return rec.get(property) === value;
32063         }, this, start);
32064     },
32065
32066     /**
32067      * Find the index of the first matching Record in this Store by a function.
32068      * If the function returns <tt>true</tt> it is considered a match.
32069      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
32070      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
32071      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
32072      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
32073      * </ul>
32074      * @param {Object} scope (optional) The scope of the function (defaults to this)
32075      * @param {Number} startIndex (optional) The index to start searching at
32076      * @return {Number} The matched index or -1
32077      */
32078     findBy : function(fn, scope, start){
32079         return this.data.findIndexBy(fn, scope, start);
32080     },
32081
32082     /**
32083      * Collects unique values for a particular dataIndex from this store.
32084      * @param {String} dataIndex The property to collect
32085      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
32086      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
32087      * @return {Array} An array of the unique values
32088      **/
32089     collect : function(dataIndex, allowNull, bypassFilter){
32090         var d = (bypassFilter === true && this.snapshot) ?
32091                 this.snapshot.items : this.data.items;
32092         var v, sv, r = [], l = {};
32093         for(var i = 0, len = d.length; i < len; i++){
32094             v = d[i].data[dataIndex];
32095             sv = String(v);
32096             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
32097                 l[sv] = true;
32098                 r[r.length] = v;
32099             }
32100         }
32101         return r;
32102     },
32103
32104     /**
32105      * Revert to a view of the Record cache with no filtering applied.
32106      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
32107      * {@link #datachanged} event.
32108      */
32109     clearFilter : function(suppressEvent){
32110         if(this.isFiltered()){
32111             this.data = this.snapshot;
32112             delete this.snapshot;
32113             if(suppressEvent !== true){
32114                 this.fireEvent('datachanged', this);
32115             }
32116         }
32117     },
32118
32119     /**
32120      * Returns true if this store is currently filtered
32121      * @return {Boolean}
32122      */
32123     isFiltered : function(){
32124         return this.snapshot && this.snapshot != this.data;
32125     },
32126
32127     // private
32128     afterEdit : function(record){
32129         if(this.modified.indexOf(record) == -1){
32130             this.modified.push(record);
32131         }
32132         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
32133     },
32134
32135     // private
32136     afterReject : function(record){
32137         this.modified.remove(record);
32138         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
32139     },
32140
32141     // private
32142     afterCommit : function(record){
32143         this.modified.remove(record);
32144         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
32145     },
32146
32147     /**
32148      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
32149      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
32150      * Ext.data.Record.COMMIT.
32151      */
32152     commitChanges : function(){
32153         var m = this.modified.slice(0);
32154         this.modified = [];
32155         for(var i = 0, len = m.length; i < len; i++){
32156             m[i].commit();
32157         }
32158     },
32159
32160     /**
32161      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
32162      */
32163     rejectChanges : function(){
32164         var m = this.modified.slice(0);
32165         this.modified = [];
32166         for(var i = 0, len = m.length; i < len; i++){
32167             m[i].reject();
32168         }
32169         var m = this.removed.slice(0).reverse();
32170         this.removed = [];
32171         for(var i = 0, len = m.length; i < len; i++){
32172             this.insert(m[i].lastIndex||0, m[i]);
32173             m[i].reject();
32174         }
32175     },
32176
32177     // private
32178     onMetaChange : function(meta){
32179         this.recordType = this.reader.recordType;
32180         this.fields = this.recordType.prototype.fields;
32181         delete this.snapshot;
32182         if(this.reader.meta.sortInfo){
32183             this.sortInfo = this.reader.meta.sortInfo;
32184         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
32185             delete this.sortInfo;
32186         }
32187         if(this.writer){
32188             this.writer.meta = this.reader.meta;
32189         }
32190         this.modified = [];
32191         this.fireEvent('metachange', this, this.reader.meta);
32192     },
32193
32194     // private
32195     findInsertIndex : function(record){
32196         this.suspendEvents();
32197         var data = this.data.clone();
32198         this.data.add(record);
32199         this.applySort();
32200         var index = this.data.indexOf(record);
32201         this.data = data;
32202         this.resumeEvents();
32203         return index;
32204     },
32205
32206     /**
32207      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
32208 myStore.setBaseParam('foo', {bar:3});
32209 </code></pre>
32210      * @param {String} name Name of the property to assign
32211      * @param {Mixed} value Value to assign the <tt>name</tt>d property
32212      **/
32213     setBaseParam : function (name, value){
32214         this.baseParams = this.baseParams || {};
32215         this.baseParams[name] = value;
32216     }
32217 });
32218
32219 Ext.reg('store', Ext.data.Store);
32220
32221 /**
32222  * @class Ext.data.Store.Error
32223  * @extends Ext.Error
32224  * Store Error extension.
32225  * @param {String} name
32226  */
32227 Ext.data.Store.Error = Ext.extend(Ext.Error, {
32228     name: 'Ext.data.Store'
32229 });
32230 Ext.apply(Ext.data.Store.Error.prototype, {
32231     lang: {
32232         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
32233     }
32234 });
32235
32236 /**
32237  * @class Ext.data.Field
32238  * <p>This class encapsulates the field definition information specified in the field definition objects
32239  * passed to {@link Ext.data.Record#create}.</p>
32240  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
32241  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
32242  */
32243 Ext.data.Field = function(config){
32244     if(typeof config == "string"){
32245         config = {name: config};
32246     }
32247     Ext.apply(this, config);
32248
32249     if(!this.type){
32250         this.type = "auto";
32251     }
32252
32253     var st = Ext.data.SortTypes;
32254     // named sortTypes are supported, here we look them up
32255     if(typeof this.sortType == "string"){
32256         this.sortType = st[this.sortType];
32257     }
32258
32259     // set default sortType for strings and dates
32260     if(!this.sortType){
32261         switch(this.type){
32262             case "string":
32263                 this.sortType = st.asUCString;
32264                 break;
32265             case "date":
32266                 this.sortType = st.asDate;
32267                 break;
32268             default:
32269                 this.sortType = st.none;
32270         }
32271     }
32272
32273     // define once
32274     var stripRe = /[\$,%]/g;
32275
32276     // prebuilt conversion function for this field, instead of
32277     // switching every time we're reading a value
32278     if(!this.convert){
32279         var cv, dateFormat = this.dateFormat;
32280         switch(this.type){
32281             case "":
32282             case "auto":
32283             case undefined:
32284                 cv = function(v){ return v; };
32285                 break;
32286             case "string":
32287                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
32288                 break;
32289             case "int":
32290                 cv = function(v){
32291                     return v !== undefined && v !== null && v !== '' ?
32292                            parseInt(String(v).replace(stripRe, ""), 10) : '';
32293                     };
32294                 break;
32295             case "float":
32296                 cv = function(v){
32297                     return v !== undefined && v !== null && v !== '' ?
32298                            parseFloat(String(v).replace(stripRe, ""), 10) : '';
32299                     };
32300                 break;
32301             case "bool":
32302             case "boolean":
32303                 cv = function(v){ return v === true || v === "true" || v == 1; };
32304                 break;
32305             case "date":
32306                 cv = function(v){
32307                     if(!v){
32308                         return '';
32309                     }
32310                     if(Ext.isDate(v)){
32311                         return v;
32312                     }
32313                     if(dateFormat){
32314                         if(dateFormat == "timestamp"){
32315                             return new Date(v*1000);
32316                         }
32317                         if(dateFormat == "time"){
32318                             return new Date(parseInt(v, 10));
32319                         }
32320                         return Date.parseDate(v, dateFormat);
32321                     }
32322                     var parsed = Date.parse(v);
32323                     return parsed ? new Date(parsed) : null;
32324                 };
32325              break;
32326
32327         }
32328         this.convert = cv;
32329     }
32330 };
32331
32332 Ext.data.Field.prototype = {
32333     /**
32334      * @cfg {String} name
32335      * The name by which the field is referenced within the Record. This is referenced by, for example,
32336      * the <tt>dataIndex</tt> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
32337      * <p>Note: In the simplest case, if no properties other than <tt>name</tt> are required, a field
32338      * definition may consist of just a String for the field name.</p>
32339      */
32340     /**
32341      * @cfg {String} type
32342      * (Optional) The data type for conversion to displayable value if <tt>{@link Ext.data.Field#convert convert}</tt>
32343      * has not been specified. Possible values are
32344      * <div class="mdetail-params"><ul>
32345      * <li>auto (Default, implies no conversion)</li>
32346      * <li>string</li>
32347      * <li>int</li>
32348      * <li>float</li>
32349      * <li>boolean</li>
32350      * <li>date</li></ul></div>
32351      */
32352     /**
32353      * @cfg {Function} convert
32354      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
32355      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
32356      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
32357      * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
32358      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
32359      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
32360      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
32361      * </ul></div>
32362      * <pre><code>
32363 // example of convert function
32364 function fullName(v, record){
32365     return record.name.last + ', ' + record.name.first;
32366 }
32367
32368 function location(v, record){
32369     return !record.city ? '' : (record.city + ', ' + record.state);
32370 }
32371
32372 var Dude = Ext.data.Record.create([
32373     {name: 'fullname',  convert: fullName},
32374     {name: 'firstname', mapping: 'name.first'},
32375     {name: 'lastname',  mapping: 'name.last'},
32376     {name: 'city', defaultValue: 'homeless'},
32377     'state',
32378     {name: 'location',  convert: location}
32379 ]);
32380
32381 // create the data store
32382 var store = new Ext.data.Store({
32383     reader: new Ext.data.JsonReader(
32384         {
32385             idProperty: 'key',
32386             root: 'daRoot',  
32387             totalProperty: 'total'
32388         },
32389         Dude  // recordType
32390     )
32391 });
32392
32393 var myData = [
32394     { key: 1,
32395       name: { first: 'Fat',    last:  'Albert' }
32396       // notice no city, state provided in data object
32397     },
32398     { key: 2,
32399       name: { first: 'Barney', last:  'Rubble' },
32400       city: 'Bedrock', state: 'Stoneridge'
32401     },
32402     { key: 3,
32403       name: { first: 'Cliff',  last:  'Claven' },
32404       city: 'Boston',  state: 'MA'
32405     }
32406 ];
32407      * </code></pre>
32408      */
32409     /**
32410      * @cfg {String} dateFormat
32411      * (Optional) A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
32412      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
32413      * javascript millisecond timestamp.
32414      */
32415     dateFormat: null,
32416     /**
32417      * @cfg {Mixed} defaultValue
32418      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
32419      * when the item referenced by the <tt>{@link Ext.data.Field#mapping mapping}</tt> does not exist in the data
32420      * object (i.e. undefined). (defaults to "")
32421      */
32422     defaultValue: "",
32423     /**
32424      * @cfg {String/Number} mapping
32425      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
32426      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
32427      * If the path expression is the same as the field name, the mapping may be omitted.</p>
32428      * <p>The form of the mapping expression depends on the Reader being used.</p>
32429      * <div class="mdetail-params"><ul>
32430      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
32431      * 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>
32432      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
32433      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
32434      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
32435      * of the field's value. Defaults to the field specification's Array position.</div></li>
32436      * </ul></div>
32437      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
32438      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
32439      * return the desired data.</p>
32440      */
32441     mapping: null,
32442     /**
32443      * @cfg {Function} sortType
32444      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
32445      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
32446      * sort example:<pre><code>
32447 // current sort     after sort we want
32448 // +-+------+          +-+------+
32449 // |1|First |          |1|First |
32450 // |2|Last  |          |3|Second|
32451 // |3|Second|          |2|Last  |
32452 // +-+------+          +-+------+
32453
32454 sortType: function(value) {
32455    switch (value.toLowerCase()) // native toLowerCase():
32456    {
32457       case 'first': return 1;
32458       case 'second': return 2;
32459       default: return 3;
32460    }
32461 }
32462      * </code></pre>
32463      */
32464     sortType : null,
32465     /**
32466      * @cfg {String} sortDir
32467      * (Optional) Initial direction to sort (<tt>"ASC"</tt> or  <tt>"DESC"</tt>).  Defaults to
32468      * <tt>"ASC"</tt>.
32469      */
32470     sortDir : "ASC",
32471         /**
32472          * @cfg {Boolean} allowBlank 
32473          * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <tt>true</tt>.
32474          * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
32475          * to evaluate to <tt>false</tt>.
32476          */
32477         allowBlank : true
32478 };/**\r
32479  * @class Ext.data.DataReader\r
32480  * Abstract base class for reading structured data from a data source and converting\r
32481  * it into an object containing {@link Ext.data.Record} objects and metadata for use\r
32482  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not\r
32483  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},\r
32484  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.\r
32485  * @constructor Create a new DataReader\r
32486  * @param {Object} meta Metadata configuration options (implementation-specific).\r
32487  * @param {Array/Object} recordType\r
32488  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
32489  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
32490  * constructor created using {@link Ext.data.Record#create}.</p>\r
32491  */\r
32492 Ext.data.DataReader = function(meta, recordType){\r
32493     /**\r
32494      * This DataReader's configured metadata as passed to the constructor.\r
32495      * @type Mixed\r
32496      * @property meta\r
32497      */\r
32498     this.meta = meta;\r
32499     /**\r
32500      * @cfg {Array/Object} fields\r
32501      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
32502      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
32503      * constructor created from {@link Ext.data.Record#create}.</p>\r
32504      */\r
32505     this.recordType = Ext.isArray(recordType) ?\r
32506         Ext.data.Record.create(recordType) : recordType;\r
32507
32508     // if recordType defined make sure extraction functions are defined\r
32509     if (this.recordType){\r
32510         this.buildExtractors();\r
32511     }
32512 };\r
32513 \r
32514 Ext.data.DataReader.prototype = {\r
32515     /**\r
32516      * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.\r
32517      */\r
32518     /**\r
32519      * Abstract method created in extension's buildExtractors impl.\r
32520      */\r
32521     getTotal: Ext.emptyFn,\r
32522     /**\r
32523      * Abstract method created in extension's buildExtractors impl.\r
32524      */\r
32525     getRoot: Ext.emptyFn,\r
32526     /**\r
32527      * Abstract method created in extension's buildExtractors impl.\r
32528      */\r
32529     getMessage: Ext.emptyFn,\r
32530     /**\r
32531      * Abstract method created in extension's buildExtractors impl.\r
32532      */\r
32533     getSuccess: Ext.emptyFn,\r
32534     /**\r
32535      * Abstract method created in extension's buildExtractors impl.\r
32536      */\r
32537     getId: Ext.emptyFn,\r
32538     /**\r
32539      * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
32540      */\r
32541     buildExtractors : Ext.emptyFn,\r
32542     /**\r
32543      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
32544      */\r
32545     extractData : Ext.emptyFn,\r
32546     /**\r
32547      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
32548      */\r
32549     extractValues : Ext.emptyFn,\r
32550 \r
32551     /**\r
32552      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.\r
32553      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming\r
32554      * data from server will be merged with the data in the local record.\r
32555      * In addition, you <b>must</b> return record-data from the server in the same order received.\r
32556      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.\r
32557      * @param {Record/Record[]} record The phantom record to be realized.\r
32558      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.\r
32559      */\r
32560     realize: function(rs, data){\r
32561         if (Ext.isArray(rs)) {\r
32562             for (var i = rs.length - 1; i >= 0; i--) {\r
32563                 // recurse\r
32564                 if (Ext.isArray(data)) {\r
32565                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
32566                 }\r
32567                 else {\r
32568                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.\r
32569                     // the else clause below will detect !this.isData and throw exception.\r
32570                     this.realize(rs.splice(i,1).shift(), data);\r
32571                 }\r
32572             }\r
32573         }\r
32574         else {\r
32575             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
32576             if (Ext.isArray(data) && data.length == 1) {\r
32577                 data = data.shift();\r
32578             }\r
32579             if (!this.isData(data)) {\r
32580                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.\r
32581                 //rs.commit();\r
32582                 throw new Ext.data.DataReader.Error('realize', rs);\r
32583             }\r
32584             rs.phantom = false; // <-- That's what it's all about\r
32585             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords\r
32586             rs.id = this.getId(data);\r
32587             rs.data = data;\r
32588             rs.commit();\r
32589         }\r
32590     },\r
32591 \r
32592     /**\r
32593      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.\r
32594      * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in\r
32595      * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be\r
32596      * suppressed as the record receives fresh new data-hash\r
32597      * @param {Record/Record[]} rs\r
32598      * @param {Object/Object[]} data\r
32599      */\r
32600     update : function(rs, data) {\r
32601         if (Ext.isArray(rs)) {\r
32602             for (var i=rs.length-1; i >= 0; i--) {\r
32603                 if (Ext.isArray(data)) {\r
32604                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
32605                 }\r
32606                 else {\r
32607                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.\r
32608                     // the else clause below will detect !this.isData and throw exception.\r
32609                     this.update(rs.splice(i,1).shift(), data);\r
32610                 }\r
32611             }\r
32612         }\r
32613         else {\r
32614             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
32615             if (Ext.isArray(data) && data.length == 1) {\r
32616                 data = data.shift();\r
32617             }\r
32618             if (this.isData(data)) {\r
32619                 rs.data = Ext.apply(rs.data, data);\r
32620             }\r
32621             rs.commit();\r
32622         }\r
32623     },\r
32624 \r
32625     /**\r
32626      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key\r
32627      * corresponding to idProperty defined in your DataReader config containing non-empty pk.\r
32628      * @param {Object} data\r
32629      * @return {Boolean}\r
32630      */\r
32631     isData : function(data) {\r
32632         return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;\r
32633     },\r
32634 \r
32635     // private function a store will createSequence upon\r
32636     onMetaChange : function(meta){\r
32637         delete this.ef;\r
32638         this.meta = meta;\r
32639         this.recordType = Ext.data.Record.create(meta.fields);\r
32640         this.buildExtractors();\r
32641     }\r
32642 };\r
32643 \r
32644 /**\r
32645  * @class Ext.data.DataReader.Error\r
32646  * @extends Ext.Error\r
32647  * General error class for Ext.data.DataReader\r
32648  */\r
32649 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {\r
32650     constructor : function(message, arg) {\r
32651         this.arg = arg;\r
32652         Ext.Error.call(this, message);\r
32653     },\r
32654     name: 'Ext.data.DataReader'\r
32655 });\r
32656 Ext.apply(Ext.data.DataReader.Error.prototype, {\r
32657     lang : {\r
32658         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",\r
32659         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",\r
32660         'invalid-response': "#readResponse received an invalid response from the server."\r
32661     }\r
32662 });\r
32663 \r
32664 \r
32665 /**\r
32666  * Ext.data.Response\r
32667  * A generic response class to normalize response-handling internally to the framework.\r
32668  * TODO move to own file, add to jsb.\r
32669  */\r
32670 Ext.data.Response = function(params) {\r
32671     Ext.apply(this, params);\r
32672 };\r
32673 Ext.data.Response.prototype = {\r
32674     /**\r
32675      * @property {String} action {@link Ext.data.Api#actions}\r
32676      */\r
32677     action: undefined,\r
32678     /**\r
32679      * @property {Boolean} success\r
32680      */\r
32681     success : undefined,\r
32682     /**\r
32683      * @property {String} message\r
32684      */\r
32685     message : undefined,\r
32686     /**\r
32687      * @property {Array/Object} data\r
32688      */\r
32689     data: undefined,\r
32690     /**\r
32691      * @property {Object} raw The raw response returned from server-code\r
32692      */\r
32693     raw: undefined,\r
32694     /**\r
32695      * @property {Ext.data.Record/Ext.data.Record[]} record(s) related to the Request action\r
32696      */\r
32697     records: undefined\r
32698 }\r
32699 /**
32700  * @class Ext.data.DataWriter
32701  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
32702  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
32703  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
32704  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
32705  * and should not be created directly. For existing implementations, see
32706  * {@link Ext.data.JsonWriter}.</p>
32707  * <p>Creating a writer is simple:</p>
32708  * <pre><code>
32709 var writer = new Ext.data.JsonWriter();
32710  * </code></pre>
32711  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
32712  * <pre><code>
32713 // Create a standard HttpProxy instance.
32714 var proxy = new Ext.data.HttpProxy({
32715     url: 'app.php/users'
32716 });
32717  * </code></pre>
32718  * <p>For finer grained control, the proxy may also be configured with an <code>api</code>:</p>
32719  * <pre><code>
32720 // Use the api specification
32721 var proxy = new Ext.data.HttpProxy({
32722     api: {
32723         read    : 'app.php/users/read',
32724         create  : 'app.php/users/create',
32725         update  : 'app.php/users/update',
32726         destroy : 'app.php/users/destroy'
32727     }
32728 });
32729  * </code></pre>
32730  * <p>Creating a Writer enabled store:</p>
32731  * <pre><code>
32732 var store = new Ext.data.Store({
32733     proxy: proxy,
32734     reader: reader,
32735     writer: writer
32736 });
32737  * </code></pre>
32738  * @constructor Create a new DataWriter
32739  * @param {Object} meta Metadata configuration options (implementation-specific)
32740  * @param {Object} recordType Either an Array of field definition objects as specified
32741  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
32742  * using {@link Ext.data.Record#create}.
32743  */
32744 Ext.data.DataWriter = function(config){
32745     Ext.apply(this, config);
32746 };
32747 Ext.data.DataWriter.prototype = {
32748
32749     /**
32750      * @cfg {Boolean} writeAllFields
32751      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
32752      * record -- not just those that changed.
32753      * <tt>false</tt> to have DataWriter only request modified fields from a record.
32754      */
32755     writeAllFields : false,
32756     /**
32757      * @cfg {Boolean} listful
32758      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
32759      * even when acting upon a single record.
32760      */
32761     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
32762
32763     /**
32764      * Writes data in preparation for server-write action.  Simply proxies to DataWriter#update, DataWriter#create
32765      * DataWriter#destroy.
32766      * @param {String} action [CREATE|UPDATE|DESTROY]
32767      * @param {Object} params The params-hash to write-to
32768      * @param {Record/Record[]} rs The recordset write.
32769      */
32770     write : function(action, params, rs) {
32771         var data    = [],
32772         renderer    = action + 'Record';
32773         // TODO implement @cfg listful here
32774         if (Ext.isArray(rs)) {
32775             Ext.each(rs, function(rec){
32776                 data.push(this[renderer](rec));
32777             }, this);
32778         }
32779         else if (rs instanceof Ext.data.Record) {
32780             data = this[renderer](rs);
32781         }
32782         this.render(action, rs, params, data);
32783     },
32784
32785     /**
32786      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
32787      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
32788      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
32789      * @param {Record[]} rs Store recordset
32790      * @param {Object} params Http params to be sent to server.
32791      * @param {Object} data object populated according to DataReader meta-data.
32792      */
32793     render : Ext.emptyFn,
32794
32795     /**
32796      * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
32797      * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
32798      */
32799     updateRecord : Ext.emptyFn,
32800
32801     /**
32802      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
32803      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
32804      */
32805     createRecord : Ext.emptyFn,
32806
32807     /**
32808      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
32809      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
32810      */
32811     destroyRecord : Ext.emptyFn,
32812
32813     /**
32814      * Converts a Record to a hash.
32815      * @param {Record}
32816      * @private
32817      */
32818     toHash : function(rec) {
32819         var map = rec.fields.map,
32820             data = {},
32821             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
32822             m;
32823         Ext.iterate(raw, function(prop, value){
32824             if((m = map[prop])){
32825                 data[m.mapping ? m.mapping : m.name] = value;
32826             }
32827         });
32828         // we don't want to write Ext auto-generated id to hash.  Careful not to remove it on Models not having auto-increment pk though.
32829         // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
32830         // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
32831         if (rec.phantom) {
32832             if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
32833                 delete data[this.meta.idProperty];
32834             }
32835         } else {
32836             data[this.meta.idProperty] = rec.id
32837         }
32838         return data;
32839     }
32840 };/**\r
32841  * @class Ext.data.DataProxy\r
32842  * @extends Ext.util.Observable\r
32843  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.\r
32844  * This class is intended to be extended and should not be created directly. For existing implementations,\r
32845  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and\r
32846  * {@link Ext.data.MemoryProxy}.</p>\r
32847  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}\r
32848  * (of the appropriate type which knows how to parse the data object) to provide a block of\r
32849  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>\r
32850  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the\r
32851  * config object to an {@link Ext.data.Connection}.</p>\r
32852  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the\r
32853  * <code>load</code> method (deprecated). See\r
32854  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or\r
32855  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>\r
32856  * <p><b><u>Example 1</u></b></p>\r
32857  * <pre><code>\r
32858 proxy: new Ext.data.ScriptTagProxy({\r
32859     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'\r
32860 }),\r
32861  * </code></pre>\r
32862  * <p><b><u>Example 2</u></b></p>\r
32863  * <pre><code>\r
32864 proxy : new Ext.data.HttpProxy({\r
32865     {@link Ext.data.Connection#method method}: 'GET',\r
32866     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,\r
32867     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}\r
32868     {@link #api}: {\r
32869         // all actions except the following will use above url\r
32870         create  : 'local/new.php',\r
32871         update  : 'local/update.php'\r
32872     }\r
32873 }),\r
32874  * </code></pre>\r
32875  */\r
32876 Ext.data.DataProxy = function(conn){\r
32877     // make sure we have a config object here to support ux proxies.\r
32878     // All proxies should now send config into superclass constructor.\r
32879     conn = conn || {};\r
32880 \r
32881     // This line caused a bug when people use custom Connection object having its own request method.\r
32882     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config\r
32883     //Ext.applyIf(this, conn);\r
32884 \r
32885     this.api     = conn.api;\r
32886     this.url     = conn.url;\r
32887     this.restful = conn.restful;\r
32888     this.listeners = conn.listeners;\r
32889 \r
32890     // deprecated\r
32891     this.prettyUrls = conn.prettyUrls;\r
32892 \r
32893     /**\r
32894      * @cfg {Object} api\r
32895      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".\r
32896      * Defaults to:<pre><code>\r
32897 api: {\r
32898     read    : undefined,\r
32899     create  : undefined,\r
32900     update  : undefined,\r
32901     destroy : undefined\r
32902 }\r
32903      * </code></pre>\r
32904      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>\r
32905      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the\r
32906      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>\r
32907      * <p>For example:</p>\r
32908      * <pre><code>\r
32909 api: {\r
32910     load :    '/controller/load',\r
32911     create :  '/controller/new',  // Server MUST return idProperty of new record\r
32912     save :    '/controller/update',\r
32913     destroy : '/controller/destroy_action'\r
32914 }\r
32915 \r
32916 // Alternatively, one can use the object-form to specify each API-action\r
32917 api: {\r
32918     load: {url: 'read.php', method: 'GET'},\r
32919     create: 'create.php',\r
32920     destroy: 'destroy.php',\r
32921     save: 'update.php'\r
32922 }\r
32923      * </code></pre>\r
32924      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request\r
32925      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>\r
32926      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API\r
32927      * property should be modified before the action is requested using the corresponding before\r
32928      * action event.  For example to modify the URL associated with the load action:\r
32929      * <pre><code>\r
32930 // modify the url for the action\r
32931 myStore.on({\r
32932     beforeload: {\r
32933         fn: function (store, options) {\r
32934             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.\r
32935             store.proxy.setUrl('changed1.php');\r
32936 \r
32937             // set optional second parameter to true to make this URL change\r
32938             // permanent, applying this URL for all subsequent requests.\r
32939             store.proxy.setUrl('changed1.php', true);\r
32940 \r
32941             // Altering the proxy API should be done using the public\r
32942             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.\r
32943             store.proxy.setApi('read', 'changed2.php');\r
32944 \r
32945             // Or set the entire API with a config-object.\r
32946             // When using the config-object option, you must redefine the <b>entire</b>\r
32947             // API -- not just a specific action of it.\r
32948             store.proxy.setApi({\r
32949                 read    : 'changed_read.php',\r
32950                 create  : 'changed_create.php',\r
32951                 update  : 'changed_update.php',\r
32952                 destroy : 'changed_destroy.php'\r
32953             });\r
32954         }\r
32955     }\r
32956 });\r
32957      * </code></pre>\r
32958      * </p>\r
32959      */\r
32960 \r
32961     this.addEvents(\r
32962         /**\r
32963          * @event exception\r
32964          * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed\r
32965          * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},\r
32966          * so any Store instance may observe this event.</p>\r
32967          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
32968          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of exception events from <b>all</b>\r
32969          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
32970          * <p>This event can be fired for one of two reasons:</p>\r
32971          * <div class="mdetail-params"><ul>\r
32972          * <li>remote-request <b>failed</b> : <div class="sub-desc">\r
32973          * The server did not return status === 200.\r
32974          * </div></li>\r
32975          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">\r
32976          * The remote-request succeeded but the reader could not read the response.\r
32977          * This means the server returned data, but the configured Reader threw an\r
32978          * error while reading the response.  In this case, this event will be\r
32979          * raised and the caught error will be passed along into this event.\r
32980          * </div></li>\r
32981          * </ul></div>\r
32982          * <br><p>This event fires with two different contexts based upon the 2nd\r
32983          * parameter <tt>type [remote|response]</tt>.  The first four parameters\r
32984          * are identical between the two contexts -- only the final two parameters\r
32985          * differ.</p>\r
32986          * @param {DataProxy} this The proxy that sent the request\r
32987          * @param {String} type\r
32988          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>\r
32989          * <div class="mdetail-params"><ul>\r
32990          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
32991          * <p>An <b>invalid</b> response from the server was returned: either 404,\r
32992          * 500 or the response meta-data does not match that defined in the DataReader\r
32993          * (e.g.: root, idProperty, successProperty).</p>\r
32994          * </div></li>\r
32995          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
32996          * <p>A <b>valid</b> response was returned from the server having\r
32997          * successProperty === false.  This response might contain an error-message\r
32998          * sent from the server.  For example, the user may have failed\r
32999          * authentication/authorization or a database validation error occurred.</p>\r
33000          * </div></li>\r
33001          * </ul></div>\r
33002          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.\r
33003          * @param {Object} options The options for the action that were specified in the {@link #request}.\r
33004          * @param {Object} response\r
33005          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
33006          * <div class="mdetail-params"><ul>\r
33007          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
33008          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>\r
33009          * </div></li>\r
33010          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
33011          * <p>The decoded response object sent from the server.</p>\r
33012          * </div></li>\r
33013          * </ul></div>\r
33014          * @param {Mixed} arg\r
33015          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
33016          * <div class="mdetail-params"><ul>\r
33017          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">\r
33018          * <p>The JavaScript Error object caught if the configured Reader could not read the data.\r
33019          * If the remote request returns success===false, this parameter will be null.</p>\r
33020          * </div></li>\r
33021          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">\r
33022          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action\r
33023          * (Ext.data.Api.actions.create|update|destroy).</p>\r
33024          * </div></li>\r
33025          * </ul></div>\r
33026          */\r
33027         'exception',\r
33028         /**\r
33029          * @event beforeload\r
33030          * Fires before a request to retrieve a data object.\r
33031          * @param {DataProxy} this The proxy for the request\r
33032          * @param {Object} params The params object passed to the {@link #request} function\r
33033          */\r
33034         'beforeload',\r
33035         /**\r
33036          * @event load\r
33037          * Fires before the load method's callback is called.\r
33038          * @param {DataProxy} this The proxy for the request\r
33039          * @param {Object} o The request transaction object\r
33040          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
33041          */\r
33042         'load',\r
33043         /**\r
33044          * @event loadexception\r
33045          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event\r
33046          * varies depending on the proxy, use the catch-all {@link #exception} event instead.\r
33047          * This event will fire in addition to the {@link #exception} event.</p>\r
33048          * @param {misc} misc See {@link #exception}.\r
33049          * @deprecated\r
33050          */\r
33051         'loadexception',\r
33052         /**\r
33053          * @event beforewrite\r
33054          * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>\r
33055          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
33056          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>\r
33057          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
33058          * @param {DataProxy} this The proxy for the request\r
33059          * @param {String} action [Ext.data.Api.actions.create|update|destroy]\r
33060          * @param {Record/Array[Record]} rs The Record(s) to create|update|destroy.\r
33061          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.\r
33062          */\r
33063         'beforewrite',\r
33064         /**\r
33065          * @event write\r
33066          * <p>Fires before the request-callback is called</p>\r
33067          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
33068          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>\r
33069          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
33070          * @param {DataProxy} this The proxy that sent the request\r
33071          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]\r
33072          * @param {Object} data The data object extracted from the server-response\r
33073          * @param {Object} response The decoded response from server\r
33074          * @param {Record/Record{}} rs The records from Store\r
33075          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
33076          */\r
33077         'write'\r
33078     );\r
33079     Ext.data.DataProxy.superclass.constructor.call(this);\r
33080 \r
33081     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.\r
33082     try {\r
33083         Ext.data.Api.prepare(this);\r
33084     } catch (e) {\r
33085         if (e instanceof Ext.data.Api.Error) {\r
33086             e.toConsole();\r
33087         }\r
33088     }\r
33089     // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening\r
33090     Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);\r
33091 };\r
33092 \r
33093 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {\r
33094     /**\r
33095      * @cfg {Boolean} restful\r
33096      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>\r
33097      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the\r
33098      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the\r
33099      * Store is RESTful, there is no need to set this option on the proxy.</p>\r
33100      * <br><p>RESTful implementations enable the serverside framework to automatically route\r
33101      * actions sent to one url based upon the HTTP method, for example:\r
33102      * <pre><code>\r
33103 store: new Ext.data.Store({\r
33104     restful: true,\r
33105     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users\r
33106     ...\r
33107 )}\r
33108      * </code></pre>\r
33109      * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,\r
33110      * all requests will be marshalled to a single RESTful url (/users) so the serverside\r
33111      * framework can inspect the HTTP Method and act accordingly:\r
33112      * <pre>\r
33113 <u>Method</u>   <u>url</u>        <u>action</u>\r
33114 POST     /users     create\r
33115 GET      /users     read\r
33116 PUT      /users/23  update\r
33117 DESTROY  /users/23  delete\r
33118      * </pre></p>\r
33119      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's\r
33120      * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,\r
33121      * Merb and Django) support segment based urls where the segments in the URL follow the\r
33122      * Model-View-Controller approach:<pre><code>\r
33123      * someSite.com/controller/action/id\r
33124      * </code></pre>\r
33125      * Where the segments in the url are typically:<div class="mdetail-params"><ul>\r
33126      * <li>The first segment : represents the controller class that should be invoked.</li>\r
33127      * <li>The second segment : represents the class function, or method, that should be called.</li>\r
33128      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>\r
33129      * </ul></div></p>\r
33130      * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>\r
33131      */\r
33132     restful: false,\r
33133 \r
33134     /**\r
33135      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>\r
33136      * <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
33137 proxy.setApi({\r
33138     read    : '/users/read',\r
33139     create  : '/users/create',\r
33140     update  : '/users/update',\r
33141     destroy : '/users/destroy'\r
33142 });\r
33143 </code></pre>\r
33144      * <p>If called with two parameters, the first parameter should be a string specifying the API action to\r
33145      * 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
33146 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');\r
33147 </code></pre>\r
33148      * @param {String/Object} api An API specification object, or the name of an action.\r
33149      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.\r
33150      */\r
33151     setApi : function() {\r
33152         if (arguments.length == 1) {\r
33153             var valid = Ext.data.Api.isValid(arguments[0]);\r
33154             if (valid === true) {\r
33155                 this.api = arguments[0];\r
33156             }\r
33157             else {\r
33158                 throw new Ext.data.Api.Error('invalid', valid);\r
33159             }\r
33160         }\r
33161         else if (arguments.length == 2) {\r
33162             if (!Ext.data.Api.isAction(arguments[0])) {\r
33163                 throw new Ext.data.Api.Error('invalid', arguments[0]);\r
33164             }\r
33165             this.api[arguments[0]] = arguments[1];\r
33166         }\r
33167         Ext.data.Api.prepare(this);\r
33168     },\r
33169 \r
33170     /**\r
33171      * Returns true if the specified action is defined as a unique action in the api-config.\r
33172      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined\r
33173      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to\r
33174      * the corresponding code for CRUD action.\r
33175      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action\r
33176      * @return {Boolean}\r
33177      */\r
33178     isApiAction : function(action) {\r
33179         return (this.api[action]) ? true : false;\r
33180     },\r
33181 \r
33182     /**\r
33183      * All proxy actions are executed through this method.  Automatically fires the "before" + action event\r
33184      * @param {String} action Name of the action\r
33185      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'\r
33186      * @param {Object} params\r
33187      * @param {Ext.data.DataReader} reader\r
33188      * @param {Function} callback\r
33189      * @param {Object} scope Scope with which to call the callback (defaults to the Proxy object)\r
33190      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.\r
33191      */\r
33192     request : function(action, rs, params, reader, callback, scope, options) {\r
33193         if (!this.api[action] && !this.load) {\r
33194             throw new Ext.data.DataProxy.Error('action-undefined', action);\r
33195         }\r
33196         params = params || {};\r
33197         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {\r
33198             this.doRequest.apply(this, arguments);\r
33199         }\r
33200         else {\r
33201             callback.call(scope || this, null, options, false);\r
33202         }\r
33203     },\r
33204 \r
33205 \r
33206     /**\r
33207      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.\r
33208      * @deprecated\r
33209      * @param {Object} params\r
33210      * @param {Object} reader\r
33211      * @param {Object} callback\r
33212      * @param {Object} scope\r
33213      * @param {Object} arg\r
33214      */\r
33215     load : null,\r
33216 \r
33217     /**\r
33218      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses\r
33219      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},\r
33220      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).\r
33221      */\r
33222     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
33223         // default implementation of doRequest for backwards compatibility with 2.0 proxies.\r
33224         // If we're executing here, the action is probably "load".\r
33225         // Call with the pre-3.0 method signature.\r
33226         this.load(params, reader, callback, scope, options);\r
33227     },\r
33228 \r
33229     /**\r
33230      * buildUrl\r
33231      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,\r
33232      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an\r
33233      * instance of Ext.data.Record rather than an Array of them.\r
33234      * @param {String} action The api action being executed [read|create|update|destroy]\r
33235      * @param {Ext.data.Record/Array[Ext.data.Record]} The record or Array of Records being acted upon.\r
33236      * @return {String} url\r
33237      * @private\r
33238      */\r
33239     buildUrl : function(action, record) {\r
33240         record = record || null;\r
33241         var url = (this.api[action]) ? this.api[action].url : this.url;\r
33242         if (!url) {\r
33243             throw new Ext.data.Api.Error('invalid-url', action);\r
33244         }\r
33245 \r
33246         // look for urls having "provides" suffix (from Rails/Merb and others...),\r
33247         // e.g.: /users.json, /users.xml, etc\r
33248         // with restful routes, we need urls like:\r
33249         // PUT /users/1.json\r
33250         // DELETE /users/1.json\r
33251         var format = null;\r
33252         var m = url.match(/(.*)(\.json|\.xml|\.html)$/);\r
33253         if (m) {\r
33254             format = m[2];  // eg ".json"\r
33255             url = m[1];     // eg: "/users"\r
33256         }\r
33257         // prettyUrls is deprectated in favor of restful-config\r
33258         if ((this.prettyUrls === true || this.restful === true) && record instanceof Ext.data.Record && !record.phantom) {\r
33259             url += '/' + record.id;\r
33260         }\r
33261         if (format) {   // <-- append the request format if exists (ie: /users/update/69[.json])\r
33262             url += format;\r
33263         }\r
33264         return url;\r
33265     },\r
33266 \r
33267     /**\r
33268      * Destroys the proxy by purging any event listeners and cancelling any active requests.\r
33269      */\r
33270     destroy: function(){\r
33271         this.purgeListeners();\r
33272     }\r
33273 });\r
33274 \r
33275 // Apply the Observable prototype to the DataProxy class so that proxy instances can relay their\r
33276 // events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.\r
33277 Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);\r
33278 Ext.util.Observable.call(Ext.data.DataProxy);\r
33279 \r
33280 /**\r
33281  * @class Ext.data.DataProxy.Error\r
33282  * @extends Ext.Error\r
33283  * DataProxy Error extension.\r
33284  * constructor\r
33285  * @param {String} name\r
33286  * @param {Record/Array[Record]/Array}\r
33287  */\r
33288 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {\r
33289     constructor : function(message, arg) {\r
33290         this.arg = arg;\r
33291         Ext.Error.call(this, message);\r
33292     },\r
33293     name: 'Ext.data.DataProxy'\r
33294 });\r
33295 Ext.apply(Ext.data.DataProxy.Error.prototype, {\r
33296     lang: {\r
33297         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",\r
33298         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'\r
33299     }\r
33300 });\r
33301 \r
33302 \r
33303 /**\r
33304  * @class Ext.data.ScriptTagProxy\r
33305  * @extends Ext.data.DataProxy\r
33306  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain\r
33307  * other than the originating domain of the running page.<br>\r
33308  * <p>\r
33309  * <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
33310  * of the running page, you must use this class, rather than HttpProxy.</b><br>\r
33311  * <p>\r
33312  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript\r
33313  * source code because it is used as the source inside a &lt;script> tag.<br>\r
33314  * <p>\r
33315  * In order for the browser to process the returned data, the server must wrap the data object\r
33316  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.\r
33317  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy\r
33318  * depending on whether the callback name was passed:\r
33319  * <p>\r
33320  * <pre><code>\r
33321 boolean scriptTag = false;\r
33322 String cb = request.getParameter("callback");\r
33323 if (cb != null) {\r
33324     scriptTag = true;\r
33325     response.setContentType("text/javascript");\r
33326 } else {\r
33327     response.setContentType("application/x-json");\r
33328 }\r
33329 Writer out = response.getWriter();\r
33330 if (scriptTag) {\r
33331     out.write(cb + "(");\r
33332 }\r
33333 out.print(dataBlock.toJsonString());\r
33334 if (scriptTag) {\r
33335     out.write(");");\r
33336 }\r
33337 </code></pre>\r
33338  *\r
33339  * @constructor\r
33340  * @param {Object} config A configuration object.\r
33341  */\r
33342 Ext.data.ScriptTagProxy = function(config){\r
33343     Ext.apply(this, config);\r
33344 \r
33345     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);\r
33346 \r
33347     this.head = document.getElementsByTagName("head")[0];\r
33348 \r
33349     /**\r
33350      * @event loadexception\r
33351      * <b>Deprecated</b> in favor of 'exception' event.\r
33352      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:\r
33353      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit\r
33354      * specified by {@link #timeout}.  In this case, this event will be raised and the\r
33355      * fourth parameter (read error) will be null.</li>\r
33356      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned\r
33357      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be\r
33358      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>\r
33359      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly\r
33360      * on any Store instance.\r
33361      * @param {Object} this\r
33362      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load\r
33363      * call timed out, this parameter will be null.\r
33364      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
33365      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.\r
33366      * If the remote request returns success: false, this parameter will be null.\r
33367      */\r
33368 };\r
33369 \r
33370 Ext.data.ScriptTagProxy.TRANS_ID = 1000;\r
33371 \r
33372 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {\r
33373     /**\r
33374      * @cfg {String} url The URL from which to request the data object.\r
33375      */\r
33376     /**\r
33377      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.\r
33378      */\r
33379     timeout : 30000,\r
33380     /**\r
33381      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells\r
33382      * the server the name of the callback function set up by the load call to process the returned data object.\r
33383      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate\r
33384      * javascript output which calls this named function passing the data object as its only parameter.\r
33385      */\r
33386     callbackParam : "callback",\r
33387     /**\r
33388      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter\r
33389      * name to the request.\r
33390      */\r
33391     nocache : true,\r
33392 \r
33393     /**\r
33394      * HttpProxy implementation of DataProxy#doRequest\r
33395      * @param {String} action\r
33396      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null\r
33397      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
33398      * for the request to the remote server.\r
33399      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
33400      * object into a block of Ext.data.Records.\r
33401      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
33402      * The function must be passed <ul>\r
33403      * <li>The Record block object</li>\r
33404      * <li>The "arg" argument from the load function</li>\r
33405      * <li>A boolean success indicator</li>\r
33406      * </ul>\r
33407      * @param {Object} scope The scope in which to call the callback\r
33408      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
33409      */\r
33410     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
33411         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));\r
33412 \r
33413         var url = this.buildUrl(action, rs);\r
33414         if (!url) {\r
33415             throw new Ext.data.Api.Error('invalid-url', url);\r
33416         }\r
33417         url = Ext.urlAppend(url, p);\r
33418 \r
33419         if(this.nocache){\r
33420             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));\r
33421         }\r
33422         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;\r
33423         var trans = {\r
33424             id : transId,\r
33425             action: action,\r
33426             cb : "stcCallback"+transId,\r
33427             scriptId : "stcScript"+transId,\r
33428             params : params,\r
33429             arg : arg,\r
33430             url : url,\r
33431             callback : callback,\r
33432             scope : scope,\r
33433             reader : reader\r
33434         };\r
33435         window[trans.cb] = this.createCallback(action, rs, trans);\r
33436         url += String.format("&{0}={1}", this.callbackParam, trans.cb);\r
33437         if(this.autoAbort !== false){\r
33438             this.abort();\r
33439         }\r
33440 \r
33441         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);\r
33442 \r
33443         var script = document.createElement("script");\r
33444         script.setAttribute("src", url);\r
33445         script.setAttribute("type", "text/javascript");\r
33446         script.setAttribute("id", trans.scriptId);\r
33447         this.head.appendChild(script);\r
33448 \r
33449         this.trans = trans;\r
33450     },\r
33451 \r
33452     // @private createCallback\r
33453     createCallback : function(action, rs, trans) {\r
33454         var self = this;\r
33455         return function(res) {\r
33456             self.trans = false;\r
33457             self.destroyTrans(trans, true);\r
33458             if (action === Ext.data.Api.actions.read) {\r
33459                 self.onRead.call(self, action, trans, res);\r
33460             } else {\r
33461                 self.onWrite.call(self, action, trans, res, rs);\r
33462             }\r
33463         };\r
33464     },\r
33465     /**\r
33466      * Callback for read actions\r
33467      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
33468      * @param {Object} trans The request transaction object\r
33469      * @param {Object} res The server response\r
33470      * @private\r
33471      */\r
33472     onRead : function(action, trans, res) {\r
33473         var result;\r
33474         try {\r
33475             result = trans.reader.readRecords(res);\r
33476         }catch(e){\r
33477             // @deprecated: fire loadexception\r
33478             this.fireEvent("loadexception", this, trans, res, e);\r
33479 \r
33480             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
33481             trans.callback.call(trans.scope||window, null, trans.arg, false);\r
33482             return;\r
33483         }\r
33484         if (result.success === false) {\r
33485             // @deprecated: fire old loadexception for backwards-compat.\r
33486             this.fireEvent('loadexception', this, trans, res);\r
33487 \r
33488             this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
33489         } else {\r
33490             this.fireEvent("load", this, res, trans.arg);\r
33491         }\r
33492         trans.callback.call(trans.scope||window, result, trans.arg, result.success);\r
33493     },\r
33494     /**\r
33495      * Callback for write actions\r
33496      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
33497      * @param {Object} trans The request transaction object\r
33498      * @param {Object} res The server response\r
33499      * @private\r
33500      */\r
33501     onWrite : function(action, trans, response, rs) {\r
33502         var reader = trans.reader;\r
33503         try {\r
33504             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.\r
33505             var res = reader.readResponse(action, response);\r
33506         } catch (e) {\r
33507             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
33508             trans.callback.call(trans.scope||window, null, res, false);\r
33509             return;\r
33510         }\r
33511         if(!res.success === true){\r
33512             this.fireEvent('exception', this, 'remote', action, trans, res, rs);\r
33513             trans.callback.call(trans.scope||window, null, res, false);\r
33514             return;\r
33515         }\r
33516         this.fireEvent("write", this, action, res.data, res, rs, trans.arg );\r
33517         trans.callback.call(trans.scope||window, res.data, res, true);\r
33518     },\r
33519 \r
33520     // private\r
33521     isLoading : function(){\r
33522         return this.trans ? true : false;\r
33523     },\r
33524 \r
33525     /**\r
33526      * Abort the current server request.\r
33527      */\r
33528     abort : function(){\r
33529         if(this.isLoading()){\r
33530             this.destroyTrans(this.trans);\r
33531         }\r
33532     },\r
33533 \r
33534     // private\r
33535     destroyTrans : function(trans, isLoaded){\r
33536         this.head.removeChild(document.getElementById(trans.scriptId));\r
33537         clearTimeout(trans.timeoutId);\r
33538         if(isLoaded){\r
33539             window[trans.cb] = undefined;\r
33540             try{\r
33541                 delete window[trans.cb];\r
33542             }catch(e){}\r
33543         }else{\r
33544             // if hasn't been loaded, wait for load to remove it to prevent script error\r
33545             window[trans.cb] = function(){\r
33546                 window[trans.cb] = undefined;\r
33547                 try{\r
33548                     delete window[trans.cb];\r
33549                 }catch(e){}\r
33550             };\r
33551         }\r
33552     },\r
33553 \r
33554     // private\r
33555     handleFailure : function(trans){\r
33556         this.trans = false;\r
33557         this.destroyTrans(trans, false);\r
33558         if (trans.action === Ext.data.Api.actions.read) {\r
33559             // @deprecated firing loadexception\r
33560             this.fireEvent("loadexception", this, null, trans.arg);\r
33561         }\r
33562 \r
33563         this.fireEvent('exception', this, 'response', trans.action, {\r
33564             response: null,\r
33565             options: trans.arg\r
33566         });\r
33567         trans.callback.call(trans.scope||window, null, trans.arg, false);\r
33568     },\r
33569 \r
33570     // inherit docs\r
33571     destroy: function(){\r
33572         this.abort();\r
33573         Ext.data.ScriptTagProxy.superclass.destroy.call(this);\r
33574     }\r
33575 });/**\r
33576  * @class Ext.data.HttpProxy\r
33577  * @extends Ext.data.DataProxy\r
33578  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same\r
33579  * domain of the originating page.</p>\r
33580  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other\r
33581  * than the domain from which the running page was served. For cross-domain requests, use a\r
33582  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>\r
33583  * <p>Be aware that to enable the browser to parse an XML document, the server must set\r
33584  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>\r
33585  * @constructor\r
33586  * @param {Object} conn\r
33587  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.\r
33588  * <p>Note that if this HttpProxy is being used by a (@link Ext.data.Store Store}, then the\r
33589  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>\r
33590  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,\r
33591  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be\r
33592  * used to pass parameters known at instantiation time.</p>\r
33593  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make\r
33594  * the request.</p>\r
33595  */\r
33596 Ext.data.HttpProxy = function(conn){\r
33597     Ext.data.HttpProxy.superclass.constructor.call(this, conn);\r
33598 \r
33599     /**\r
33600      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy\r
33601      * uses to make requests to the server. Properties of this object may be changed dynamically to\r
33602      * change the way data is requested.\r
33603      * @property\r
33604      */\r
33605     this.conn = conn;\r
33606 \r
33607     // nullify the connection url.  The url param has been copied to 'this' above.  The connection\r
33608     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the\r
33609     // connection url during beforeaction events (ie: beforeload, beforewrite, etc).\r
33610     // Url is always re-defined during doRequest.\r
33611     this.conn.url = null;\r
33612 \r
33613     this.useAjax = !conn || !conn.events;\r
33614 \r
33615     // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]\r
33616     var actions = Ext.data.Api.actions;\r
33617     this.activeRequest = {};\r
33618     for (var verb in actions) {\r
33619         this.activeRequest[actions[verb]] = undefined;\r
33620     }\r
33621 };\r
33622 \r
33623 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {\r
33624     /**\r
33625      * Return the {@link Ext.data.Connection} object being used by this Proxy.\r
33626      * @return {Connection} The Connection object. This object may be used to subscribe to events on\r
33627      * a finer-grained basis than the DataProxy events.\r
33628      */\r
33629     getConnection : function() {\r
33630         return this.useAjax ? Ext.Ajax : this.conn;\r
33631     },\r
33632 \r
33633     /**\r
33634      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl\r
33635      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for\r
33636      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined\r
33637      * in the initial proxy configuration.\r
33638      * @param {String} url\r
33639      * @param {Boolean} makePermanent (Optional) [false]\r
33640      *\r
33641      * (e.g.: beforeload, beforesave, etc).\r
33642      */\r
33643     setUrl : function(url, makePermanent) {\r
33644         this.conn.url = url;\r
33645         if (makePermanent === true) {\r
33646             this.url = url;\r
33647             this.api = null;\r
33648             Ext.data.Api.prepare(this);\r
33649         }\r
33650     },\r
33651 \r
33652     /**\r
33653      * HttpProxy implementation of DataProxy#doRequest\r
33654      * @param {String} action The crud action type (create, read, update, destroy)\r
33655      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
33656      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
33657      * for the request to the remote server.\r
33658      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
33659      * object into a block of Ext.data.Records.\r
33660      * @param {Function} callback\r
33661      * <div class="sub-desc"><p>A function to be called after the request.\r
33662      * The <tt>callback</tt> is passed the following arguments:<ul>\r
33663      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>\r
33664      * <li><tt>options</tt>: Options object from the action request</li>\r
33665      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>\r
33666      * @param {Object} scope The scope in which to call the callback\r
33667      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
33668      */\r
33669     doRequest : function(action, rs, params, reader, cb, scope, arg) {\r
33670         var  o = {\r
33671             method: (this.api[action]) ? this.api[action]['method'] : undefined,\r
33672             request: {\r
33673                 callback : cb,\r
33674                 scope : scope,\r
33675                 arg : arg\r
33676             },\r
33677             reader: reader,\r
33678             callback : this.createCallback(action, rs),\r
33679             scope: this\r
33680         };\r
33681 \r
33682         // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).\r
33683         // Use std HTTP params otherwise.\r
33684         // TODO wrap into 1 Ext.apply now?\r
33685         if (params.jsonData) {\r
33686             o.jsonData = params.jsonData;\r
33687         } else if (params.xmlData) {\r
33688             o.xmlData = params.xmlData;\r
33689         } else {\r
33690             o.params = params || {};\r
33691         }\r
33692         // Set the connection url.  If this.conn.url is not null here,\r
33693         // the user may have overridden the url during a beforeaction event-handler.\r
33694         // this.conn.url is nullified after each request.\r
33695         if (this.conn.url === null) {\r
33696             this.conn.url = this.buildUrl(action, rs);\r
33697         }\r
33698         else if (this.restful === true && rs instanceof Ext.data.Record && !rs.phantom) { // <-- user must have intervened with #setApi or #setUrl\r
33699             this.conn.url += '/' + rs.id;\r
33700         }\r
33701         if(this.useAjax){\r
33702 \r
33703             Ext.applyIf(o, this.conn);\r
33704 \r
33705             // If a currently running request is found for this action, abort it.\r
33706             if (this.activeRequest[action]) {\r
33707                 ////\r
33708                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array\r
33709                 // TODO ideas anyone?\r
33710                 //\r
33711                 //Ext.Ajax.abort(this.activeRequest[action]);\r
33712             }\r
33713             this.activeRequest[action] = Ext.Ajax.request(o);\r
33714         }else{\r
33715             this.conn.request(o);\r
33716         }\r
33717         // request is sent, nullify the connection url in preparation for the next request\r
33718         this.conn.url = null;\r
33719     },\r
33720 \r
33721     /**\r
33722      * Returns a callback function for a request.  Note a special case is made for the\r
33723      * read action vs all the others.\r
33724      * @param {String} action [create|update|delete|load]\r
33725      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon\r
33726      * @private\r
33727      */\r
33728     createCallback : function(action, rs) {\r
33729         return function(o, success, response) {\r
33730             this.activeRequest[action] = undefined;\r
33731             if (!success) {\r
33732                 if (action === Ext.data.Api.actions.read) {\r
33733                     // @deprecated: fire loadexception for backwards compat.\r
33734                     // TODO remove in 3.1\r
33735                     this.fireEvent('loadexception', this, o, response);\r
33736                 }\r
33737                 this.fireEvent('exception', this, 'response', action, o, response);\r
33738                 o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
33739                 return;\r
33740             }\r
33741             if (action === Ext.data.Api.actions.read) {\r
33742                 this.onRead(action, o, response);\r
33743             } else {\r
33744                 this.onWrite(action, o, response, rs);\r
33745             }\r
33746         }\r
33747     },\r
33748 \r
33749     /**\r
33750      * Callback for read action\r
33751      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
33752      * @param {Object} o The request transaction object\r
33753      * @param {Object} res The server response\r
33754      * @fires loadexception (deprecated)\r
33755      * @fires exception\r
33756      * @fires load\r
33757      * @private\r
33758      */\r
33759     onRead : function(action, o, response) {\r
33760         var result;\r
33761         try {\r
33762             result = o.reader.read(response);\r
33763         }catch(e){\r
33764             // @deprecated: fire old loadexception for backwards-compat.\r
33765             // TODO remove in 3.1\r
33766             this.fireEvent('loadexception', this, o, response, e);\r
33767 \r
33768             this.fireEvent('exception', this, 'response', action, o, response, e);\r
33769             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
33770             return;\r
33771         }\r
33772         if (result.success === false) {\r
33773             // @deprecated: fire old loadexception for backwards-compat.\r
33774             // TODO remove in 3.1\r
33775             this.fireEvent('loadexception', this, o, response);\r
33776 \r
33777             // Get DataReader read-back a response-object to pass along to exception event\r
33778             var res = o.reader.readResponse(action, response);\r
33779             this.fireEvent('exception', this, 'remote', action, o, res, null);\r
33780         }\r
33781         else {\r
33782             this.fireEvent('load', this, o, o.request.arg);\r
33783         }\r
33784         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
33785         // the calls to request.callback(...) in each will have to be made identical.\r
33786         // NOTE reader.readResponse does not currently return Ext.data.Response\r
33787         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);\r
33788     },\r
33789     /**\r
33790      * Callback for write actions\r
33791      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
33792      * @param {Object} trans The request transaction object\r
33793      * @param {Object} res The server response\r
33794      * @fires exception\r
33795      * @fires write\r
33796      * @private\r
33797      */\r
33798     onWrite : function(action, o, response, rs) {\r
33799         var reader = o.reader;\r
33800         var res;\r
33801         try {\r
33802             res = reader.readResponse(action, response);\r
33803         } catch (e) {\r
33804             this.fireEvent('exception', this, 'response', action, o, response, e);\r
33805             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
33806             return;\r
33807         }\r
33808         if (res.success === false) {\r
33809             this.fireEvent('exception', this, 'remote', action, o, res, rs);\r
33810         } else {\r
33811             this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);\r
33812         }\r
33813         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
33814         // the calls to request.callback(...) in each will have to be made similar.\r
33815         // NOTE reader.readResponse does not currently return Ext.data.Response\r
33816         o.request.callback.call(o.request.scope, res.data, res, res.success);\r
33817     },\r
33818 \r
33819     // inherit docs\r
33820     destroy: function(){\r
33821         if(!this.useAjax){\r
33822             this.conn.abort();\r
33823         }else if(this.activeRequest){\r
33824             var actions = Ext.data.Api.actions;\r
33825             for (var verb in actions) {\r
33826                 if(this.activeRequest[actions[verb]]){\r
33827                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);\r
33828                 }\r
33829             }\r
33830         }\r
33831         Ext.data.HttpProxy.superclass.destroy.call(this);\r
33832     }\r
33833 });/**\r
33834  * @class Ext.data.MemoryProxy\r
33835  * @extends Ext.data.DataProxy\r
33836  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor\r
33837  * to the Reader when its load method is called.\r
33838  * @constructor\r
33839  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.\r
33840  */\r
33841 Ext.data.MemoryProxy = function(data){\r
33842     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super\r
33843     var api = {};\r
33844     api[Ext.data.Api.actions.read] = true;\r
33845     Ext.data.MemoryProxy.superclass.constructor.call(this, {\r
33846         api: api\r
33847     });\r
33848     this.data = data;\r
33849 };\r
33850 \r
33851 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {\r
33852     /**\r
33853      * @event loadexception\r
33854      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed\r
33855      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.\r
33856      * @param {Object} this\r
33857      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
33858      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy\r
33859      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data\r
33860      */\r
33861 \r
33862        /**\r
33863      * MemoryProxy implementation of DataProxy#doRequest\r
33864      * @param {String} action\r
33865      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
33866      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
33867      * for the request to the remote server.\r
33868      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
33869      * object into a block of Ext.data.Records.\r
33870      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
33871      * The function must be passed <ul>\r
33872      * <li>The Record block object</li>\r
33873      * <li>The "arg" argument from the load function</li>\r
33874      * <li>A boolean success indicator</li>\r
33875      * </ul>\r
33876      * @param {Object} scope The scope in which to call the callback\r
33877      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
33878      */\r
33879     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
33880         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'\r
33881         params = params || {};\r
33882         var result;\r
33883         try {\r
33884             result = reader.readRecords(this.data);\r
33885         }catch(e){\r
33886             // @deprecated loadexception\r
33887             this.fireEvent("loadexception", this, null, arg, e);\r
33888 \r
33889             this.fireEvent('exception', this, 'response', action, arg, null, e);\r
33890             callback.call(scope, null, arg, false);\r
33891             return;\r
33892         }\r
33893         callback.call(scope, result, arg, true);\r
33894     }\r
33895 });/**
33896  * @class Ext.data.JsonWriter
33897  * @extends Ext.data.DataWriter
33898  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
33899  */
33900 Ext.data.JsonWriter = function(config) {
33901     Ext.data.JsonWriter.superclass.constructor.call(this, config);
33902
33903     // careful to respect "returnJson", renamed to "encode"
33904     // TODO: remove after v3 final release
33905     if (this.returnJson != undefined) {
33906         this.encode = this.returnJson;
33907     }
33908 }
33909 Ext.extend(Ext.data.JsonWriter, Ext.data.DataWriter, {
33910     /**
33911      * @cfg {Boolean} returnJson <b>Deprecated</b>.  Use {@link Ext.data.JsonWriter#encode} instead.
33912      */
33913     returnJson : undefined,
33914     /**
33915      * @cfg {Boolean} encode <tt>true</tt> to {@link Ext.util.JSON#encode encode} the
33916      * {@link Ext.data.DataWriter#toHash hashed data}. Defaults to <tt>true</tt>.  When using
33917      * {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
33918      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
33919      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
33920      * instead of <b>params</b>.  When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
33921      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>, as in
33922      * let the lower-level connection object (eg: Ext.Ajax) do the encoding.
33923      */
33924     encode : true,
33925
33926     /**
33927      * Final action of a write event.  Apply the written data-object to params.
33928      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
33929      * @param {Record[]} rs
33930      * @param {Object} http params
33931      * @param {Object} data object populated according to DataReader meta-data "root" and "idProperty"
33932      */
33933     render : function(action, rs, params, data) {
33934         if (this.encode === true) {
33935             params[this.meta.root] = Ext.encode(data);
33936         } else {
33937             params.jsonData = {};
33938             params.jsonData[this.meta.root] = data;
33939         }
33940     },
33941     /**
33942      * Implements abstract Ext.data.DataWriter#createRecord
33943      * @protected
33944      * @param {Ext.data.Record} rec
33945      * @return {Object}
33946      */
33947     createRecord : function(rec) {
33948        return this.toHash(rec);
33949     },
33950     /**
33951      * Implements abstract Ext.data.DataWriter#updateRecord
33952      * @protected
33953      * @param {Ext.data.Record} rec
33954      * @return {Object}
33955      */
33956     updateRecord : function(rec) {
33957         return this.toHash(rec);
33958
33959     },
33960     /**
33961      * Implements abstract Ext.data.DataWriter#destroyRecord
33962      * @protected
33963      * @param {Ext.data.Record} rec
33964      * @return {Object}
33965      */
33966     destroyRecord : function(rec) {
33967         return rec.id;
33968     }
33969 });/**
33970  * @class Ext.data.JsonReader
33971  * @extends Ext.data.DataReader
33972  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects
33973  * from a JSON packet based on mappings in a provided {@link Ext.data.Record}
33974  * constructor.</p>
33975  * <p>Example code:</p>
33976  * <pre><code>
33977 var myReader = new Ext.data.JsonReader({
33978     // metadata configuration options:
33979     {@link #idProperty}: 'id'
33980     {@link #root}: 'rows',
33981     {@link #totalProperty}: 'results',
33982     {@link Ext.data.DataReader#messageProperty}: "msg"  // The element within the response that provides a user-feedback message (optional)
33983
33984     // the fields config option will internally create an {@link Ext.data.Record}
33985     // constructor that provides mapping for reading the record data objects
33986     {@link Ext.data.DataReader#fields fields}: [
33987         // map Record&#39;s 'firstname' field to data object&#39;s key of same name
33988         {name: 'name'},
33989         // map Record&#39;s 'job' field to data object&#39;s 'occupation' key
33990         {name: 'job', mapping: 'occupation'}
33991     ]
33992 });
33993 </code></pre>
33994  * <p>This would consume a JSON data object of the form:</p><pre><code>
33995 {
33996     results: 2000, // Reader&#39;s configured {@link #totalProperty}
33997     rows: [        // Reader&#39;s configured {@link #root}
33998         // record data objects:
33999         { {@link #idProperty id}: 1, firstname: 'Bill', occupation: 'Gardener' },
34000         { {@link #idProperty id}: 2, firstname: 'Ben' , occupation: 'Horticulturalist' },
34001         ...
34002     ]
34003 }
34004 </code></pre>
34005  * <p><b><u>Automatic configuration using metaData</u></b></p>
34006  * <p>It is possible to change a JsonReader's metadata at any time by including
34007  * a <b><tt>metaData</tt></b> property in the JSON data object. If the JSON data
34008  * object has a <b><tt>metaData</tt></b> property, a {@link Ext.data.Store Store}
34009  * object using this Reader will reconfigure itself to use the newly provided
34010  * field definition and fire its {@link Ext.data.Store#metachange metachange}
34011  * event. The metachange event handler may interrogate the <b><tt>metaData</tt></b>
34012  * property to perform any configuration required.</p>
34013  * <p>Note that reconfiguring a Store potentially invalidates objects which may
34014  * refer to Fields or Records which no longer exist.</p>
34015  * <p>To use this facility you would create the JsonReader like this:</p><pre><code>
34016 var myReader = new Ext.data.JsonReader();
34017 </code></pre>
34018  * <p>The first data packet from the server would configure the reader by
34019  * containing a <b><tt>metaData</tt></b> property <b>and</b> the data. For
34020  * example, the JSON data object might take the form:</p><pre><code>
34021 {
34022     metaData: {
34023         "{@link #idProperty}": "id",
34024         "{@link #root}": "rows",
34025         "{@link #totalProperty}": "results"
34026         "{@link #successProperty}": "success",
34027         "{@link Ext.data.DataReader#fields fields}": [
34028             {"name": "name"},
34029             {"name": "job", "mapping": "occupation"}
34030         ],
34031         // used by store to set its sortInfo
34032         "sortInfo":{
34033            "field": "name",
34034            "direction": "ASC"
34035         },
34036         // {@link Ext.PagingToolbar paging data} (if applicable)
34037         "start": 0,
34038         "limit": 2,
34039         // custom property
34040         "foo": "bar"
34041     },
34042     // Reader&#39;s configured {@link #successProperty}
34043     "success": true,
34044     // Reader&#39;s configured {@link #totalProperty}
34045     "results": 2000,
34046     // Reader&#39;s configured {@link #root}
34047     // (this data simulates 2 results {@link Ext.PagingToolbar per page})
34048     "rows": [ // <b>*Note:</b> this must be an Array
34049         { "id": 1, "name": "Bill", "occupation": "Gardener" },
34050         { "id": 2, "name":  "Ben", "occupation": "Horticulturalist" }
34051     ]
34052 }
34053  * </code></pre>
34054  * <p>The <b><tt>metaData</tt></b> property in the JSON data object should contain:</p>
34055  * <div class="mdetail-params"><ul>
34056  * <li>any of the configuration options for this class</li>
34057  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which
34058  * the JsonReader will use as an argument to the
34059  * {@link Ext.data.Record#create data Record create method} in order to
34060  * configure the layout of the Records it will produce.</li>
34061  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property
34062  * which the JsonReader will use to set the {@link Ext.data.Store}'s
34063  * {@link Ext.data.Store#sortInfo sortInfo} property</li>
34064  * <li>any custom properties needed</li>
34065  * </ul></div>
34066  *
34067  * @constructor
34068  * Create a new JsonReader
34069  * @param {Object} meta Metadata configuration options.
34070  * @param {Array/Object} recordType
34071  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
34072  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
34073  * constructor created from {@link Ext.data.Record#create}.</p>
34074  */
34075 Ext.data.JsonReader = function(meta, recordType){
34076     meta = meta || {};
34077     /**
34078      * @cfg {String} idProperty [id] Name of the property within a row object
34079      * that contains a record identifier value.  Defaults to <tt>id</tt>
34080      */
34081     /**
34082      * @cfg {String} successProperty [success] Name of the property from which to
34083      * retrieve the success attribute. Defaults to <tt>success</tt>.  See
34084      * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
34085      * for additional information.
34086      */
34087     /**
34088      * @cfg {String} totalProperty [total] Name of the property from which to
34089      * retrieve the total number of records in the dataset. This is only needed
34090      * if the whole dataset is not passed in one go, but is being paged from
34091      * the remote server.  Defaults to <tt>total</tt>.
34092      */
34093     /**
34094      * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
34095      * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
34096      * An exception will be thrown if the root property is undefined. The data
34097      * packet value for this property should be an empty array to clear the data
34098      * or show no data.
34099      */
34100     Ext.applyIf(meta, {
34101         idProperty: 'id',
34102         successProperty: 'success',
34103         totalProperty: 'total'
34104     });
34105
34106     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
34107 };
34108 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
34109     /**
34110      * This JsonReader's metadata as passed to the constructor, or as passed in
34111      * the last data packet's <b><tt>metaData</tt></b> property.
34112      * @type Mixed
34113      * @property meta
34114      */
34115     /**
34116      * This method is only used by a DataProxy which has retrieved data from a remote server.
34117      * @param {Object} response The XHR object which contains the JSON data in its responseText.
34118      * @return {Object} data A data block which is used by an Ext.data.Store object as
34119      * a cache of Ext.data.Records.
34120      */
34121     read : function(response){
34122         var json = response.responseText;
34123         var o = Ext.decode(json);
34124         if(!o) {
34125             throw {message: 'JsonReader.read: Json object not found'};
34126         }
34127         return this.readRecords(o);
34128     },
34129
34130     /**
34131      * Decode a json response from server.
34132      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
34133      * @param {Object} response
34134      * TODO: refactor code between JsonReader#readRecords, #readResponse into 1 method.
34135      * there's ugly duplication going on due to maintaining backwards compat. with 2.0.  It's time to do this.
34136      */
34137     readResponse : function(action, response) {
34138         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
34139         if(!o) {
34140             throw new Ext.data.JsonReader.Error('response');
34141         }
34142
34143         var root = this.getRoot(o);
34144         if (action === Ext.data.Api.actions.create) {
34145             var def = Ext.isDefined(root);
34146             if (def && Ext.isEmpty(root)) {
34147                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
34148             }
34149             else if (!def) {
34150                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
34151             }
34152         }
34153
34154         // instantiate response object
34155         var res = new Ext.data.Response({
34156             action: action,
34157             success: this.getSuccess(o),
34158             data: this.extractData(root),
34159             message: this.getMessage(o),
34160             raw: o
34161         });
34162
34163         // blow up if no successProperty
34164         if (Ext.isEmpty(res.success)) {
34165             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
34166         }
34167         return res;
34168     },
34169
34170     /**
34171      * Create a data block containing Ext.data.Records from a JSON object.
34172      * @param {Object} o An object which contains an Array of row objects in the property specified
34173      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
34174      * which contains the total size of the dataset.
34175      * @return {Object} data A data block which is used by an Ext.data.Store object as
34176      * a cache of Ext.data.Records.
34177      */
34178     readRecords : function(o){
34179         /**
34180          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
34181          * loaded or there is a load exception this property will be undefined.
34182          * @type Object
34183          */
34184         this.jsonData = o;
34185         if(o.metaData){
34186             this.onMetaChange(o.metaData);
34187         }
34188         var s = this.meta, Record = this.recordType,
34189             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
34190
34191         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
34192         if(s.totalProperty){
34193             v = parseInt(this.getTotal(o), 10);
34194             if(!isNaN(v)){
34195                 totalRecords = v;
34196             }
34197         }
34198         if(s.successProperty){
34199             v = this.getSuccess(o);
34200             if(v === false || v === 'false'){
34201                 success = false;
34202             }
34203         }
34204
34205         // TODO return Ext.data.Response instance instead.  @see #readResponse
34206         return {
34207             success : success,
34208             records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
34209             totalRecords : totalRecords
34210         };
34211     },
34212
34213     // private
34214     buildExtractors : function() {
34215         if(this.ef){
34216             return;
34217         }
34218         var s = this.meta, Record = this.recordType,
34219             f = Record.prototype.fields, fi = f.items, fl = f.length;
34220
34221         if(s.totalProperty) {
34222             this.getTotal = this.createAccessor(s.totalProperty);
34223         }
34224         if(s.successProperty) {
34225             this.getSuccess = this.createAccessor(s.successProperty);
34226         }
34227         if (s.messageProperty) {
34228             this.getMessage = this.createAccessor(s.messageProperty);
34229         }
34230         this.getRoot = s.root ? this.createAccessor(s.root) : function(p){return p;};
34231         if (s.id || s.idProperty) {
34232             var g = this.createAccessor(s.id || s.idProperty);
34233             this.getId = function(rec) {
34234                 var r = g(rec);
34235                 return (r === undefined || r === '') ? null : r;
34236             };
34237         } else {
34238             this.getId = function(){return null;};
34239         }
34240         var ef = [];
34241         for(var i = 0; i < fl; i++){
34242             f = fi[i];
34243             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
34244             ef.push(this.createAccessor(map));
34245         }
34246         this.ef = ef;
34247     },
34248
34249     /**
34250      * @ignore
34251      * TODO This isn't used anywhere??  Don't we want to use this where possible instead of complex #createAccessor?
34252      */
34253     simpleAccess : function(obj, subsc) {
34254         return obj[subsc];
34255     },
34256
34257     /**
34258      * @ignore
34259      */
34260     createAccessor : function(){
34261         var re = /[\[\.]/;
34262         return function(expr) {
34263             try {
34264                 return(re.test(expr)) ?
34265                 new Function('obj', 'return obj.' + expr) :
34266                 function(obj){
34267                     return obj[expr];
34268                 };
34269             } catch(e){}
34270             return Ext.emptyFn;
34271         };
34272     }(),
34273
34274     /**
34275      * returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
34276      * @param {Object[]/Object} data-root from server response
34277      * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
34278      * @private
34279      */
34280     extractData : function(root, returnRecords) {
34281         var rs = undefined;
34282         if (this.isData(root)) {
34283             root = [root];
34284         }
34285         if (Ext.isArray(root)) {
34286             var f       = this.recordType.prototype.fields,
34287                 fi      = f.items,
34288                 fl      = f.length,
34289                 rs      = [];
34290             if (returnRecords === true) {
34291                 var Record = this.recordType;
34292                 for (var i = 0; i < root.length; i++) {
34293                     var n = root[i];
34294                     var record = new Record(this.extractValues(n, fi, fl), this.getId(n));
34295                     record.json = n;
34296                     rs.push(record);
34297                 }
34298             }
34299             else {
34300                 for (var i = 0; i < root.length; i++) {
34301                     rs.push(this.extractValues(root[i], fi, fl));
34302                 }
34303             }
34304         }
34305         return rs;
34306     },
34307
34308     /**
34309      * type-casts a single row of raw-data from server
34310      * @param {Object} data
34311      * @param {Array} items
34312      * @param {Integer} len
34313      * @private
34314      */
34315     extractValues : function(data, items, len) {
34316         var f, values = {};
34317         for(var j = 0; j < len; j++){
34318             f = items[j];
34319             var v = this.ef[j](data);
34320             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
34321         }
34322         return values;
34323     }
34324 });
34325
34326 /**
34327  * @class Ext.data.JsonReader.Error
34328  * Error class for JsonReader
34329  */
34330 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
34331     constructor : function(message, arg) {
34332         this.arg = arg;
34333         Ext.Error.call(this, message);
34334     },
34335     name : 'Ext.data.JsonReader'
34336 });
34337 Ext.apply(Ext.data.JsonReader.Error.prototype, {
34338     lang: {
34339         'response': 'An error occurred while json-decoding your server response',
34340         '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.',
34341         '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.',
34342         '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.',
34343         'root-empty': 'Data was expected to be returned by the server in the "root" property of the response.  Please review your JsonReader configuration to ensure the "root" property matches that returned in the server-response.  See JsonReader docs.'
34344     }
34345 });
34346 /**
34347  * @class Ext.data.ArrayReader
34348  * @extends Ext.data.JsonReader
34349  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
34350  * Each element of that Array represents a row of data fields. The
34351  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
34352  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
34353  * <p>Example code:</p>
34354  * <pre><code>
34355 var Employee = Ext.data.Record.create([
34356     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
34357     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
34358 ]);
34359 var myReader = new Ext.data.ArrayReader({
34360     {@link #idIndex}: 0
34361 }, Employee);
34362 </code></pre>
34363  * <p>This would consume an Array like this:</p>
34364  * <pre><code>
34365 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
34366  * </code></pre>
34367  * @constructor
34368  * Create a new ArrayReader
34369  * @param {Object} meta Metadata configuration options.
34370  * @param {Array/Object} recordType
34371  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
34372  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
34373  * constructor created from {@link Ext.data.Record#create}.</p>
34374  */
34375 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
34376     /**
34377      * @cfg {String} successProperty
34378      * @hide
34379      */
34380     /**
34381      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
34382      * Deprecated. Use {@link #idIndex} instead.
34383      */
34384     /**
34385      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
34386      */
34387     /**
34388      * Create a data block containing Ext.data.Records from an Array.
34389      * @param {Object} o An Array of row objects which represents the dataset.
34390      * @return {Object} data A data block which is used by an Ext.data.Store object as
34391      * a cache of Ext.data.Records.
34392      */
34393     readRecords : function(o){
34394         this.arrayData = o;
34395         var s = this.meta,
34396             sid = s ? Ext.num(s.idIndex, s.id) : null,
34397             recordType = this.recordType, 
34398             fields = recordType.prototype.fields,
34399             records = [],
34400             v;
34401
34402         if(!this.getRoot) {
34403             this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p) {return p;};
34404             if(s.totalProperty) {
34405                 this.getTotal = this.getJsonAccessor(s.totalProperty);
34406             }
34407         }
34408
34409         var root = this.getRoot(o);
34410
34411         for(var i = 0, len = root.length; i < len; i++) {
34412             var n = root[i],
34413                 values = {},
34414                 id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
34415             for(var j = 0, jlen = fields.length; j < jlen; j++) {
34416                 var f = fields.items[j],
34417                     k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
34418                 v = n[k] !== undefined ? n[k] : f.defaultValue;
34419                 v = f.convert(v, n);
34420                 values[f.name] = v;
34421             }
34422             var record = new recordType(values, id);
34423             record.json = n;
34424             records[records.length] = record;
34425         }
34426
34427         var totalRecords = records.length;
34428
34429         if(s.totalProperty) {
34430             v = parseInt(this.getTotal(o), 10);
34431             if(!isNaN(v)) {
34432                 totalRecords = v;
34433             }
34434         }
34435
34436         return {
34437             records : records,
34438             totalRecords : totalRecords
34439         };
34440     }
34441 });/**
34442  * @class Ext.data.ArrayStore
34443  * @extends Ext.data.Store
34444  * <p>Formerly known as "SimpleStore".</p>
34445  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
34446  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
34447  * <p>A store configuration would be something like:<pre><code>
34448 var store = new Ext.data.ArrayStore({
34449     // store configs
34450     autoDestroy: true,
34451     storeId: 'myStore',
34452     // reader configs
34453     idIndex: 0,  
34454     fields: [
34455        'company',
34456        {name: 'price', type: 'float'},
34457        {name: 'change', type: 'float'},
34458        {name: 'pctChange', type: 'float'},
34459        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
34460     ]
34461 });
34462  * </code></pre></p>
34463  * <p>This store is configured to consume a returned object of the form:<pre><code>
34464 var myData = [
34465     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
34466     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
34467     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
34468     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
34469     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
34470 ];
34471  * </code></pre>
34472  * An object literal of this form could also be used as the {@link #data} config option.</p>
34473  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
34474  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
34475  * @constructor
34476  * @param {Object} config
34477  * @xtype arraystore
34478  */
34479 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
34480     /**
34481      * @cfg {Ext.data.DataReader} reader @hide
34482      */
34483     constructor: function(config){
34484         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
34485             reader: new Ext.data.ArrayReader(config)
34486         }));
34487     },
34488
34489     loadData : function(data, append){
34490         if(this.expandData === true){
34491             var r = [];
34492             for(var i = 0, len = data.length; i < len; i++){
34493                 r[r.length] = [data[i]];
34494             }
34495             data = r;
34496         }
34497         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
34498     }
34499 });
34500 Ext.reg('arraystore', Ext.data.ArrayStore);
34501
34502 // backwards compat
34503 Ext.data.SimpleStore = Ext.data.ArrayStore;
34504 Ext.reg('simplestore', Ext.data.SimpleStore);/**
34505  * @class Ext.data.JsonStore
34506  * @extends Ext.data.Store
34507  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
34508  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
34509  * <p>A store configuration would be something like:<pre><code>
34510 var store = new Ext.data.JsonStore({
34511     // store configs
34512     autoDestroy: true,
34513     url: 'get-images.php',
34514     storeId: 'myStore',
34515     // reader configs
34516     root: 'images',
34517     idProperty: 'name',
34518     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
34519 });
34520  * </code></pre></p>
34521  * <p>This store is configured to consume a returned object of the form:<pre><code>
34522 {
34523     images: [
34524         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
34525         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
34526     ]
34527 }
34528  * </code></pre>
34529  * An object literal of this form could also be used as the {@link #data} config option.</p>
34530  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
34531  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
34532  * @constructor
34533  * @param {Object} config
34534  * @xtype jsonstore
34535  */
34536 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
34537     /**
34538      * @cfg {Ext.data.DataReader} reader @hide
34539      */
34540     constructor: function(config){
34541         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
34542             reader: new Ext.data.JsonReader(config)
34543         }));
34544     }
34545 });
34546 Ext.reg('jsonstore', Ext.data.JsonStore);/**
34547  * @class Ext.data.XmlWriter
34548  * @extends Ext.data.DataWriter
34549  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
34550  */
34551 Ext.data.XmlWriter = function(params) {
34552     Ext.data.XmlWriter.superclass.constructor.apply(this, arguments);
34553     this.tpl = new Ext.XTemplate(this.tpl).compile();
34554 };
34555 Ext.extend(Ext.data.XmlWriter, Ext.data.DataWriter, {
34556     /**
34557      * @cfg {String} root [records] The name of the root element when writing <b>multiple</b> records to the server.  Each
34558      * xml-record written to the server will be wrapped in an element named after {@link Ext.data.XmlReader#record} property.
34559      * eg:
34560 <code><pre>
34561 &lt;?xml version="1.0" encoding="UTF-8"?>
34562 &lt;user>&lt;first>Barney&lt;/first>&lt;/user>
34563 </code></pre>
34564      * However, when <b>multiple</b> records are written in a batch-operation, these records must be wrapped in a containing
34565      * Element.
34566      * eg:
34567 <code><pre>
34568 &lt;?xml version="1.0" encoding="UTF-8"?>
34569     &lt;records>
34570         &lt;first>Barney&lt;/first>&lt;/user>
34571         &lt;records>&lt;first>Barney&lt;/first>&lt;/user>
34572     &lt;/records>
34573 </code></pre>
34574      * Defaults to <tt>records</tt>
34575      */
34576     root: 'records',
34577     /**
34578      * @cfg {String} xmlVersion [1.0] The <tt>version</tt> written to header of xml documents.
34579 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
34580      */
34581     xmlVersion : '1.0',
34582     /**
34583      * @cfg {String} xmlEncoding [ISO-8859-15] The <tt>encoding</tt> written to header of xml documents.
34584 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
34585      */
34586     xmlEncoding: 'ISO-8859-15',
34587     /**
34588      * @cfg {String} tpl The xml template.  Defaults to
34589 <code><pre>
34590 &lt;?xml version="{version}" encoding="{encoding}"?>
34591     &lt;tpl if="{[values.nodes.length>1]}">&lt;{root}}>',
34592     &lt;tpl for="records">
34593         &lt;{parent.record}>
34594         &lt;tpl for="fields">
34595             &lt;{name}>{value}&lt;/{name}>
34596         &lt;/tpl>
34597         &lt;/{parent.record}>
34598     &lt;/tpl>
34599     &lt;tpl if="{[values.records.length>1]}">&lt;/{root}}>&lt;/tpl>
34600 </pre></code>
34601      */
34602     // Break up encoding here in case it's being included by some kind of page that will parse it (eg. PHP)
34603     tpl: '<tpl for="."><' + '?xml version="{version}" encoding="{encoding}"?' + '><tpl if="documentRoot"><{documentRoot}><tpl for="baseParams"><tpl for="."><{name}>{value}</{name}</tpl></tpl></tpl><tpl if="records.length&gt;1"><{root}></tpl><tpl for="records"><{parent.record}><tpl for="."><{name}>{value}</{name}></tpl></{parent.record}></tpl><tpl if="records.length&gt;1"></{root}></tpl><tpl if="documentRoot"></{documentRoot}></tpl></tpl>',
34604
34605     /**
34606      * Final action of a write event.  Apply the written data-object to params.
34607      * @param {String} action [Ext.data.Api.create|read|update|destroy]
34608      * @param {Ext.data.Record/Ext.data.Record[]} rs
34609      * @param {Object} http params
34610      * @param {Object/Object[]} rendered data.
34611      */
34612     render : function(action, rs, params, data) {
34613         params.xmlData = this.tpl.applyTemplate({
34614             version: this.xmlVersion,
34615             encoding: this.xmlEncoding,
34616             record: this.meta.record,
34617             root: this.root,
34618             records: (Ext.isArray(rs)) ? data : [data]
34619         });
34620     },
34621
34622     /**
34623      * Converts an Ext.data.Record to xml
34624      * @param {Ext.data.Record} rec
34625      * @return {String} rendered xml-element
34626      * @private
34627      */
34628     toXml : function(data) {
34629         var fields = [];
34630         Ext.iterate(data, function(k, v) {
34631             fields.push({
34632                 name: k,
34633                 value: v
34634             });
34635         },this);
34636         return {
34637             fields: fields
34638         };
34639     },
34640
34641     /**
34642      * createRecord
34643      * @param {Ext.data.Record} rec
34644      * @return {String} xml element
34645      * @private
34646      */
34647     createRecord : function(rec) {
34648         return this.toXml(this.toHash(rec));
34649     },
34650
34651     /**
34652      * updateRecord
34653      * @param {Ext.data.Record} rec
34654      * @return {String} xml element
34655      * @private
34656      */
34657     updateRecord : function(rec) {
34658         return this.toXml(this.toHash(rec));
34659
34660     },
34661     /**
34662      * destroyRecord
34663      * @param {Ext.data.Record} rec
34664      * @return {String} xml element
34665      */
34666     destroyRecord : function(rec) {
34667         var data = {};
34668         data[this.meta.idProperty] = rec.id;
34669         return this.toXml(data);
34670     }
34671 });
34672
34673 /**
34674  * @class Ext.data.XmlReader
34675  * @extends Ext.data.DataReader
34676  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
34677  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
34678  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
34679  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
34680  * <p>Example code:</p>
34681  * <pre><code>
34682 var Employee = Ext.data.Record.create([
34683    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
34684    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
34685 ]);
34686 var myReader = new Ext.data.XmlReader({
34687    totalProperty: "results", // The element which contains the total dataset size (optional)
34688    record: "row",           // The repeated element which contains row information
34689    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
34690    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
34691 }, Employee);
34692 </code></pre>
34693  * <p>
34694  * This would consume an XML file like this:
34695  * <pre><code>
34696 &lt;?xml version="1.0" encoding="UTF-8"?>
34697 &lt;dataset>
34698  &lt;results>2&lt;/results>
34699  &lt;row>
34700    &lt;id>1&lt;/id>
34701    &lt;name>Bill&lt;/name>
34702    &lt;occupation>Gardener&lt;/occupation>
34703  &lt;/row>
34704  &lt;row>
34705    &lt;id>2&lt;/id>
34706    &lt;name>Ben&lt;/name>
34707    &lt;occupation>Horticulturalist&lt;/occupation>
34708  &lt;/row>
34709 &lt;/dataset>
34710 </code></pre>
34711  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
34712  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
34713  * paged from the remote server.
34714  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
34715  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
34716  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
34717  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
34718  * a record identifier value.
34719  * @constructor
34720  * Create a new XmlReader.
34721  * @param {Object} meta Metadata configuration options
34722  * @param {Object} recordType Either an Array of field definition objects as passed to
34723  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
34724  */
34725 Ext.data.XmlReader = function(meta, recordType){
34726     meta = meta || {};
34727
34728     // backwards compat, convert idPath to idProperty
34729     meta.idProperty = meta.idProperty || meta.idPath;
34730
34731     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
34732 };
34733 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
34734     /**
34735      * This method is only used by a DataProxy which has retrieved data from a remote server.
34736      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
34737      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
34738      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
34739      * a cache of Ext.data.Records.
34740      */
34741     read : function(response){
34742         var doc = response.responseXML;
34743         if(!doc) {
34744             throw {message: "XmlReader.read: XML Document not available"};
34745         }
34746         return this.readRecords(doc);
34747     },
34748
34749     /**
34750      * Create a data block containing Ext.data.Records from an XML document.
34751      * @param {Object} doc A parsed XML document.
34752      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
34753      * a cache of Ext.data.Records.
34754      */
34755     readRecords : function(doc){
34756         /**
34757          * After any data loads/reads, the raw XML Document is available for further custom processing.
34758          * @type XMLDocument
34759          */
34760         this.xmlData = doc;
34761
34762         var root    = doc.documentElement || doc,
34763             q       = Ext.DomQuery,
34764             totalRecords = 0,
34765             success = true;
34766
34767         if(this.meta.totalProperty){
34768             totalRecords = this.getTotal(root, 0);
34769         }
34770         if(this.meta.successProperty){
34771             success = this.getSuccess(root);
34772         }
34773
34774         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
34775
34776         // TODO return Ext.data.Response instance.  @see #readResponse
34777         return {
34778             success : success,
34779             records : records,
34780             totalRecords : totalRecords || records.length
34781         };
34782     },
34783
34784     /**
34785      * Decode a json response from server.
34786      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
34787      * @param {Ext.data.Response} response Returns an instance of {@link Ext.data.Response}
34788      */
34789     readResponse : function(action, response) {
34790         var q   = Ext.DomQuery,
34791         doc     = response.responseXML;
34792
34793         var res = new Ext.data.Response({
34794             action: action,
34795             success : this.getSuccess(doc),
34796             message: this.getMessage(doc),
34797             data: this.extractData(q.select(this.meta.record, doc) || q.select(this.meta.root, doc)),
34798             raw: doc
34799         });
34800
34801         if (Ext.isEmpty(res.success)) {
34802             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
34803         }
34804
34805         if (action === Ext.data.Api.actions.create) {
34806             var def = Ext.isDefined(res.data);
34807             if (def && Ext.isEmpty(res.data)) {
34808                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
34809             }
34810             else if (!def) {
34811                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
34812             }
34813         }
34814         return res;
34815     },
34816
34817     getSuccess : function() {
34818         return true;
34819     },
34820
34821     /**
34822      * build response-data extractor functions.
34823      * @private
34824      * @ignore
34825      */
34826     buildExtractors : function() {
34827         if(this.ef){
34828             return;
34829         }
34830         var s       = this.meta,
34831             Record  = this.recordType,
34832             f       = Record.prototype.fields,
34833             fi      = f.items,
34834             fl      = f.length;
34835
34836         if(s.totalProperty) {
34837             this.getTotal = this.createAccessor(s.totalProperty);
34838         }
34839         if(s.successProperty) {
34840             this.getSuccess = this.createAccessor(s.successProperty);
34841         }
34842         if (s.messageProperty) {
34843             this.getMessage = this.createAccessor(s.messageProperty);
34844         }
34845         this.getRoot = function(res) {
34846             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
34847         }
34848         if (s.idPath || s.idProperty) {
34849             var g = this.createAccessor(s.idPath || s.idProperty);
34850             this.getId = function(rec) {
34851                 var id = g(rec) || rec.id;
34852                 return (id === undefined || id === '') ? null : id;
34853             };
34854         } else {
34855             this.getId = function(){return null;};
34856         }
34857         var ef = [];
34858         for(var i = 0; i < fl; i++){
34859             f = fi[i];
34860             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
34861             ef.push(this.createAccessor(map));
34862         }
34863         this.ef = ef;
34864     },
34865
34866     /**
34867      * Creates a function to return some particular key of data from a response.
34868      * @param {String} key
34869      * @return {Function}
34870      * @private
34871      * @ignore
34872      */
34873     createAccessor : function(){
34874         var q = Ext.DomQuery;
34875         return function(key) {
34876             switch(key) {
34877                 case this.meta.totalProperty:
34878                     return function(root, def){
34879                         return q.selectNumber(key, root, def);
34880                     }
34881                     break;
34882                 case this.meta.successProperty:
34883                     return function(root, def) {
34884                         var sv = q.selectValue(key, root, true);
34885                         var success = sv !== false && sv !== 'false';
34886                         return success;
34887                     }
34888                     break;
34889                 default:
34890                     return function(root, def) {
34891                         return q.selectValue(key, root, def);
34892                     }
34893                     break;
34894             }
34895         };
34896     }(),
34897
34898     /**
34899      * Extracts rows of record-data from server.  iterates and calls #extractValues
34900      * TODO I don't care much for method-names of #extractData, #extractValues.
34901      * @param {Array} root
34902      * @param {Boolean} returnRecords When true, will return instances of Ext.data.Record; otherwise just hashes.
34903      * @private
34904      * @ignore
34905      */
34906     extractData : function(root, returnRecords) {
34907         var Record  = this.recordType,
34908         records     = [],
34909         f           = Record.prototype.fields,
34910         fi          = f.items,
34911         fl          = f.length;
34912         if (returnRecords === true) {
34913             for (var i = 0, len = root.length; i < len; i++) {
34914                 var data = root[i],
34915                     record = new Record(this.extractValues(data, fi, fl), this.getId(data));
34916                     
34917                 record.node = data;
34918                 records.push(record);
34919             }
34920         } else {
34921             for (var i = 0, len = root.length; i < len; i++) {
34922                 records.push(this.extractValues(root[i], fi, fl));
34923             }
34924         }
34925         return records;
34926     },
34927
34928     /**
34929      * extracts values and type-casts a row of data from server, extracted by #extractData
34930      * @param {Hash} data
34931      * @param {Ext.data.Field[]} items
34932      * @param {Number} len
34933      * @private
34934      * @ignore
34935      */
34936     extractValues : function(data, items, len) {
34937         var f, values = {};
34938         for(var j = 0; j < len; j++){
34939             f = items[j];
34940             var v = this.ef[j](data);
34941             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
34942         }
34943         return values;
34944     }
34945 });/**\r
34946  * @class Ext.data.XmlStore\r
34947  * @extends Ext.data.Store\r
34948  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.\r
34949  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>\r
34950  * <p>A store configuration would be something like:<pre><code>\r
34951 var store = new Ext.data.XmlStore({\r
34952     // store configs\r
34953     autoDestroy: true,\r
34954     storeId: 'myStore',\r
34955     url: 'sheldon.xml', // automatically configures a HttpProxy\r
34956     // reader configs\r
34957     record: 'Item', // records will have an "Item" tag\r
34958     idPath: 'ASIN',\r
34959     totalRecords: '@TotalResults'\r
34960     fields: [\r
34961         // set up the fields mapping into the xml doc\r
34962         // The first needs mapping, the others are very basic\r
34963         {name: 'Author', mapping: 'ItemAttributes > Author'},\r
34964         'Title', 'Manufacturer', 'ProductGroup'\r
34965     ]\r
34966 });\r
34967  * </code></pre></p>\r
34968  * <p>This store is configured to consume a returned object of the form:<pre><code>\r
34969 &#60?xml version="1.0" encoding="UTF-8"?>\r
34970 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">\r
34971     &#60Items>\r
34972         &#60Request>\r
34973             &#60IsValid>True&#60/IsValid>\r
34974             &#60ItemSearchRequest>\r
34975                 &#60Author>Sidney Sheldon&#60/Author>\r
34976                 &#60SearchIndex>Books&#60/SearchIndex>\r
34977             &#60/ItemSearchRequest>\r
34978         &#60/Request>\r
34979         &#60TotalResults>203&#60/TotalResults>\r
34980         &#60TotalPages>21&#60/TotalPages>\r
34981         &#60Item>\r
34982             &#60ASIN>0446355453&#60/ASIN>\r
34983             &#60DetailPageURL>\r
34984                 http://www.amazon.com/\r
34985             &#60/DetailPageURL>\r
34986             &#60ItemAttributes>\r
34987                 &#60Author>Sidney Sheldon&#60/Author>\r
34988                 &#60Manufacturer>Warner Books&#60/Manufacturer>\r
34989                 &#60ProductGroup>Book&#60/ProductGroup>\r
34990                 &#60Title>Master of the Game&#60/Title>\r
34991             &#60/ItemAttributes>\r
34992         &#60/Item>\r
34993     &#60/Items>\r
34994 &#60/ItemSearchResponse>\r
34995  * </code></pre>\r
34996  * An object literal of this form could also be used as the {@link #data} config option.</p>\r
34997  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of \r
34998  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>\r
34999  * @constructor\r
35000  * @param {Object} config\r
35001  * @xtype xmlstore\r
35002  */\r
35003 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {\r
35004     /**\r
35005      * @cfg {Ext.data.DataReader} reader @hide\r
35006      */\r
35007     constructor: function(config){\r
35008         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {\r
35009             reader: new Ext.data.XmlReader(config)\r
35010         }));\r
35011     }\r
35012 });\r
35013 Ext.reg('xmlstore', Ext.data.XmlStore);/**\r
35014  * @class Ext.data.GroupingStore\r
35015  * @extends Ext.data.Store\r
35016  * A specialized store implementation that provides for grouping records by one of the available fields. This\r
35017  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to proved the data model for\r
35018  * a grouped GridPanel.\r
35019  * @constructor\r
35020  * Creates a new GroupingStore.\r
35021  * @param {Object} config A config object containing the objects needed for the Store to access data,\r
35022  * and read the data into Records.\r
35023  * @xtype groupingstore\r
35024  */\r
35025 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {\r
35026     \r
35027     //inherit docs\r
35028     constructor: function(config){\r
35029         Ext.data.GroupingStore.superclass.constructor.call(this, config);\r
35030         this.applyGroupField();\r
35031     },\r
35032     \r
35033     /**\r
35034      * @cfg {String} groupField\r
35035      * The field name by which to sort the store's data (defaults to '').\r
35036      */\r
35037     /**\r
35038      * @cfg {Boolean} remoteGroup\r
35039      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the\r
35040      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a\r
35041      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.\r
35042      */\r
35043     remoteGroup : false,\r
35044     /**\r
35045      * @cfg {Boolean} groupOnSort\r
35046      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the\r
35047      * existing sort info (defaults to false).\r
35048      */\r
35049     groupOnSort:false,\r
35050 \r
35051         groupDir : 'ASC',\r
35052         \r
35053     /**\r
35054      * Clears any existing grouping and refreshes the data using the default sort.\r
35055      */\r
35056     clearGrouping : function(){\r
35057         this.groupField = false;\r
35058         if(this.remoteGroup){\r
35059             if(this.baseParams){\r
35060                 delete this.baseParams.groupBy;\r
35061             }\r
35062             var lo = this.lastOptions;\r
35063             if(lo && lo.params){\r
35064                 delete lo.params.groupBy;\r
35065             }\r
35066             this.reload();\r
35067         }else{\r
35068             this.applySort();\r
35069             this.fireEvent('datachanged', this);\r
35070         }\r
35071     },\r
35072 \r
35073     /**\r
35074      * Groups the data by the specified field.\r
35075      * @param {String} field The field name by which to sort the store's data\r
35076      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed\r
35077      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)\r
35078      */\r
35079     groupBy : function(field, forceRegroup, direction){\r
35080                 direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;\r
35081         if(this.groupField == field && this.groupDir == direction && !forceRegroup){\r
35082             return; // already grouped by this field\r
35083         }\r
35084         this.groupField = field;\r
35085                 this.groupDir = direction;\r
35086         this.applyGroupField();\r
35087         if(this.groupOnSort){\r
35088             this.sort(field, direction);\r
35089             return;\r
35090         }\r
35091         if(this.remoteGroup){\r
35092             this.reload();\r
35093         }else{\r
35094             var si = this.sortInfo || {};\r
35095             if(si.field != field || si.direction != direction){\r
35096                 this.applySort();\r
35097             }else{\r
35098                 this.sortData(field, direction);\r
35099             }\r
35100             this.fireEvent('datachanged', this);\r
35101         }\r
35102     },\r
35103     \r
35104     // private\r
35105     applyGroupField: function(){\r
35106         if(this.remoteGroup){\r
35107             if(!this.baseParams){\r
35108                 this.baseParams = {};\r
35109             }\r
35110             this.baseParams.groupBy = this.groupField;\r
35111             this.baseParams.groupDir = this.groupDir;\r
35112         }\r
35113     },\r
35114 \r
35115     // private\r
35116     applySort : function(){\r
35117         Ext.data.GroupingStore.superclass.applySort.call(this);\r
35118         if(!this.groupOnSort && !this.remoteGroup){\r
35119             var gs = this.getGroupState();\r
35120             if(gs && (gs != this.sortInfo.field || this.groupDir != this.sortInfo.direction)){\r
35121                 this.sortData(this.groupField, this.groupDir);\r
35122             }\r
35123         }\r
35124     },\r
35125 \r
35126     // private\r
35127     applyGrouping : function(alwaysFireChange){\r
35128         if(this.groupField !== false){\r
35129             this.groupBy(this.groupField, true, this.groupDir);\r
35130             return true;\r
35131         }else{\r
35132             if(alwaysFireChange === true){\r
35133                 this.fireEvent('datachanged', this);\r
35134             }\r
35135             return false;\r
35136         }\r
35137     },\r
35138 \r
35139     // private\r
35140     getGroupState : function(){\r
35141         return this.groupOnSort && this.groupField !== false ?\r
35142                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;\r
35143     }\r
35144 });\r
35145 Ext.reg('groupingstore', Ext.data.GroupingStore);/**\r
35146  * @class Ext.data.DirectProxy\r
35147  * @extends Ext.data.DataProxy\r
35148  */\r
35149 Ext.data.DirectProxy = function(config){\r
35150     Ext.apply(this, config);\r
35151     if(typeof this.paramOrder == 'string'){\r
35152         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
35153     }\r
35154     Ext.data.DirectProxy.superclass.constructor.call(this, config);\r
35155 };\r
35156 \r
35157 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {\r
35158     /**\r
35159      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed\r
35160      * server side.  Specify the params in the order in which they must be executed on the server-side\r
35161      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
35162      * comma, or pipe. For example,\r
35163      * any of the following would be acceptable:<pre><code>\r
35164 paramOrder: ['param1','param2','param3']\r
35165 paramOrder: 'param1 param2 param3'\r
35166 paramOrder: 'param1,param2,param3'\r
35167 paramOrder: 'param1|param2|param'\r
35168      </code></pre>\r
35169      */\r
35170     paramOrder: undefined,\r
35171 \r
35172     /**\r
35173      * @cfg {Boolean} paramsAsHash\r
35174      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a\r
35175      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
35176      */\r
35177     paramsAsHash: true,\r
35178 \r
35179     /**\r
35180      * @cfg {Function} directFn\r
35181      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter\r
35182      * for Store's which will not implement a full CRUD api.\r
35183      */\r
35184     directFn : undefined,\r
35185 \r
35186     // protected\r
35187     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
35188         var args = [];\r
35189         var directFn = this.api[action] || this.directFn;\r
35190 \r
35191         switch (action) {\r
35192             case Ext.data.Api.actions.create:\r
35193                 args.push(params.jsonData[reader.meta.root]);           // <-- create(Hash)\r
35194                 break;\r
35195             case Ext.data.Api.actions.read:\r
35196                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.\r
35197                 if(directFn.directCfg.method.len > 0){\r
35198                     if(this.paramOrder){\r
35199                         for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
35200                             args.push(params[this.paramOrder[i]]);\r
35201                         }\r
35202                     }else if(this.paramsAsHash){\r
35203                         args.push(params);\r
35204                     }\r
35205                 }\r
35206                 break;\r
35207             case Ext.data.Api.actions.update:\r
35208                 args.push(params.jsonData[reader.meta.root]);        // <-- update(Hash/Hash[])\r
35209                 break;\r
35210             case Ext.data.Api.actions.destroy:\r
35211                 args.push(params.jsonData[reader.meta.root]);        // <-- destroy(Int/Int[])\r
35212                 break;\r
35213         }\r
35214 \r
35215         var trans = {\r
35216             params : params || {},\r
35217             request: {\r
35218                 callback : callback,\r
35219                 scope : scope,\r
35220                 arg : options\r
35221             },\r
35222             reader: reader\r
35223         };\r
35224 \r
35225         args.push(this.createCallback(action, rs, trans), this);\r
35226         directFn.apply(window, args);\r
35227     },\r
35228 \r
35229     // private\r
35230     createCallback : function(action, rs, trans) {\r
35231         return function(result, res) {\r
35232             if (!res.status) {\r
35233                 // @deprecated fire loadexception\r
35234                 if (action === Ext.data.Api.actions.read) {\r
35235                     this.fireEvent("loadexception", this, trans, res, null);\r
35236                 }\r
35237                 this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
35238                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
35239                 return;\r
35240             }\r
35241             if (action === Ext.data.Api.actions.read) {\r
35242                 this.onRead(action, trans, result, res);\r
35243             } else {\r
35244                 this.onWrite(action, trans, result, res, rs);\r
35245             }\r
35246         };\r
35247     },\r
35248     /**\r
35249      * Callback for read actions\r
35250      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35251      * @param {Object} trans The request transaction object\r
35252      * @param {Object} res The server response\r
35253      * @private\r
35254      */\r
35255     onRead : function(action, trans, result, res) {\r
35256         var records;\r
35257         try {\r
35258             records = trans.reader.readRecords(result);\r
35259         }\r
35260         catch (ex) {\r
35261             // @deprecated: Fire old loadexception for backwards-compat.\r
35262             this.fireEvent("loadexception", this, trans, res, ex);\r
35263 \r
35264             this.fireEvent('exception', this, 'response', action, trans, res, ex);\r
35265             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
35266             return;\r
35267         }\r
35268         this.fireEvent("load", this, res, trans.request.arg);\r
35269         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);\r
35270     },\r
35271     /**\r
35272      * Callback for write actions\r
35273      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35274      * @param {Object} trans The request transaction object\r
35275      * @param {Object} res The server response\r
35276      * @private\r
35277      */\r
35278     onWrite : function(action, trans, result, res, rs) {\r
35279         var data = trans.reader.extractData(result);\r
35280         this.fireEvent("write", this, action, data, res, rs, trans.request.arg);\r
35281         trans.request.callback.call(trans.request.scope, data, res, true);\r
35282     }\r
35283 });\r
35284 \r
35285 /**\r
35286  * @class Ext.data.DirectStore\r
35287  * @extends Ext.data.Store\r
35288  * <p>Small helper class to create an {@link Ext.data.Store} configured with an\r
35289  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting\r
35290  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.\r
35291  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}\r
35292  * configured as needed.</p>\r
35293  *\r
35294  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>\r
35295  * <div><ul class="mdetail-params">\r
35296  * <li><b>{@link Ext.data.Store Store}</b></li>\r
35297  * <div class="sub-desc"><ul class="mdetail-params">\r
35298  *\r
35299  * </ul></div>\r
35300  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>\r
35301  * <div class="sub-desc"><ul class="mdetail-params">\r
35302  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>\r
35303  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>\r
35304  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>\r
35305  * </ul></div>\r
35306  *\r
35307  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>\r
35308  * <div class="sub-desc"><ul class="mdetail-params">\r
35309  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>\r
35310  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>\r
35311  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>\r
35312  * </ul></div>\r
35313  * </ul></div>\r
35314  *\r
35315  * @xtype directstore\r
35316  *\r
35317  * @constructor\r
35318  * @param {Object} config\r
35319  */\r
35320 Ext.data.DirectStore = function(c){\r
35321     // each transaction upon a singe record will generatie a distinct Direct transaction since Direct queues them into one Ajax request.\r
35322     c.batchTransactions = false;\r
35323 \r
35324     Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {\r
35325         proxy: (typeof(c.proxy) == 'undefined') ? new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')) : c.proxy,\r
35326         reader: (typeof(c.reader) == 'undefined' && typeof(c.fields) == 'object') ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader\r
35327     }));\r
35328 };\r
35329 Ext.extend(Ext.data.DirectStore, Ext.data.Store, {});\r
35330 Ext.reg('directstore', Ext.data.DirectStore);
35331 /**\r
35332  * @class Ext.Direct\r
35333  * @extends Ext.util.Observable\r
35334  * <p><b><u>Overview</u></b></p>\r
35335  * \r
35336  * <p>Ext.Direct aims to streamline communication between the client and server\r
35337  * by providing a single interface that reduces the amount of common code\r
35338  * typically required to validate data and handle returned data packets\r
35339  * (reading data, error conditions, etc).</p>\r
35340  *  \r
35341  * <p>The Ext.direct namespace includes several classes for a closer integration\r
35342  * with the server-side. The Ext.data namespace also includes classes for working\r
35343  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>\r
35344  * \r
35345  * <p><b><u>Specification</u></b></p>\r
35346  * \r
35347  * <p>For additional information consult the \r
35348  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>\r
35349  *   \r
35350  * <p><b><u>Providers</u></b></p>\r
35351  * \r
35352  * <p>Ext.Direct uses a provider architecture, where one or more providers are\r
35353  * used to transport data to and from the server. There are several providers\r
35354  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>\r
35355  * \r
35356  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>\r
35357  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>\r
35358  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side\r
35359  * on the client.</li>\r
35360  * </ul></div>\r
35361  * \r
35362  * <p>A provider does not need to be invoked directly, providers are added via\r
35363  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>\r
35364  * \r
35365  * <p><b><u>Router</u></b></p>\r
35366  * \r
35367  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client\r
35368  * to the appropriate server-side method. Because the Ext.Direct API is completely\r
35369  * platform-agnostic, you could completely swap out a Java based server solution\r
35370  * and replace it with one that uses C# without changing the client side JavaScript\r
35371  * at all.</p>\r
35372  * \r
35373  * <p><b><u>Server side events</u></b></p>\r
35374  * \r
35375  * <p>Custom events from the server may be handled by the client by adding\r
35376  * listeners, for example:</p>\r
35377  * <pre><code>\r
35378 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}\r
35379 \r
35380 // add a handler for a 'message' event sent by the server \r
35381 Ext.Direct.on('message', function(e){\r
35382     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));\r
35383             out.el.scrollTo('t', 100000, true);\r
35384 });\r
35385  * </code></pre>\r
35386  * @singleton\r
35387  */\r
35388 Ext.Direct = Ext.extend(Ext.util.Observable, {\r
35389     /**\r
35390      * Each event type implements a getData() method. The default event types are:\r
35391      * <div class="mdetail-params"><ul>\r
35392      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>\r
35393      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>\r
35394      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>\r
35395      * </ul></div>\r
35396      * @property eventTypes\r
35397      * @type Object\r
35398      */\r
35399 \r
35400     /**\r
35401      * Four types of possible exceptions which can occur:\r
35402      * <div class="mdetail-params"><ul>\r
35403      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>\r
35404      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>\r
35405      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>\r
35406      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>\r
35407      * </ul></div>\r
35408      * @property exceptions\r
35409      * @type Object\r
35410      */\r
35411     exceptions: {\r
35412         TRANSPORT: 'xhr',\r
35413         PARSE: 'parse',\r
35414         LOGIN: 'login',\r
35415         SERVER: 'exception'\r
35416     },\r
35417     \r
35418     // private\r
35419     constructor: function(){\r
35420         this.addEvents(\r
35421             /**\r
35422              * @event event\r
35423              * Fires after an event.\r
35424              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
35425              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
35426              */\r
35427             'event',\r
35428             /**\r
35429              * @event exception\r
35430              * Fires after an event exception.\r
35431              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
35432              */\r
35433             'exception'\r
35434         );\r
35435         this.transactions = {};\r
35436         this.providers = {};\r
35437     },\r
35438 \r
35439     /**\r
35440      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.\r
35441      * If the provider is not already connected, it will auto-connect.\r
35442      * <pre><code>\r
35443 var pollProv = new Ext.direct.PollingProvider({\r
35444     url: 'php/poll2.php'\r
35445 }); \r
35446 \r
35447 Ext.Direct.addProvider(\r
35448     {\r
35449         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider} \r
35450         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.\r
35451         "actions":{              // each property within the actions object represents a Class \r
35452             "TestAction":[       // array of methods within each server side Class   \r
35453             {\r
35454                 "name":"doEcho", // name of method\r
35455                 "len":1\r
35456             },{\r
35457                 "name":"multiply",\r
35458                 "len":1\r
35459             },{\r
35460                 "name":"doForm",\r
35461                 "formHandler":true, // handle form on server with Ext.Direct.Transaction \r
35462                 "len":1\r
35463             }]\r
35464         },\r
35465         "namespace":"myApplication",// namespace to create the Remoting Provider in\r
35466     },{\r
35467         type: 'polling', // create a {@link Ext.direct.PollingProvider} \r
35468         url:  'php/poll.php'\r
35469     },\r
35470     pollProv // reference to previously created instance\r
35471 );\r
35472      * </code></pre>\r
35473      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance\r
35474      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each\r
35475      * Provider description instructs Ext.Direct how to create client-side stub methods.\r
35476      */\r
35477     addProvider : function(provider){        \r
35478         var a = arguments;\r
35479         if(a.length > 1){\r
35480             for(var i = 0, len = a.length; i < len; i++){\r
35481                 this.addProvider(a[i]);\r
35482             }\r
35483             return;\r
35484         }\r
35485         \r
35486         // if provider has not already been instantiated\r
35487         if(!provider.events){\r
35488             provider = new Ext.Direct.PROVIDERS[provider.type](provider);\r
35489         }\r
35490         provider.id = provider.id || Ext.id();\r
35491         this.providers[provider.id] = provider;\r
35492 \r
35493         provider.on('data', this.onProviderData, this);\r
35494         provider.on('exception', this.onProviderException, this);\r
35495 \r
35496 \r
35497         if(!provider.isConnected()){\r
35498             provider.connect();\r
35499         }\r
35500 \r
35501         return provider;\r
35502     },\r
35503 \r
35504     /**\r
35505      * Retrieve a {@link Ext.direct.Provider provider} by the\r
35506      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is\r
35507      * {@link #addProvider added}.\r
35508      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider} \r
35509      */\r
35510     getProvider : function(id){\r
35511         return this.providers[id];\r
35512     },\r
35513 \r
35514     removeProvider : function(id){\r
35515         var provider = id.id ? id : this.providers[id.id];\r
35516         provider.un('data', this.onProviderData, this);\r
35517         provider.un('exception', this.onProviderException, this);\r
35518         delete this.providers[provider.id];\r
35519         return provider;\r
35520     },\r
35521 \r
35522     addTransaction: function(t){\r
35523         this.transactions[t.tid] = t;\r
35524         return t;\r
35525     },\r
35526 \r
35527     removeTransaction: function(t){\r
35528         delete this.transactions[t.tid || t];\r
35529         return t;\r
35530     },\r
35531 \r
35532     getTransaction: function(tid){\r
35533         return this.transactions[tid.tid || tid];\r
35534     },\r
35535 \r
35536     onProviderData : function(provider, e){\r
35537         if(Ext.isArray(e)){\r
35538             for(var i = 0, len = e.length; i < len; i++){\r
35539                 this.onProviderData(provider, e[i]);\r
35540             }\r
35541             return;\r
35542         }\r
35543         if(e.name && e.name != 'event' && e.name != 'exception'){\r
35544             this.fireEvent(e.name, e);\r
35545         }else if(e.type == 'exception'){\r
35546             this.fireEvent('exception', e);\r
35547         }\r
35548         this.fireEvent('event', e, provider);\r
35549     },\r
35550 \r
35551     createEvent : function(response, extraProps){\r
35552         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));\r
35553     }\r
35554 });\r
35555 // overwrite impl. with static instance\r
35556 Ext.Direct = new Ext.Direct();\r
35557 \r
35558 Ext.Direct.TID = 1;\r
35559 Ext.Direct.PROVIDERS = {};/**\r
35560  * @class Ext.Direct.Transaction\r
35561  * @extends Object\r
35562  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>\r
35563  * @constructor\r
35564  * @param {Object} config\r
35565  */\r
35566 Ext.Direct.Transaction = function(config){\r
35567     Ext.apply(this, config);\r
35568     this.tid = ++Ext.Direct.TID;\r
35569     this.retryCount = 0;\r
35570 };\r
35571 Ext.Direct.Transaction.prototype = {\r
35572     send: function(){\r
35573         this.provider.queueTransaction(this);\r
35574     },\r
35575 \r
35576     retry: function(){\r
35577         this.retryCount++;\r
35578         this.send();\r
35579     },\r
35580 \r
35581     getProvider: function(){\r
35582         return this.provider;\r
35583     }\r
35584 };Ext.Direct.Event = function(config){\r
35585     Ext.apply(this, config);\r
35586 }\r
35587 Ext.Direct.Event.prototype = {\r
35588     status: true,\r
35589     getData: function(){\r
35590         return this.data;\r
35591     }\r
35592 };\r
35593 \r
35594 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {\r
35595     type: 'rpc',\r
35596     getTransaction: function(){\r
35597         return this.transaction || Ext.Direct.getTransaction(this.tid);\r
35598     }\r
35599 });\r
35600 \r
35601 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {\r
35602     status: false,\r
35603     type: 'exception'\r
35604 });\r
35605 \r
35606 Ext.Direct.eventTypes = {\r
35607     'rpc':  Ext.Direct.RemotingEvent,\r
35608     'event':  Ext.Direct.Event,\r
35609     'exception':  Ext.Direct.ExceptionEvent\r
35610 };\r
35611 \r
35612 /**\r
35613  * @class Ext.direct.Provider\r
35614  * @extends Ext.util.Observable\r
35615  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>\r
35616  * \r
35617  * <p>For example ExtJs implements the following subclasses:</p>\r
35618  * <pre><code>\r
35619 Provider\r
35620 |\r
35621 +---{@link Ext.direct.JsonProvider JsonProvider} \r
35622     |\r
35623     +---{@link Ext.direct.PollingProvider PollingProvider}   \r
35624     |\r
35625     +---{@link Ext.direct.RemotingProvider RemotingProvider}   \r
35626  * </code></pre>\r
35627  * @abstract\r
35628  */\r
35629 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    \r
35630     /**\r
35631      * @cfg {String} id\r
35632      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).\r
35633      * You should assign an id if you need to be able to access the provider later and you do\r
35634      * not have an object reference available, for example:\r
35635      * <pre><code>\r
35636 Ext.Direct.addProvider(\r
35637     {\r
35638         type: 'polling',\r
35639         url:  'php/poll.php',\r
35640         id:   'poll-provider'\r
35641     }\r
35642 );\r
35643      \r
35644 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');\r
35645 p.disconnect();\r
35646      * </code></pre>\r
35647      */\r
35648         \r
35649     /**\r
35650      * @cfg {Number} priority\r
35651      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).\r
35652      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.\r
35653      */    \r
35654     priority: 1,\r
35655 \r
35656     /**\r
35657      * @cfg {String} type\r
35658      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified\r
35659      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a\r
35660      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>\r
35661      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>\r
35662      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>\r
35663      * </ul></div>\r
35664      */    \r
35665  \r
35666     // private\r
35667     constructor : function(config){\r
35668         Ext.apply(this, config);\r
35669         this.addEvents(\r
35670             /**\r
35671              * @event connect\r
35672              * Fires when the Provider connects to the server-side\r
35673              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
35674              */            \r
35675             'connect',\r
35676             /**\r
35677              * @event disconnect\r
35678              * Fires when the Provider disconnects from the server-side\r
35679              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
35680              */            \r
35681             'disconnect',\r
35682             /**\r
35683              * @event data\r
35684              * Fires when the Provider receives data from the server-side\r
35685              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
35686              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
35687              */            \r
35688             'data',\r
35689             /**\r
35690              * @event exception\r
35691              * Fires when the Provider receives an exception from the server-side\r
35692              */                        \r
35693             'exception'\r
35694         );\r
35695         Ext.direct.Provider.superclass.constructor.call(this, config);\r
35696     },\r
35697 \r
35698     /**\r
35699      * Returns whether or not the server-side is currently connected.\r
35700      * Abstract method for subclasses to implement.\r
35701      */\r
35702     isConnected: function(){\r
35703         return false;\r
35704     },\r
35705 \r
35706     /**\r
35707      * Abstract methods for subclasses to implement.\r
35708      */\r
35709     connect: Ext.emptyFn,\r
35710     \r
35711     /**\r
35712      * Abstract methods for subclasses to implement.\r
35713      */\r
35714     disconnect: Ext.emptyFn\r
35715 });\r
35716 /**\r
35717  * @class Ext.direct.JsonProvider\r
35718  * @extends Ext.direct.Provider\r
35719  */\r
35720 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {\r
35721     parseResponse: function(xhr){\r
35722         if(!Ext.isEmpty(xhr.responseText)){\r
35723             if(typeof xhr.responseText == 'object'){\r
35724                 return xhr.responseText;\r
35725             }\r
35726             return Ext.decode(xhr.responseText);\r
35727         }\r
35728         return null;\r
35729     },\r
35730 \r
35731     getEvents: function(xhr){\r
35732         var data = null;\r
35733         try{\r
35734             data = this.parseResponse(xhr);\r
35735         }catch(e){\r
35736             var event = new Ext.Direct.ExceptionEvent({\r
35737                 data: e,\r
35738                 xhr: xhr,\r
35739                 code: Ext.Direct.exceptions.PARSE,\r
35740                 message: 'Error parsing json response: \n\n ' + data\r
35741             })\r
35742             return [event];\r
35743         }\r
35744         var events = [];\r
35745         if(Ext.isArray(data)){\r
35746             for(var i = 0, len = data.length; i < len; i++){\r
35747                 events.push(Ext.Direct.createEvent(data[i]));\r
35748             }\r
35749         }else{\r
35750             events.push(Ext.Direct.createEvent(data));\r
35751         }\r
35752         return events;\r
35753     }\r
35754 });/**\r
35755  * @class Ext.direct.PollingProvider\r
35756  * @extends Ext.direct.JsonProvider\r
35757  *\r
35758  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.\r
35759  * The initial request for data originates from the client, and then is responded to by the\r
35760  * server.</p>\r
35761  * \r
35762  * <p>All configurations for the PollingProvider should be generated by the server-side\r
35763  * API portion of the Ext.Direct stack.</p>\r
35764  *\r
35765  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply\r
35766  * specifying <tt>type = 'polling'</tt>.  For example:</p>\r
35767  * <pre><code>\r
35768 var pollA = new Ext.direct.PollingProvider({\r
35769     type:'polling',\r
35770     url: 'php/pollA.php',\r
35771 });\r
35772 Ext.Direct.addProvider(pollA);\r
35773 pollA.disconnect();\r
35774 \r
35775 Ext.Direct.addProvider(\r
35776     {\r
35777         type:'polling',\r
35778         url: 'php/pollB.php',\r
35779         id: 'pollB-provider'\r
35780     }\r
35781 );\r
35782 var pollB = Ext.Direct.getProvider('pollB-provider');\r
35783  * </code></pre>\r
35784  */\r
35785 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {\r
35786     /**\r
35787      * @cfg {Number} priority\r
35788      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.\r
35789      */\r
35790     // override default priority\r
35791     priority: 3,\r
35792     \r
35793     /**\r
35794      * @cfg {Number} interval\r
35795      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every\r
35796      * 3 seconds).\r
35797      */\r
35798     interval: 3000,\r
35799 \r
35800     /**\r
35801      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters\r
35802      * on every polling request\r
35803      */\r
35804     \r
35805     /**\r
35806      * @cfg {String/Function} url\r
35807      * The url which the PollingProvider should contact with each request. This can also be\r
35808      * an imported Ext.Direct method which will accept the baseParams as its only argument.\r
35809      */\r
35810 \r
35811     // private\r
35812     constructor : function(config){\r
35813         Ext.direct.PollingProvider.superclass.constructor.call(this, config);\r
35814         this.addEvents(\r
35815             /**\r
35816              * @event beforepoll\r
35817              * Fired immediately before a poll takes place, an event handler can return false\r
35818              * in order to cancel the poll.\r
35819              * @param {Ext.direct.PollingProvider}\r
35820              */\r
35821             'beforepoll',            \r
35822             /**\r
35823              * @event poll\r
35824              * This event has not yet been implemented.\r
35825              * @param {Ext.direct.PollingProvider}\r
35826              */\r
35827             'poll'\r
35828         );\r
35829     },\r
35830 \r
35831     // inherited\r
35832     isConnected: function(){\r
35833         return !!this.pollTask;\r
35834     },\r
35835 \r
35836     /**\r
35837      * Connect to the server-side and begin the polling process. To handle each\r
35838      * response subscribe to the data event.\r
35839      */\r
35840     connect: function(){\r
35841         if(this.url && !this.pollTask){\r
35842             this.pollTask = Ext.TaskMgr.start({\r
35843                 run: function(){\r
35844                     if(this.fireEvent('beforepoll', this) !== false){\r
35845                         if(typeof this.url == 'function'){\r
35846                             this.url(this.baseParams);\r
35847                         }else{\r
35848                             Ext.Ajax.request({\r
35849                                 url: this.url,\r
35850                                 callback: this.onData,\r
35851                                 scope: this,\r
35852                                 params: this.baseParams\r
35853                             });\r
35854                         }\r
35855                     }\r
35856                 },\r
35857                 interval: this.interval,\r
35858                 scope: this\r
35859             });\r
35860             this.fireEvent('connect', this);\r
35861         }else if(!this.url){\r
35862             throw 'Error initializing PollingProvider, no url configured.';\r
35863         }\r
35864     },\r
35865 \r
35866     /**\r
35867      * Disconnect from the server-side and stop the polling process. The disconnect\r
35868      * event will be fired on a successful disconnect.\r
35869      */\r
35870     disconnect: function(){\r
35871         if(this.pollTask){\r
35872             Ext.TaskMgr.stop(this.pollTask);\r
35873             delete this.pollTask;\r
35874             this.fireEvent('disconnect', this);\r
35875         }\r
35876     },\r
35877 \r
35878     // private\r
35879     onData: function(opt, success, xhr){\r
35880         if(success){\r
35881             var events = this.getEvents(xhr);\r
35882             for(var i = 0, len = events.length; i < len; i++){\r
35883                 var e = events[i];\r
35884                 this.fireEvent('data', this, e);\r
35885             }\r
35886         }else{\r
35887             var e = new Ext.Direct.ExceptionEvent({\r
35888                 data: e,\r
35889                 code: Ext.Direct.exceptions.TRANSPORT,\r
35890                 message: 'Unable to connect to the server.',\r
35891                 xhr: xhr\r
35892             });\r
35893             this.fireEvent('data', this, e);\r
35894         }\r
35895     }\r
35896 });\r
35897 \r
35898 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**\r
35899  * @class Ext.direct.RemotingProvider\r
35900  * @extends Ext.direct.JsonProvider\r
35901  * \r
35902  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to\r
35903  * server side methods on the client (a remote procedure call (RPC) type of\r
35904  * connection where the client can initiate a procedure on the server).</p>\r
35905  * \r
35906  * <p>This allows for code to be organized in a fashion that is maintainable,\r
35907  * while providing a clear path between client and server, something that is\r
35908  * not always apparent when using URLs.</p>\r
35909  * \r
35910  * <p>To accomplish this the server-side needs to describe what classes and methods\r
35911  * are available on the client-side. This configuration will typically be\r
35912  * outputted by the server-side Ext.Direct stack when the API description is built.</p>\r
35913  */\r
35914 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       \r
35915     /**\r
35916      * @cfg {Object} actions\r
35917      * Object literal defining the server side actions and methods. For example, if\r
35918      * the Provider is configured with:\r
35919      * <pre><code>\r
35920 "actions":{ // each property within the 'actions' object represents a server side Class \r
35921     "TestAction":[ // array of methods within each server side Class to be   \r
35922     {              // stubbed out on client\r
35923         "name":"doEcho", \r
35924         "len":1            \r
35925     },{\r
35926         "name":"multiply",// name of method\r
35927         "len":2           // The number of parameters that will be used to create an\r
35928                           // array of data to send to the server side function.\r
35929                           // Ensure the server sends back a Number, not a String. \r
35930     },{\r
35931         "name":"doForm",\r
35932         "formHandler":true, // direct the client to use specialized form handling method \r
35933         "len":1\r
35934     }]\r
35935 }\r
35936      * </code></pre>\r
35937      * <p>Note that a Store is not required, a server method can be called at any time.\r
35938      * In the following example a <b>client side</b> handler is used to call the\r
35939      * server side method "multiply" in the server-side "TestAction" Class:</p>\r
35940      * <pre><code>\r
35941 TestAction.multiply(\r
35942     2, 4, // pass two arguments to server, so specify len=2\r
35943     // callback function after the server is called\r
35944     // result: the result returned by the server\r
35945     //      e: Ext.Direct.RemotingEvent object\r
35946     function(result, e){\r
35947         var t = e.getTransaction();\r
35948         var action = t.action; // server side Class called\r
35949         var method = t.method; // server side method called\r
35950         if(e.status){\r
35951             var answer = Ext.encode(result); // 8\r
35952     \r
35953         }else{\r
35954             var msg = e.message; // failure message\r
35955         }\r
35956     }\r
35957 );\r
35958      * </code></pre>\r
35959      * In the example above, the server side "multiply" function will be passed two\r
35960      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be\r
35961      * available as the <tt>result</tt> in the example above. \r
35962      */\r
35963     \r
35964     /**\r
35965      * @cfg {String/Object} namespace\r
35966      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).\r
35967      * Explicitly specify the namespace Object, or specify a String to have a\r
35968      * {@link Ext#namespace namespace created} implicitly.\r
35969      */\r
35970     \r
35971     /**\r
35972      * @cfg {String} url\r
35973      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. \r
35974      */\r
35975     \r
35976     /**\r
35977      * @cfg {String} enableUrlEncode\r
35978      * Specify which param will hold the arguments for the method.\r
35979      * Defaults to <tt>'data'</tt>.\r
35980      */\r
35981     \r
35982     /**\r
35983      * @cfg {Number/Boolean} enableBuffer\r
35984      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method\r
35985      * calls. If a number is specified this is the amount of time in milliseconds\r
35986      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>\r
35987      * <br><p>Calls which are received within the specified timeframe will be\r
35988      * concatenated together and sent in a single request, optimizing the\r
35989      * application by reducing the amount of round trips that have to be made\r
35990      * to the server.</p>\r
35991      */\r
35992     enableBuffer: 10,\r
35993     \r
35994     /**\r
35995      * @cfg {Number} maxRetries\r
35996      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.\r
35997      */\r
35998     maxRetries: 1,\r
35999     \r
36000     /**\r
36001      * @cfg {Number} timeout\r
36002      * The timeout to use for each request. Defaults to <tt>undefined</tt>.\r
36003      */\r
36004     timeout: undefined,\r
36005 \r
36006     constructor : function(config){\r
36007         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);\r
36008         this.addEvents(\r
36009             /**\r
36010              * @event beforecall\r
36011              * Fires immediately before the client-side sends off the RPC call.\r
36012              * By returning false from an event handler you can prevent the call from\r
36013              * executing.\r
36014              * @param {Ext.direct.RemotingProvider} provider\r
36015              * @param {Ext.Direct.Transaction} transaction\r
36016              */            \r
36017             'beforecall',            \r
36018             /**\r
36019              * @event call\r
36020              * Fires immediately after the request to the server-side is sent. This does\r
36021              * NOT fire after the response has come back from the call.\r
36022              * @param {Ext.direct.RemotingProvider} provider\r
36023              * @param {Ext.Direct.Transaction} transaction\r
36024              */            \r
36025             'call'\r
36026         );\r
36027         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;\r
36028         this.transactions = {};\r
36029         this.callBuffer = [];\r
36030     },\r
36031 \r
36032     // private\r
36033     initAPI : function(){\r
36034         var o = this.actions;\r
36035         for(var c in o){\r
36036             var cls = this.namespace[c] || (this.namespace[c] = {}),\r
36037                 ms = o[c];\r
36038             for(var i = 0, len = ms.length; i < len; i++){\r
36039                 var m = ms[i];\r
36040                 cls[m.name] = this.createMethod(c, m);\r
36041             }\r
36042         }\r
36043     },\r
36044 \r
36045     // inherited\r
36046     isConnected: function(){\r
36047         return !!this.connected;\r
36048     },\r
36049 \r
36050     connect: function(){\r
36051         if(this.url){\r
36052             this.initAPI();\r
36053             this.connected = true;\r
36054             this.fireEvent('connect', this);\r
36055         }else if(!this.url){\r
36056             throw 'Error initializing RemotingProvider, no url configured.';\r
36057         }\r
36058     },\r
36059 \r
36060     disconnect: function(){\r
36061         if(this.connected){\r
36062             this.connected = false;\r
36063             this.fireEvent('disconnect', this);\r
36064         }\r
36065     },\r
36066 \r
36067     onData: function(opt, success, xhr){\r
36068         if(success){\r
36069             var events = this.getEvents(xhr);\r
36070             for(var i = 0, len = events.length; i < len; i++){\r
36071                 var e = events[i],\r
36072                     t = this.getTransaction(e);\r
36073                 this.fireEvent('data', this, e);\r
36074                 if(t){\r
36075                     this.doCallback(t, e, true);\r
36076                     Ext.Direct.removeTransaction(t);\r
36077                 }\r
36078             }\r
36079         }else{\r
36080             var ts = [].concat(opt.ts);\r
36081             for(var i = 0, len = ts.length; i < len; i++){\r
36082                 var t = this.getTransaction(ts[i]);\r
36083                 if(t && t.retryCount < this.maxRetries){\r
36084                     t.retry();\r
36085                 }else{\r
36086                     var e = new Ext.Direct.ExceptionEvent({\r
36087                         data: e,\r
36088                         transaction: t,\r
36089                         code: Ext.Direct.exceptions.TRANSPORT,\r
36090                         message: 'Unable to connect to the server.',\r
36091                         xhr: xhr\r
36092                     });\r
36093                     this.fireEvent('data', this, e);\r
36094                     if(t){\r
36095                         this.doCallback(t, e, false);\r
36096                         Ext.Direct.removeTransaction(t);\r
36097                     }\r
36098                 }\r
36099             }\r
36100         }\r
36101     },\r
36102 \r
36103     getCallData: function(t){\r
36104         return {\r
36105             action: t.action,\r
36106             method: t.method,\r
36107             data: t.data,\r
36108             type: 'rpc',\r
36109             tid: t.tid\r
36110         };\r
36111     },\r
36112 \r
36113     doSend : function(data){\r
36114         var o = {\r
36115             url: this.url,\r
36116             callback: this.onData,\r
36117             scope: this,\r
36118             ts: data,\r
36119             timeout: this.timeout\r
36120         }, callData;\r
36121 \r
36122         if(Ext.isArray(data)){\r
36123             callData = [];\r
36124             for(var i = 0, len = data.length; i < len; i++){\r
36125                 callData.push(this.getCallData(data[i]));\r
36126             }\r
36127         }else{\r
36128             callData = this.getCallData(data);\r
36129         }\r
36130 \r
36131         if(this.enableUrlEncode){\r
36132             var params = {};\r
36133             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);\r
36134             o.params = params;\r
36135         }else{\r
36136             o.jsonData = callData;\r
36137         }\r
36138         Ext.Ajax.request(o);\r
36139     },\r
36140 \r
36141     combineAndSend : function(){\r
36142         var len = this.callBuffer.length;\r
36143         if(len > 0){\r
36144             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);\r
36145             this.callBuffer = [];\r
36146         }\r
36147     },\r
36148 \r
36149     queueTransaction: function(t){\r
36150         if(t.form){\r
36151             this.processForm(t);\r
36152             return;\r
36153         }\r
36154         this.callBuffer.push(t);\r
36155         if(this.enableBuffer){\r
36156             if(!this.callTask){\r
36157                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);\r
36158             }\r
36159             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);\r
36160         }else{\r
36161             this.combineAndSend();\r
36162         }\r
36163     },\r
36164 \r
36165     doCall : function(c, m, args){\r
36166         var data = null, hs = args[m.len], scope = args[m.len+1];\r
36167 \r
36168         if(m.len !== 0){\r
36169             data = args.slice(0, m.len);\r
36170         }\r
36171 \r
36172         var t = new Ext.Direct.Transaction({\r
36173             provider: this,\r
36174             args: args,\r
36175             action: c,\r
36176             method: m.name,\r
36177             data: data,\r
36178             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs\r
36179         });\r
36180 \r
36181         if(this.fireEvent('beforecall', this, t) !== false){\r
36182             Ext.Direct.addTransaction(t);\r
36183             this.queueTransaction(t);\r
36184             this.fireEvent('call', this, t);\r
36185         }\r
36186     },\r
36187 \r
36188     doForm : function(c, m, form, callback, scope){\r
36189         var t = new Ext.Direct.Transaction({\r
36190             provider: this,\r
36191             action: c,\r
36192             method: m.name,\r
36193             args:[form, callback, scope],\r
36194             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,\r
36195             isForm: true\r
36196         });\r
36197 \r
36198         if(this.fireEvent('beforecall', this, t) !== false){\r
36199             Ext.Direct.addTransaction(t);\r
36200             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',\r
36201                 params = {\r
36202                     extTID: t.tid,\r
36203                     extAction: c,\r
36204                     extMethod: m.name,\r
36205                     extType: 'rpc',\r
36206                     extUpload: String(isUpload)\r
36207                 };\r
36208             \r
36209             // change made from typeof callback check to callback.params\r
36210             // to support addl param passing in DirectSubmit EAC 6/2\r
36211             Ext.apply(t, {\r
36212                 form: Ext.getDom(form),\r
36213                 isUpload: isUpload,\r
36214                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params\r
36215             });\r
36216             this.fireEvent('call', this, t);\r
36217             this.processForm(t);\r
36218         }\r
36219     },\r
36220     \r
36221     processForm: function(t){\r
36222         Ext.Ajax.request({\r
36223             url: this.url,\r
36224             params: t.params,\r
36225             callback: this.onData,\r
36226             scope: this,\r
36227             form: t.form,\r
36228             isUpload: t.isUpload,\r
36229             ts: t\r
36230         });\r
36231     },\r
36232 \r
36233     createMethod : function(c, m){\r
36234         var f;\r
36235         if(!m.formHandler){\r
36236             f = function(){\r
36237                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));\r
36238             }.createDelegate(this);\r
36239         }else{\r
36240             f = function(form, callback, scope){\r
36241                 this.doForm(c, m, form, callback, scope);\r
36242             }.createDelegate(this);\r
36243         }\r
36244         f.directCfg = {\r
36245             action: c,\r
36246             method: m\r
36247         };\r
36248         return f;\r
36249     },\r
36250 \r
36251     getTransaction: function(opt){\r
36252         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;\r
36253     },\r
36254 \r
36255     doCallback: function(t, e){\r
36256         var fn = e.status ? 'success' : 'failure';\r
36257         if(t && t.cb){\r
36258             var hs = t.cb,\r
36259                 result = Ext.isDefined(e.result) ? e.result : e.data;\r
36260             if(Ext.isFunction(hs)){\r
36261                 hs(result, e);\r
36262             } else{\r
36263                 Ext.callback(hs[fn], hs.scope, [result, e]);\r
36264                 Ext.callback(hs.callback, hs.scope, [result, e]);\r
36265             }\r
36266         }\r
36267     }\r
36268 });\r
36269 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/**\r
36270  * @class Ext.Resizable\r
36271  * @extends Ext.util.Observable\r
36272  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element \r
36273  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap\r
36274  * 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
36275  * the element will be wrapped for you automatically.</p>\r
36276  * <p>Here is the list of valid resize handles:</p>\r
36277  * <pre>\r
36278 Value   Description\r
36279 ------  -------------------\r
36280  'n'     north\r
36281  's'     south\r
36282  'e'     east\r
36283  'w'     west\r
36284  'nw'    northwest\r
36285  'sw'    southwest\r
36286  'se'    southeast\r
36287  'ne'    northeast\r
36288  'all'   all\r
36289 </pre>\r
36290  * <p>Here's an example showing the creation of a typical Resizable:</p>\r
36291  * <pre><code>\r
36292 var resizer = new Ext.Resizable('element-id', {\r
36293     handles: 'all',\r
36294     minWidth: 200,\r
36295     minHeight: 100,\r
36296     maxWidth: 500,\r
36297     maxHeight: 400,\r
36298     pinned: true\r
36299 });\r
36300 resizer.on('resize', myHandler);\r
36301 </code></pre>\r
36302  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>\r
36303  * resizer.east.setDisplayed(false);</p>\r
36304  * @constructor\r
36305  * Create a new resizable component\r
36306  * @param {Mixed} el The id or element to resize\r
36307  * @param {Object} config configuration options\r
36308   */\r
36309 Ext.Resizable = function(el, config){\r
36310     this.el = Ext.get(el);\r
36311     \r
36312     if(config && config.wrap){\r
36313         config.resizeChild = this.el;\r
36314         this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});\r
36315         this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';\r
36316         this.el.setStyle('overflow', 'hidden');\r
36317         this.el.setPositioning(config.resizeChild.getPositioning());\r
36318         config.resizeChild.clearPositioning();\r
36319         if(!config.width || !config.height){\r
36320             var csize = config.resizeChild.getSize();\r
36321             this.el.setSize(csize.width, csize.height);\r
36322         }\r
36323         if(config.pinned && !config.adjustments){\r
36324             config.adjustments = 'auto';\r
36325         }\r
36326     }\r
36327 \r
36328     /**\r
36329      * The proxy Element that is resized in place of the real Element during the resize operation.\r
36330      * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.\r
36331      * Read only.\r
36332      * @type Ext.Element.\r
36333      * @property proxy\r
36334      */\r
36335     this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());\r
36336     this.proxy.unselectable();\r
36337     this.proxy.enableDisplayMode('block');\r
36338 \r
36339     Ext.apply(this, config);\r
36340     \r
36341     if(this.pinned){\r
36342         this.disableTrackOver = true;\r
36343         this.el.addClass('x-resizable-pinned');\r
36344     }\r
36345     // if the element isn't positioned, make it relative\r
36346     var position = this.el.getStyle('position');\r
36347     if(position != 'absolute' && position != 'fixed'){\r
36348         this.el.setStyle('position', 'relative');\r
36349     }\r
36350     if(!this.handles){ // no handles passed, must be legacy style\r
36351         this.handles = 's,e,se';\r
36352         if(this.multiDirectional){\r
36353             this.handles += ',n,w';\r
36354         }\r
36355     }\r
36356     if(this.handles == 'all'){\r
36357         this.handles = 'n s e w ne nw se sw';\r
36358     }\r
36359     var hs = this.handles.split(/\s*?[,;]\s*?| /);\r
36360     var ps = Ext.Resizable.positions;\r
36361     for(var i = 0, len = hs.length; i < len; i++){\r
36362         if(hs[i] && ps[hs[i]]){\r
36363             var pos = ps[hs[i]];\r
36364             this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);\r
36365         }\r
36366     }\r
36367     // legacy\r
36368     this.corner = this.southeast;\r
36369     \r
36370     if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){\r
36371         this.updateBox = true;\r
36372     }   \r
36373    \r
36374     this.activeHandle = null;\r
36375     \r
36376     if(this.resizeChild){\r
36377         if(typeof this.resizeChild == 'boolean'){\r
36378             this.resizeChild = Ext.get(this.el.dom.firstChild, true);\r
36379         }else{\r
36380             this.resizeChild = Ext.get(this.resizeChild, true);\r
36381         }\r
36382     }\r
36383     \r
36384     if(this.adjustments == 'auto'){\r
36385         var rc = this.resizeChild;\r
36386         var hw = this.west, he = this.east, hn = this.north, hs = this.south;\r
36387         if(rc && (hw || hn)){\r
36388             rc.position('relative');\r
36389             rc.setLeft(hw ? hw.el.getWidth() : 0);\r
36390             rc.setTop(hn ? hn.el.getHeight() : 0);\r
36391         }\r
36392         this.adjustments = [\r
36393             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),\r
36394             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1 \r
36395         ];\r
36396     }\r
36397     \r
36398     if(this.draggable){\r
36399         this.dd = this.dynamic ? \r
36400             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});\r
36401         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);\r
36402     }\r
36403     \r
36404     this.addEvents(\r
36405         /**\r
36406          * @event beforeresize\r
36407          * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.\r
36408          * @param {Ext.Resizable} this\r
36409          * @param {Ext.EventObject} e The mousedown event\r
36410          */\r
36411         'beforeresize',\r
36412         /**\r
36413          * @event resize\r
36414          * Fired after a resize.\r
36415          * @param {Ext.Resizable} this\r
36416          * @param {Number} width The new width\r
36417          * @param {Number} height The new height\r
36418          * @param {Ext.EventObject} e The mouseup event\r
36419          */\r
36420         'resize'\r
36421     );\r
36422     \r
36423     if(this.width !== null && this.height !== null){\r
36424         this.resizeTo(this.width, this.height);\r
36425     }else{\r
36426         this.updateChildSize();\r
36427     }\r
36428     if(Ext.isIE){\r
36429         this.el.dom.style.zoom = 1;\r
36430     }\r
36431     Ext.Resizable.superclass.constructor.call(this);\r
36432 };\r
36433 \r
36434 Ext.extend(Ext.Resizable, Ext.util.Observable, {\r
36435 \r
36436     /**\r
36437      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the\r
36438      * resize operation's new size (defaults to <tt>[0, 0]</tt>)\r
36439      */\r
36440     adjustments : [0, 0],\r
36441     /**\r
36442      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)\r
36443      */\r
36444     animate : false,\r
36445     /**\r
36446      * @cfg {Mixed} constrainTo Constrain the resize to a particular element\r
36447      */\r
36448     /**\r
36449      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)\r
36450      */\r
36451     disableTrackOver : false,\r
36452     /**\r
36453      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)\r
36454      */\r
36455     draggable: false,\r
36456     /**\r
36457      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)\r
36458      */\r
36459     duration : 0.35,\r
36460     /**\r
36461      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)\r
36462      */\r
36463     dynamic : false,\r
36464     /**\r
36465      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)\r
36466      */\r
36467     easing : 'easeOutStrong',\r
36468     /**\r
36469      * @cfg {Boolean} enabled False to disable resizing (defaults to true)\r
36470      */\r
36471     enabled : true,\r
36472     /**\r
36473      * @property enabled Writable. False if resizing is disabled.\r
36474      * @type Boolean \r
36475      */\r
36476     /**\r
36477      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).\r
36478      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.\r
36479      */\r
36480     handles : false,\r
36481     /**\r
36482      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.\r
36483      */\r
36484     multiDirectional : false,\r
36485     /**\r
36486      * @cfg {Number} height The height of the element in pixels (defaults to null)\r
36487      */\r
36488     height : null,\r
36489     /**\r
36490      * @cfg {Number} width The width of the element in pixels (defaults to null)\r
36491      */\r
36492     width : null,\r
36493     /**\r
36494      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels\r
36495      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
36496      */\r
36497     heightIncrement : 0,\r
36498     /**\r
36499      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels\r
36500      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
36501      */\r
36502     widthIncrement : 0,\r
36503     /**\r
36504      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)\r
36505      */\r
36506     minHeight : 5,\r
36507     /**\r
36508      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)\r
36509      */\r
36510     minWidth : 5,\r
36511     /**\r
36512      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)\r
36513      */\r
36514     maxHeight : 10000,\r
36515     /**\r
36516      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)\r
36517      */\r
36518     maxWidth : 10000,\r
36519     /**\r
36520      * @cfg {Number} minX The minimum x for the element (defaults to 0)\r
36521      */\r
36522     minX: 0,\r
36523     /**\r
36524      * @cfg {Number} minY The minimum x for the element (defaults to 0)\r
36525      */\r
36526     minY: 0,\r
36527     /**\r
36528      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the\r
36529      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)\r
36530      */\r
36531     pinned : false,\r
36532     /**\r
36533      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height\r
36534      * and width during resize (defaults to false)\r
36535      */\r
36536     preserveRatio : false,\r
36537     /**\r
36538      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false) \r
36539      */ \r
36540     resizeChild : false,\r
36541     /**\r
36542      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)\r
36543      */\r
36544     transparent: false,\r
36545     /**\r
36546      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region\r
36547      */\r
36548     /**\r
36549      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)\r
36550      * in favor of the handles config option (defaults to false)\r
36551      */\r
36552 \r
36553     \r
36554     /**\r
36555      * Perform a manual resize and fires the 'resize' event.\r
36556      * @param {Number} width\r
36557      * @param {Number} height\r
36558      */\r
36559     resizeTo : function(width, height){\r
36560         this.el.setSize(width, height);\r
36561         this.updateChildSize();\r
36562         this.fireEvent('resize', this, width, height, null);\r
36563     },\r
36564 \r
36565     // private\r
36566     startSizing : function(e, handle){\r
36567         this.fireEvent('beforeresize', this, e);\r
36568         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler\r
36569 \r
36570             if(!this.overlay){\r
36571                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());\r
36572                 this.overlay.unselectable();\r
36573                 this.overlay.enableDisplayMode('block');\r
36574                 this.overlay.on({\r
36575                     scope: this,\r
36576                     mousemove: this.onMouseMove,\r
36577                     mouseup: this.onMouseUp\r
36578                 });\r
36579             }\r
36580             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));\r
36581 \r
36582             this.resizing = true;\r
36583             this.startBox = this.el.getBox();\r
36584             this.startPoint = e.getXY();\r
36585             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],\r
36586                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];\r
36587 \r
36588             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
36589             this.overlay.show();\r
36590 \r
36591             if(this.constrainTo) {\r
36592                 var ct = Ext.get(this.constrainTo);\r
36593                 this.resizeRegion = ct.getRegion().adjust(\r
36594                     ct.getFrameWidth('t'),\r
36595                     ct.getFrameWidth('l'),\r
36596                     -ct.getFrameWidth('b'),\r
36597                     -ct.getFrameWidth('r')\r
36598                 );\r
36599             }\r
36600 \r
36601             this.proxy.setStyle('visibility', 'hidden'); // workaround display none\r
36602             this.proxy.show();\r
36603             this.proxy.setBox(this.startBox);\r
36604             if(!this.dynamic){\r
36605                 this.proxy.setStyle('visibility', 'visible');\r
36606             }\r
36607         }\r
36608     },\r
36609 \r
36610     // private\r
36611     onMouseDown : function(handle, e){\r
36612         if(this.enabled){\r
36613             e.stopEvent();\r
36614             this.activeHandle = handle;\r
36615             this.startSizing(e, handle);\r
36616         }          \r
36617     },\r
36618 \r
36619     // private\r
36620     onMouseUp : function(e){\r
36621         this.activeHandle = null;\r
36622         var size = this.resizeElement();\r
36623         this.resizing = false;\r
36624         this.handleOut();\r
36625         this.overlay.hide();\r
36626         this.proxy.hide();\r
36627         this.fireEvent('resize', this, size.width, size.height, e);\r
36628     },\r
36629 \r
36630     // private\r
36631     updateChildSize : function(){\r
36632         if(this.resizeChild){\r
36633             var el = this.el;\r
36634             var child = this.resizeChild;\r
36635             var adj = this.adjustments;\r
36636             if(el.dom.offsetWidth){\r
36637                 var b = el.getSize(true);\r
36638                 child.setSize(b.width+adj[0], b.height+adj[1]);\r
36639             }\r
36640             // Second call here for IE\r
36641             // The first call enables instant resizing and\r
36642             // the second call corrects scroll bars if they\r
36643             // exist\r
36644             if(Ext.isIE){\r
36645                 setTimeout(function(){\r
36646                     if(el.dom.offsetWidth){\r
36647                         var b = el.getSize(true);\r
36648                         child.setSize(b.width+adj[0], b.height+adj[1]);\r
36649                     }\r
36650                 }, 10);\r
36651             }\r
36652         }\r
36653     },\r
36654 \r
36655     // private\r
36656     snap : function(value, inc, min){\r
36657         if(!inc || !value){\r
36658             return value;\r
36659         }\r
36660         var newValue = value;\r
36661         var m = value % inc;\r
36662         if(m > 0){\r
36663             if(m > (inc/2)){\r
36664                 newValue = value + (inc-m);\r
36665             }else{\r
36666                 newValue = value - m;\r
36667             }\r
36668         }\r
36669         return Math.max(min, newValue);\r
36670     },\r
36671 \r
36672     /**\r
36673      * <p>Performs resizing of the associated Element. This method is called internally by this\r
36674      * class, and should not be called by user code.</p>\r
36675      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI\r
36676      * component such as a Panel, this method may be overridden by specifying an implementation\r
36677      * as a config option to provide appropriate behaviour at the end of the resize operation on\r
36678      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>\r
36679      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}\r
36680      * Element. Example:\r
36681 <pre><code>\r
36682 new Ext.Panel({\r
36683     title: 'Resize me',\r
36684     x: 100,\r
36685     y: 100,\r
36686     renderTo: Ext.getBody(),\r
36687     floating: true,\r
36688     frame: true,\r
36689     width: 400,\r
36690     height: 200,\r
36691     listeners: {\r
36692         render: function(p) {\r
36693             new Ext.Resizable(p.getEl(), {\r
36694                 handles: 'all',\r
36695                 pinned: true,\r
36696                 transparent: true,\r
36697                 resizeElement: function() {\r
36698                     var box = this.proxy.getBox();\r
36699                     p.updateBox(box);\r
36700                     if (p.layout) {\r
36701                         p.doLayout();\r
36702                     }\r
36703                     return box;\r
36704                 }\r
36705            });\r
36706        }\r
36707     }\r
36708 }).show();\r
36709 </code></pre>\r
36710      */\r
36711     resizeElement : function(){\r
36712         var box = this.proxy.getBox();\r
36713         if(this.updateBox){\r
36714             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);\r
36715         }else{\r
36716             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);\r
36717         }\r
36718         this.updateChildSize();\r
36719         if(!this.dynamic){\r
36720             this.proxy.hide();\r
36721         }\r
36722         return box;\r
36723     },\r
36724 \r
36725     // private\r
36726     constrain : function(v, diff, m, mx){\r
36727         if(v - diff < m){\r
36728             diff = v - m;    \r
36729         }else if(v - diff > mx){\r
36730             diff = v - mx; \r
36731         }\r
36732         return diff;                \r
36733     },\r
36734 \r
36735     // private\r
36736     onMouseMove : function(e){\r
36737         if(this.enabled && this.activeHandle){\r
36738             try{// try catch so if something goes wrong the user doesn't get hung\r
36739 \r
36740             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {\r
36741                 return;\r
36742             }\r
36743 \r
36744             //var curXY = this.startPoint;\r
36745             var curSize = this.curSize || this.startBox,\r
36746                 x = this.startBox.x, y = this.startBox.y,\r
36747                 ox = x, \r
36748                 oy = y,\r
36749                 w = curSize.width, \r
36750                 h = curSize.height,\r
36751                 ow = w, \r
36752                 oh = h,\r
36753                 mw = this.minWidth, \r
36754                 mh = this.minHeight,\r
36755                 mxw = this.maxWidth, \r
36756                 mxh = this.maxHeight,\r
36757                 wi = this.widthIncrement,\r
36758                 hi = this.heightIncrement,\r
36759                 eventXY = e.getXY(),\r
36760                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),\r
36761                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),\r
36762                 pos = this.activeHandle.position,\r
36763                 tw,\r
36764                 th;\r
36765             \r
36766             switch(pos){\r
36767                 case 'east':\r
36768                     w += diffX; \r
36769                     w = Math.min(Math.max(mw, w), mxw);\r
36770                     break;\r
36771                 case 'south':\r
36772                     h += diffY;\r
36773                     h = Math.min(Math.max(mh, h), mxh);\r
36774                     break;\r
36775                 case 'southeast':\r
36776                     w += diffX; \r
36777                     h += diffY;\r
36778                     w = Math.min(Math.max(mw, w), mxw);\r
36779                     h = Math.min(Math.max(mh, h), mxh);\r
36780                     break;\r
36781                 case 'north':\r
36782                     diffY = this.constrain(h, diffY, mh, mxh);\r
36783                     y += diffY;\r
36784                     h -= diffY;\r
36785                     break;\r
36786                 case 'west':\r
36787                     diffX = this.constrain(w, diffX, mw, mxw);\r
36788                     x += diffX;\r
36789                     w -= diffX;\r
36790                     break;\r
36791                 case 'northeast':\r
36792                     w += diffX; \r
36793                     w = Math.min(Math.max(mw, w), mxw);\r
36794                     diffY = this.constrain(h, diffY, mh, mxh);\r
36795                     y += diffY;\r
36796                     h -= diffY;\r
36797                     break;\r
36798                 case 'northwest':\r
36799                     diffX = this.constrain(w, diffX, mw, mxw);\r
36800                     diffY = this.constrain(h, diffY, mh, mxh);\r
36801                     y += diffY;\r
36802                     h -= diffY;\r
36803                     x += diffX;\r
36804                     w -= diffX;\r
36805                     break;\r
36806                case 'southwest':\r
36807                     diffX = this.constrain(w, diffX, mw, mxw);\r
36808                     h += diffY;\r
36809                     h = Math.min(Math.max(mh, h), mxh);\r
36810                     x += diffX;\r
36811                     w -= diffX;\r
36812                     break;\r
36813             }\r
36814             \r
36815             var sw = this.snap(w, wi, mw);\r
36816             var sh = this.snap(h, hi, mh);\r
36817             if(sw != w || sh != h){\r
36818                 switch(pos){\r
36819                     case 'northeast':\r
36820                         y -= sh - h;\r
36821                     break;\r
36822                     case 'north':\r
36823                         y -= sh - h;\r
36824                         break;\r
36825                     case 'southwest':\r
36826                         x -= sw - w;\r
36827                     break;\r
36828                     case 'west':\r
36829                         x -= sw - w;\r
36830                         break;\r
36831                     case 'northwest':\r
36832                         x -= sw - w;\r
36833                         y -= sh - h;\r
36834                     break;\r
36835                 }\r
36836                 w = sw;\r
36837                 h = sh;\r
36838             }\r
36839             \r
36840             if(this.preserveRatio){\r
36841                 switch(pos){\r
36842                     case 'southeast':\r
36843                     case 'east':\r
36844                         h = oh * (w/ow);\r
36845                         h = Math.min(Math.max(mh, h), mxh);\r
36846                         w = ow * (h/oh);\r
36847                        break;\r
36848                     case 'south':\r
36849                         w = ow * (h/oh);\r
36850                         w = Math.min(Math.max(mw, w), mxw);\r
36851                         h = oh * (w/ow);\r
36852                         break;\r
36853                     case 'northeast':\r
36854                         w = ow * (h/oh);\r
36855                         w = Math.min(Math.max(mw, w), mxw);\r
36856                         h = oh * (w/ow);\r
36857                     break;\r
36858                     case 'north':\r
36859                         tw = w;\r
36860                         w = ow * (h/oh);\r
36861                         w = Math.min(Math.max(mw, w), mxw);\r
36862                         h = oh * (w/ow);\r
36863                         x += (tw - w) / 2;\r
36864                         break;\r
36865                     case 'southwest':\r
36866                         h = oh * (w/ow);\r
36867                         h = Math.min(Math.max(mh, h), mxh);\r
36868                         tw = w;\r
36869                         w = ow * (h/oh);\r
36870                         x += tw - w;\r
36871                         break;\r
36872                     case 'west':\r
36873                         th = h;\r
36874                         h = oh * (w/ow);\r
36875                         h = Math.min(Math.max(mh, h), mxh);\r
36876                         y += (th - h) / 2;\r
36877                         tw = w;\r
36878                         w = ow * (h/oh);\r
36879                         x += tw - w;\r
36880                        break;\r
36881                     case 'northwest':\r
36882                         tw = w;\r
36883                         th = h;\r
36884                         h = oh * (w/ow);\r
36885                         h = Math.min(Math.max(mh, h), mxh);\r
36886                         w = ow * (h/oh);\r
36887                         y += th - h;\r
36888                         x += tw - w;\r
36889                         break;\r
36890                         \r
36891                 }\r
36892             }\r
36893             this.proxy.setBounds(x, y, w, h);\r
36894             if(this.dynamic){\r
36895                 this.resizeElement();\r
36896             }\r
36897             }catch(ex){}\r
36898         }\r
36899     },\r
36900 \r
36901     // private\r
36902     handleOver : function(){\r
36903         if(this.enabled){\r
36904             this.el.addClass('x-resizable-over');\r
36905         }\r
36906     },\r
36907 \r
36908     // private\r
36909     handleOut : function(){\r
36910         if(!this.resizing){\r
36911             this.el.removeClass('x-resizable-over');\r
36912         }\r
36913     },\r
36914     \r
36915     /**\r
36916      * Returns the element this component is bound to.\r
36917      * @return {Ext.Element}\r
36918      */\r
36919     getEl : function(){\r
36920         return this.el;\r
36921     },\r
36922     \r
36923     /**\r
36924      * Returns the resizeChild element (or null).\r
36925      * @return {Ext.Element}\r
36926      */\r
36927     getResizeChild : function(){\r
36928         return this.resizeChild;\r
36929     },\r
36930     \r
36931     /**\r
36932      * Destroys this resizable. If the element was wrapped and \r
36933      * removeEl is not true then the element remains.\r
36934      * @param {Boolean} removeEl (optional) true to remove the element from the DOM\r
36935      */\r
36936     destroy : function(removeEl){\r
36937         Ext.destroy(this.dd, this.overlay, this.proxy);\r
36938         this.overlay = null;\r
36939         this.proxy = null;\r
36940         \r
36941         var ps = Ext.Resizable.positions;\r
36942         for(var k in ps){\r
36943             if(typeof ps[k] != 'function' && this[ps[k]]){\r
36944                 this[ps[k]].destroy();\r
36945             }\r
36946         }\r
36947         if(removeEl){\r
36948             this.el.update('');\r
36949             Ext.destroy(this.el);\r
36950             this.el = null;\r
36951         }\r
36952         this.purgeListeners();\r
36953     },\r
36954 \r
36955     syncHandleHeight : function(){\r
36956         var h = this.el.getHeight(true);\r
36957         if(this.west){\r
36958             this.west.el.setHeight(h);\r
36959         }\r
36960         if(this.east){\r
36961             this.east.el.setHeight(h);\r
36962         }\r
36963     }\r
36964 });\r
36965 \r
36966 // private\r
36967 // hash to map config positions to true positions\r
36968 Ext.Resizable.positions = {\r
36969     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'\r
36970 };\r
36971 \r
36972 // private\r
36973 Ext.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){\r
36974     if(!this.tpl){\r
36975         // only initialize the template if resizable is used\r
36976         var tpl = Ext.DomHelper.createTemplate(\r
36977             {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}\r
36978         );\r
36979         tpl.compile();\r
36980         Ext.Resizable.Handle.prototype.tpl = tpl;\r
36981     }\r
36982     this.position = pos;\r
36983     this.rz = rz;\r
36984     this.el = this.tpl.append(rz.el.dom, [this.position], true);\r
36985     this.el.unselectable();\r
36986     if(transparent){\r
36987         this.el.setOpacity(0);\r
36988     }\r
36989     this.el.on('mousedown', this.onMouseDown, this);\r
36990     if(!disableTrackOver){\r
36991         this.el.on({\r
36992             scope: this,\r
36993             mouseover: this.onMouseOver,\r
36994             mouseout: this.onMouseOut\r
36995         });\r
36996     }\r
36997 };\r
36998 \r
36999 // private\r
37000 Ext.Resizable.Handle.prototype = {\r
37001     // private\r
37002     afterResize : function(rz){\r
37003         // do nothing    \r
37004     },\r
37005     // private\r
37006     onMouseDown : function(e){\r
37007         this.rz.onMouseDown(this, e);\r
37008     },\r
37009     // private\r
37010     onMouseOver : function(e){\r
37011         this.rz.handleOver(this, e);\r
37012     },\r
37013     // private\r
37014     onMouseOut : function(e){\r
37015         this.rz.handleOut(this, e);\r
37016     },\r
37017     // private\r
37018     destroy : function(){\r
37019         Ext.destroy(this.el);\r
37020         this.el = null;\r
37021     }\r
37022 };\r
37023 /**
37024  * @class Ext.Window
37025  * @extends Ext.Panel
37026  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
37027  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
37028  * restored to their prior size, and can be {@link #minimize}d.</p>
37029  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide
37030  * grouping, activation, to front, to back and other application-specific behavior.</p>
37031  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
37032  * specify {@link Ext.Component#renderTo renderTo}.</p>
37033  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
37034  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
37035  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
37036  * @constructor
37037  * @param {Object} config The config object
37038  * @xtype window
37039  */
37040 Ext.Window = Ext.extend(Ext.Panel, {
37041     /**
37042      * @cfg {Number} x
37043      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
37044      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
37045      */
37046     /**
37047      * @cfg {Number} y
37048      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
37049      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
37050      */
37051     /**
37052      * @cfg {Boolean} modal
37053      * True to make the window modal and mask everything behind it when displayed, false to display it without
37054      * restricting access to other UI elements (defaults to false).
37055      */
37056     /**
37057      * @cfg {String/Element} animateTarget
37058      * Id or element from which the window should animate while opening (defaults to null with no animation).
37059      */
37060     /**
37061      * @cfg {String} resizeHandles
37062      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
37063      */
37064     /**
37065      * @cfg {Ext.WindowGroup} manager
37066      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
37067      */
37068     /**
37069     * @cfg {String/Number/Button} defaultButton
37070     * The id / index of a button or a button instance to focus when this window received the focus.
37071     */
37072     /**
37073     * @cfg {Function} onEsc
37074     * Allows override of the built-in processing for the escape key. Default action
37075     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
37076     * To prevent the Window closing when the escape key is pressed, specify this as
37077     * Ext.emptyFn (See {@link Ext#emptyFn}).
37078     */
37079     /**
37080      * @cfg {Boolean} collapsed
37081      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
37082      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window
37083      * will always be expanded when shown.
37084      */
37085     /**
37086      * @cfg {Boolean} maximized
37087      * True to initially display the window in a maximized state. (Defaults to false).
37088      */
37089
37090     /**
37091     * @cfg {String} baseCls
37092     * The base CSS class to apply to this panel's element (defaults to 'x-window').
37093     */
37094     baseCls : 'x-window',
37095     /**
37096      * @cfg {Boolean} resizable
37097      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
37098      */
37099     resizable : true,
37100     /**
37101      * @cfg {Boolean} draggable
37102      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
37103      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
37104      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
37105      */
37106     draggable : true,
37107     /**
37108      * @cfg {Boolean} closable
37109      * <p>True to display the 'close' tool button and allow the user to close the window, false to
37110      * hide the button and disallow closing the window (defaults to true).</p>
37111      * <p>By default, when close is requested by either clicking the close button in the header
37112      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
37113      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
37114      * it may not be reused.</p>
37115      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
37116      * {@link #closeAction} to 'hide'.
37117      */
37118     closable : true,
37119     /**
37120      * @cfg {String} closeAction
37121      * <p>The action to take when the close header tool is clicked:
37122      * <div class="mdetail-params"><ul>
37123      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
37124      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
37125      * it and all descendant Components. The window will <b>not</b> be available to be
37126      * redisplayed via the {@link #show} method.
37127      * </div></li>
37128      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
37129      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
37130      * The window will be available to be redisplayed via the {@link #show} method.
37131      * </div></li>
37132      * </ul></div>
37133      * <p><b>Note:</b> This setting does not affect the {@link #close} method
37134      * which will always {@link Ext.Component#destroy destroy} the window. To
37135      * programatically <i>hide</i> a window, call {@link #hide}.</p>
37136      */
37137     closeAction : 'close',
37138     /**
37139      * @cfg {Boolean} constrain
37140      * True to constrain the window within its containing element, false to allow it to fall outside of its
37141      * containing element. By default the window will be rendered to document.body.  To render and constrain the
37142      * window within another element specify {@link #renderTo}.
37143      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
37144      */
37145     constrain : false,
37146     /**
37147      * @cfg {Boolean} constrainHeader
37148      * True to constrain the window header within its containing element (allowing the window body to fall outside
37149      * of its containing element) or false to allow the header to fall outside its containing element (defaults to
37150      * false). Optionally the entire window can be constrained using {@link #constrain}.
37151      */
37152     constrainHeader : false,
37153     /**
37154      * @cfg {Boolean} plain
37155      * True to render the window body with a transparent background so that it will blend into the framing
37156      * elements, false to add a lighter background color to visually highlight the body element and separate it
37157      * more distinctly from the surrounding frame (defaults to false).
37158      */
37159     plain : false,
37160     /**
37161      * @cfg {Boolean} minimizable
37162      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
37163      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
37164      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
37165      * custom minimize behavior implemented for this option to be useful.
37166      */
37167     minimizable : false,
37168     /**
37169      * @cfg {Boolean} maximizable
37170      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
37171      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
37172      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
37173      * restore the window to its previous size.
37174      */
37175     maximizable : false,
37176     /**
37177      * @cfg {Number} minHeight
37178      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
37179      */
37180     minHeight : 100,
37181     /**
37182      * @cfg {Number} minWidth
37183      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
37184      */
37185     minWidth : 200,
37186     /**
37187      * @cfg {Boolean} expandOnShow
37188      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
37189      * {@link #collapsed}) when displayed (defaults to true).
37190      */
37191     expandOnShow : true,
37192
37193     // inherited docs, same default
37194     collapsible : false,
37195
37196     /**
37197      * @cfg {Boolean} initHidden
37198      * True to hide the window until show() is explicitly called (defaults to true).
37199      * @deprecated
37200      */
37201     initHidden : undefined,
37202     
37203     /**
37204      * @cfg {Boolean} hidden
37205      * Render this component hidden (default is <tt>true</tt>). If <tt>true</tt>, the
37206      * {@link #hide} method will be called internally.
37207      */
37208     hidden : true,
37209     
37210     /**
37211     * @cfg {Boolean} monitorResize @hide
37212     * This is automatically managed based on the value of constrain and constrainToHeader
37213     */
37214     monitorResize : true,
37215
37216     // The following configs are set to provide the basic functionality of a window.
37217     // Changing them would require additional code to handle correctly and should
37218     // usually only be done in subclasses that can provide custom behavior.  Changing them
37219     // may have unexpected or undesirable results.
37220     /** @cfg {String} elements @hide */
37221     elements : 'header,body',
37222     /** @cfg {Boolean} frame @hide */
37223     frame : true,
37224     /** @cfg {Boolean} floating @hide */
37225     floating : true,
37226
37227     // private
37228     initComponent : function(){
37229         this.initTools();
37230         Ext.Window.superclass.initComponent.call(this);
37231         this.addEvents(
37232             /**
37233              * @event activate
37234              * Fires after the window has been visually activated via {@link #setActive}.
37235              * @param {Ext.Window} this
37236              */
37237             /**
37238              * @event deactivate
37239              * Fires after the window has been visually deactivated via {@link #setActive}.
37240              * @param {Ext.Window} this
37241              */
37242             /**
37243              * @event resize
37244              * Fires after the window has been resized.
37245              * @param {Ext.Window} this
37246              * @param {Number} width The window's new width
37247              * @param {Number} height The window's new height
37248              */
37249             'resize',
37250             /**
37251              * @event maximize
37252              * Fires after the window has been maximized.
37253              * @param {Ext.Window} this
37254              */
37255             'maximize',
37256             /**
37257              * @event minimize
37258              * Fires after the window has been minimized.
37259              * @param {Ext.Window} this
37260              */
37261             'minimize',
37262             /**
37263              * @event restore
37264              * Fires after the window has been restored to its original size after being maximized.
37265              * @param {Ext.Window} this
37266              */
37267             'restore'
37268         );
37269         // for backwards compat, this should be removed at some point
37270         if(Ext.isDefined(this.initHidden)){
37271             this.hidden = this.initHidden;
37272         }
37273         if(this.hidden === false){
37274             this.hidden = true;
37275             this.show();
37276         }
37277     },
37278
37279     // private
37280     getState : function(){
37281         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
37282     },
37283
37284     // private
37285     onRender : function(ct, position){
37286         Ext.Window.superclass.onRender.call(this, ct, position);
37287
37288         if(this.plain){
37289             this.el.addClass('x-window-plain');
37290         }
37291
37292         // this element allows the Window to be focused for keyboard events
37293         this.focusEl = this.el.createChild({
37294                     tag: 'a', href:'#', cls:'x-dlg-focus',
37295                     tabIndex:'-1', html: '&#160;'});
37296         this.focusEl.swallowEvent('click', true);
37297
37298         this.proxy = this.el.createProxy('x-window-proxy');
37299         this.proxy.enableDisplayMode('block');
37300
37301         if(this.modal){
37302             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
37303             this.mask.enableDisplayMode('block');
37304             this.mask.hide();
37305             this.mon(this.mask, 'click', this.focus, this);
37306         }
37307         if(this.maximizable){
37308             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
37309         }
37310     },
37311
37312     // private
37313     initEvents : function(){
37314         Ext.Window.superclass.initEvents.call(this);
37315         if(this.animateTarget){
37316             this.setAnimateTarget(this.animateTarget);
37317         }
37318
37319         if(this.resizable){
37320             this.resizer = new Ext.Resizable(this.el, {
37321                 minWidth: this.minWidth,
37322                 minHeight:this.minHeight,
37323                 handles: this.resizeHandles || 'all',
37324                 pinned: true,
37325                 resizeElement : this.resizerAction
37326             });
37327             this.resizer.window = this;
37328             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
37329         }
37330
37331         if(this.draggable){
37332             this.header.addClass('x-window-draggable');
37333         }
37334         this.mon(this.el, 'mousedown', this.toFront, this);
37335         this.manager = this.manager || Ext.WindowMgr;
37336         this.manager.register(this);
37337         if(this.maximized){
37338             this.maximized = false;
37339             this.maximize();
37340         }
37341         if(this.closable){
37342             var km = this.getKeyMap();
37343             km.on(27, this.onEsc, this);
37344             km.disable();
37345         }
37346     },
37347
37348     initDraggable : function(){
37349         /**
37350          * If this Window is configured {@link #draggable}, this property will contain
37351          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.
37352          * @type Ext.dd.DD
37353          * @property dd
37354          */
37355         this.dd = new Ext.Window.DD(this);
37356     },
37357
37358    // private
37359     onEsc : function(){
37360         this[this.closeAction]();
37361     },
37362
37363     // private
37364     beforeDestroy : function(){
37365         if (this.rendered){
37366             this.hide();
37367           if(this.doAnchor){
37368                 Ext.EventManager.removeResizeListener(this.doAnchor, this);
37369               Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
37370             }
37371             Ext.destroy(
37372                 this.focusEl,
37373                 this.resizer,
37374                 this.dd,
37375                 this.proxy,
37376                 this.mask
37377             );
37378         }
37379         Ext.Window.superclass.beforeDestroy.call(this);
37380     },
37381
37382     // private
37383     onDestroy : function(){
37384         if(this.manager){
37385             this.manager.unregister(this);
37386         }
37387         Ext.Window.superclass.onDestroy.call(this);
37388     },
37389
37390     // private
37391     initTools : function(){
37392         if(this.minimizable){
37393             this.addTool({
37394                 id: 'minimize',
37395                 handler: this.minimize.createDelegate(this, [])
37396             });
37397         }
37398         if(this.maximizable){
37399             this.addTool({
37400                 id: 'maximize',
37401                 handler: this.maximize.createDelegate(this, [])
37402             });
37403             this.addTool({
37404                 id: 'restore',
37405                 handler: this.restore.createDelegate(this, []),
37406                 hidden:true
37407             });
37408         }
37409         if(this.closable){
37410             this.addTool({
37411                 id: 'close',
37412                 handler: this[this.closeAction].createDelegate(this, [])
37413             });
37414         }
37415     },
37416
37417     // private
37418     resizerAction : function(){
37419         var box = this.proxy.getBox();
37420         this.proxy.hide();
37421         this.window.handleResize(box);
37422         return box;
37423     },
37424
37425     // private
37426     beforeResize : function(){
37427         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
37428         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
37429         this.resizeBox = this.el.getBox();
37430     },
37431
37432     // private
37433     updateHandles : function(){
37434         if(Ext.isIE && this.resizer){
37435             this.resizer.syncHandleHeight();
37436             this.el.repaint();
37437         }
37438     },
37439
37440     // private
37441     handleResize : function(box){
37442         var rz = this.resizeBox;
37443         if(rz.x != box.x || rz.y != box.y){
37444             this.updateBox(box);
37445         }else{
37446             this.setSize(box);
37447         }
37448         this.focus();
37449         this.updateHandles();
37450         this.saveState();
37451         this.doLayout();
37452     },
37453
37454     /**
37455      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
37456      * window itself will receive focus.
37457      */
37458     focus : function(){
37459         var f = this.focusEl, db = this.defaultButton, t = typeof db;
37460         if(Ext.isDefined(db)){
37461             if(Ext.isNumber(db) && this.fbar){
37462                 f = this.fbar.items.get(db);
37463             }else if(Ext.isString(db)){
37464                 f = Ext.getCmp(db);
37465             }else{
37466                 f = db;
37467             }
37468         }
37469         f = f || this.focusEl;
37470         f.focus.defer(10, f);
37471     },
37472
37473     /**
37474      * Sets the target element from which the window should animate while opening.
37475      * @param {String/Element} el The target element or id
37476      */
37477     setAnimateTarget : function(el){
37478         el = Ext.get(el);
37479         this.animateTarget = el;
37480     },
37481
37482     // private
37483     beforeShow : function(){
37484         delete this.el.lastXY;
37485         delete this.el.lastLT;
37486         if(this.x === undefined || this.y === undefined){
37487             var xy = this.el.getAlignToXY(this.container, 'c-c');
37488             var pos = this.el.translatePoints(xy[0], xy[1]);
37489             this.x = this.x === undefined? pos.left : this.x;
37490             this.y = this.y === undefined? pos.top : this.y;
37491         }
37492         this.el.setLeftTop(this.x, this.y);
37493
37494         if(this.expandOnShow){
37495             this.expand(false);
37496         }
37497
37498         if(this.modal){
37499             Ext.getBody().addClass('x-body-masked');
37500             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
37501             this.mask.show();
37502         }
37503     },
37504
37505     /**
37506      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
37507      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
37508      * animate while opening (defaults to null with no animation)
37509      * @param {Function} callback (optional) A callback function to call after the window is displayed
37510      * @param {Object} scope (optional) The scope in which to execute the callback
37511      * @return {Ext.Window} this
37512      */
37513     show : function(animateTarget, cb, scope){
37514         if(!this.rendered){
37515             this.render(Ext.getBody());
37516         }
37517         if(this.hidden === false){
37518             this.toFront();
37519             return this;
37520         }
37521         if(this.fireEvent('beforeshow', this) === false){
37522             return this;
37523         }
37524         if(cb){
37525             this.on('show', cb, scope, {single:true});
37526         }
37527         this.hidden = false;
37528         if(Ext.isDefined(animateTarget)){
37529             this.setAnimateTarget(animateTarget);
37530         }
37531         this.beforeShow();
37532         if(this.animateTarget){
37533             this.animShow();
37534         }else{
37535             this.afterShow();
37536         }
37537         return this;
37538     },
37539
37540     // private
37541     afterShow : function(isAnim){
37542         this.proxy.hide();
37543         this.el.setStyle('display', 'block');
37544         this.el.show();
37545         if(this.maximized){
37546             this.fitContainer();
37547         }
37548         if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
37549             this.cascade(this.setAutoScroll);
37550         }
37551
37552         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
37553             Ext.EventManager.onWindowResize(this.onWindowResize, this);
37554         }
37555         this.doConstrain();
37556         this.doLayout();
37557         if(this.keyMap){
37558             this.keyMap.enable();
37559         }
37560         this.toFront();
37561         this.updateHandles();
37562         if(isAnim && (Ext.isIE || Ext.isWebKit)){
37563             var sz = this.getSize();
37564             this.onResize(sz.width, sz.height);
37565         }
37566         this.fireEvent('show', this);
37567     },
37568
37569     // private
37570     animShow : function(){
37571         this.proxy.show();
37572         this.proxy.setBox(this.animateTarget.getBox());
37573         this.proxy.setOpacity(0);
37574         var b = this.getBox();
37575         this.el.setStyle('display', 'none');
37576         this.proxy.shift(Ext.apply(b, {
37577             callback: this.afterShow.createDelegate(this, [true], false),
37578             scope: this,
37579             easing: 'easeNone',
37580             duration: 0.25,
37581             opacity: 0.5
37582         }));
37583     },
37584
37585     /**
37586      * Hides the window, setting it to invisible and applying negative offsets.
37587      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
37588      * animate while hiding (defaults to null with no animation)
37589      * @param {Function} callback (optional) A callback function to call after the window is hidden
37590      * @param {Object} scope (optional) The scope in which to execute the callback
37591      * @return {Ext.Window} this
37592      */
37593     hide : function(animateTarget, cb, scope){
37594         if(this.hidden || this.fireEvent('beforehide', this) === false){
37595             return this;
37596         }
37597         if(cb){
37598             this.on('hide', cb, scope, {single:true});
37599         }
37600         this.hidden = true;
37601         if(animateTarget !== undefined){
37602             this.setAnimateTarget(animateTarget);
37603         }
37604         if(this.modal){
37605             this.mask.hide();
37606             Ext.getBody().removeClass('x-body-masked');
37607         }
37608         if(this.animateTarget){
37609             this.animHide();
37610         }else{
37611             this.el.hide();
37612             this.afterHide();
37613         }
37614         return this;
37615     },
37616
37617     // private
37618     afterHide : function(){
37619         this.proxy.hide();
37620         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
37621             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
37622         }
37623         if(this.keyMap){
37624             this.keyMap.disable();
37625         }
37626         this.fireEvent('hide', this);
37627     },
37628
37629     // private
37630     animHide : function(){
37631         this.proxy.setOpacity(0.5);
37632         this.proxy.show();
37633         var tb = this.getBox(false);
37634         this.proxy.setBox(tb);
37635         this.el.hide();
37636         this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
37637             callback: this.afterHide,
37638             scope: this,
37639             duration: 0.25,
37640             easing: 'easeNone',
37641             opacity: 0
37642         }));
37643     },
37644
37645     // private
37646     onWindowResize : function(){
37647         if(this.maximized){
37648             this.fitContainer();
37649         }
37650         if(this.modal){
37651             this.mask.setSize('100%', '100%');
37652             var force = this.mask.dom.offsetHeight;
37653             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
37654         }
37655         this.doConstrain();
37656     },
37657
37658     // private
37659     doConstrain : function(){
37660         if(this.constrain || this.constrainHeader){
37661             var offsets;
37662             if(this.constrain){
37663                 offsets = {
37664                     right:this.el.shadowOffset,
37665                     left:this.el.shadowOffset,
37666                     bottom:this.el.shadowOffset
37667                 };
37668             }else {
37669                 var s = this.getSize();
37670                 offsets = {
37671                     right:-(s.width - 100),
37672                     bottom:-(s.height - 25)
37673                 };
37674             }
37675
37676             var xy = this.el.getConstrainToXY(this.container, true, offsets);
37677             if(xy){
37678                 this.setPosition(xy[0], xy[1]);
37679             }
37680         }
37681     },
37682
37683     // private - used for dragging
37684     ghost : function(cls){
37685         var ghost = this.createGhost(cls);
37686         var box = this.getBox(true);
37687         ghost.setLeftTop(box.x, box.y);
37688         ghost.setWidth(box.width);
37689         this.el.hide();
37690         this.activeGhost = ghost;
37691         return ghost;
37692     },
37693
37694     // private
37695     unghost : function(show, matchPosition){
37696         if(!this.activeGhost) {
37697             return;
37698         }
37699         if(show !== false){
37700             this.el.show();
37701             this.focus();
37702             if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
37703                 this.cascade(this.setAutoScroll);
37704             }
37705         }
37706         if(matchPosition !== false){
37707             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
37708         }
37709         this.activeGhost.hide();
37710         this.activeGhost.remove();
37711         delete this.activeGhost;
37712     },
37713
37714     /**
37715      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
37716      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
37717      * either the minimize event can be handled or this method can be overridden.
37718      * @return {Ext.Window} this
37719      */
37720     minimize : function(){
37721         this.fireEvent('minimize', this);
37722         return this;
37723     },
37724
37725     /**
37726      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
37727      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
37728      * event is fired before the close happens and will cancel the close action if it returns false.<p>
37729      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
37730      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
37731      * To hide the Window without destroying it, call {@link #hide}.</p>
37732      */
37733     close : function(){
37734         if(this.fireEvent('beforeclose', this) !== false){
37735             if(this.hidden){
37736                 this.doClose();
37737             }else{
37738                 this.hide(null, this.doClose, this);
37739             }
37740         }
37741     },
37742     
37743     // private
37744     doClose : function(){
37745         this.fireEvent('close', this);
37746         this.destroy();
37747     },
37748
37749     /**
37750      * Fits the window within its current container and automatically replaces
37751      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
37752      * Also see {@link #toggleMaximize}.
37753      * @return {Ext.Window} this
37754      */
37755     maximize : function(){
37756         if(!this.maximized){
37757             this.expand(false);
37758             this.restoreSize = this.getSize();
37759             this.restorePos = this.getPosition(true);
37760             if (this.maximizable){
37761                 this.tools.maximize.hide();
37762                 this.tools.restore.show();
37763             }
37764             this.maximized = true;
37765             this.el.disableShadow();
37766
37767             if(this.dd){
37768                 this.dd.lock();
37769             }
37770             if(this.collapsible){
37771                 this.tools.toggle.hide();
37772             }
37773             this.el.addClass('x-window-maximized');
37774             this.container.addClass('x-window-maximized-ct');
37775
37776             this.setPosition(0, 0);
37777             this.fitContainer();
37778             this.fireEvent('maximize', this);
37779         }
37780         return this;
37781     },
37782
37783     /**
37784      * Restores a {@link #maximizable maximized}  window back to its original
37785      * size and position prior to being maximized and also replaces
37786      * the 'restore' tool button with the 'maximize' tool button.
37787      * Also see {@link #toggleMaximize}.
37788      * @return {Ext.Window} this
37789      */
37790     restore : function(){
37791         if(this.maximized){
37792             this.el.removeClass('x-window-maximized');
37793             this.tools.restore.hide();
37794             this.tools.maximize.show();
37795             this.setPosition(this.restorePos[0], this.restorePos[1]);
37796             this.setSize(this.restoreSize.width, this.restoreSize.height);
37797             delete this.restorePos;
37798             delete this.restoreSize;
37799             this.maximized = false;
37800             this.el.enableShadow(true);
37801
37802             if(this.dd){
37803                 this.dd.unlock();
37804             }
37805             if(this.collapsible){
37806                 this.tools.toggle.show();
37807             }
37808             this.container.removeClass('x-window-maximized-ct');
37809
37810             this.doConstrain();
37811             this.fireEvent('restore', this);
37812         }
37813         return this;
37814     },
37815
37816     /**
37817      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
37818      * state of the window.
37819      * @return {Ext.Window} this
37820      */
37821     toggleMaximize : function(){
37822         return this[this.maximized ? 'restore' : 'maximize']();
37823     },
37824
37825     // private
37826     fitContainer : function(){
37827         var vs = this.container.getViewSize();
37828         this.setSize(vs.width, vs.height);
37829     },
37830
37831     // private
37832     // z-index is managed by the WindowManager and may be overwritten at any time
37833     setZIndex : function(index){
37834         if(this.modal){
37835             this.mask.setStyle('z-index', index);
37836         }
37837         this.el.setZIndex(++index);
37838         index += 5;
37839
37840         if(this.resizer){
37841             this.resizer.proxy.setStyle('z-index', ++index);
37842         }
37843
37844         this.lastZIndex = index;
37845     },
37846
37847     /**
37848      * Aligns the window to the specified element
37849      * @param {Mixed} element The element to align to.
37850      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details).
37851      * @param {Array} offsets (optional) Offset the positioning by [x, y]
37852      * @return {Ext.Window} this
37853      */
37854     alignTo : function(element, position, offsets){
37855         var xy = this.el.getAlignToXY(element, position, offsets);
37856         this.setPagePosition(xy[0], xy[1]);
37857         return this;
37858     },
37859
37860     /**
37861      * Anchors this window to another element and realigns it when the window is resized or scrolled.
37862      * @param {Mixed} element The element to align to.
37863      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
37864      * @param {Array} offsets (optional) Offset the positioning by [x, y]
37865      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
37866      * is a number, it is used as the buffer delay (defaults to 50ms).
37867      * @return {Ext.Window} this
37868      */
37869     anchorTo : function(el, alignment, offsets, monitorScroll){
37870       if(this.doAnchor){
37871           Ext.EventManager.removeResizeListener(this.doAnchor, this);
37872           Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
37873       }
37874       this.doAnchor = function(){
37875           this.alignTo(el, alignment, offsets);
37876       };
37877       Ext.EventManager.onWindowResize(this.doAnchor, this);
37878
37879       var tm = typeof monitorScroll;
37880       if(tm != 'undefined'){
37881           Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
37882               {buffer: tm == 'number' ? monitorScroll : 50});
37883       }
37884       this.doAnchor();
37885       return this;
37886     },
37887
37888     /**
37889      * Brings this window to the front of any other visible windows
37890      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
37891      * @return {Ext.Window} this
37892      */
37893     toFront : function(e){
37894         if(this.manager.bringToFront(this)){
37895             if(!e || !e.getTarget().focus){
37896                 this.focus();
37897             }
37898         }
37899         return this;
37900     },
37901
37902     /**
37903      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
37904      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is
37905      * called internally by {@link Ext.WindowMgr}.
37906      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
37907      */
37908     setActive : function(active){
37909         if(active){
37910             if(!this.maximized){
37911                 this.el.enableShadow(true);
37912             }
37913             this.fireEvent('activate', this);
37914         }else{
37915             this.el.disableShadow();
37916             this.fireEvent('deactivate', this);
37917         }
37918     },
37919
37920     /**
37921      * Sends this window to the back of (lower z-index than) any other visible windows
37922      * @return {Ext.Window} this
37923      */
37924     toBack : function(){
37925         this.manager.sendToBack(this);
37926         return this;
37927     },
37928
37929     /**
37930      * Centers this window in the viewport
37931      * @return {Ext.Window} this
37932      */
37933     center : function(){
37934         var xy = this.el.getAlignToXY(this.container, 'c-c');
37935         this.setPagePosition(xy[0], xy[1]);
37936         return this;
37937     }
37938
37939     /**
37940      * @cfg {Boolean} autoWidth @hide
37941      **/
37942 });
37943 Ext.reg('window', Ext.Window);
37944
37945 // private - custom Window DD implementation
37946 Ext.Window.DD = function(win){
37947     this.win = win;
37948     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
37949     this.setHandleElId(win.header.id);
37950     this.scroll = false;
37951 };
37952
37953 Ext.extend(Ext.Window.DD, Ext.dd.DD, {
37954     moveOnly:true,
37955     headerOffsets:[100, 25],
37956     startDrag : function(){
37957         var w = this.win;
37958         this.proxy = w.ghost();
37959         if(w.constrain !== false){
37960             var so = w.el.shadowOffset;
37961             this.constrainTo(w.container, {right: so, left: so, bottom: so});
37962         }else if(w.constrainHeader !== false){
37963             var s = this.proxy.getSize();
37964             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
37965         }
37966     },
37967     b4Drag : Ext.emptyFn,
37968
37969     onDrag : function(e){
37970         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
37971     },
37972
37973     endDrag : function(e){
37974         this.win.unghost();
37975         this.win.saveState();
37976     }
37977 });
37978 /**
37979  * @class Ext.WindowGroup
37980  * An object that represents a group of {@link Ext.Window} instances and provides z-order management
37981  * and window activation behavior.
37982  * @constructor
37983  */
37984 Ext.WindowGroup = function(){
37985     var list = {};
37986     var accessList = [];
37987     var front = null;
37988
37989     // private
37990     var sortWindows = function(d1, d2){
37991         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
37992     };
37993
37994     // private
37995     var orderWindows = function(){
37996         var a = accessList, len = a.length;
37997         if(len > 0){
37998             a.sort(sortWindows);
37999             var seed = a[0].manager.zseed;
38000             for(var i = 0; i < len; i++){
38001                 var win = a[i];
38002                 if(win && !win.hidden){
38003                     win.setZIndex(seed + (i*10));
38004                 }
38005             }
38006         }
38007         activateLast();
38008     };
38009
38010     // private
38011     var setActiveWin = function(win){
38012         if(win != front){
38013             if(front){
38014                 front.setActive(false);
38015             }
38016             front = win;
38017             if(win){
38018                 win.setActive(true);
38019             }
38020         }
38021     };
38022
38023     // private
38024     var activateLast = function(){
38025         for(var i = accessList.length-1; i >=0; --i) {
38026             if(!accessList[i].hidden){
38027                 setActiveWin(accessList[i]);
38028                 return;
38029             }
38030         }
38031         // none to activate
38032         setActiveWin(null);
38033     };
38034
38035     return {
38036         /**
38037          * The starting z-index for windows (defaults to 9000)
38038          * @type Number The z-index value
38039          */
38040         zseed : 9000,
38041
38042         // private
38043         register : function(win){
38044             list[win.id] = win;
38045             accessList.push(win);
38046             win.on('hide', activateLast);
38047         },
38048
38049         // private
38050         unregister : function(win){
38051             delete list[win.id];
38052             win.un('hide', activateLast);
38053             accessList.remove(win);
38054         },
38055
38056         /**
38057          * Gets a registered window by id.
38058          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
38059          * @return {Ext.Window}
38060          */
38061         get : function(id){
38062             return typeof id == "object" ? id : list[id];
38063         },
38064
38065         /**
38066          * Brings the specified window to the front of any other active windows.
38067          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
38068          * @return {Boolean} True if the dialog was brought to the front, else false
38069          * if it was already in front
38070          */
38071         bringToFront : function(win){
38072             win = this.get(win);
38073             if(win != front){
38074                 win._lastAccess = new Date().getTime();
38075                 orderWindows();
38076                 return true;
38077             }
38078             return false;
38079         },
38080
38081         /**
38082          * Sends the specified window to the back of other active windows.
38083          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
38084          * @return {Ext.Window} The window
38085          */
38086         sendToBack : function(win){
38087             win = this.get(win);
38088             win._lastAccess = -(new Date().getTime());
38089             orderWindows();
38090             return win;
38091         },
38092
38093         /**
38094          * Hides all windows in the group.
38095          */
38096         hideAll : function(){
38097             for(var id in list){
38098                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
38099                     list[id].hide();
38100                 }
38101             }
38102         },
38103
38104         /**
38105          * Gets the currently-active window in the group.
38106          * @return {Ext.Window} The active window
38107          */
38108         getActive : function(){
38109             return front;
38110         },
38111
38112         /**
38113          * Returns zero or more windows in the group using the custom search function passed to this method.
38114          * The function should accept a single {@link Ext.Window} reference as its only argument and should
38115          * return true if the window matches the search criteria, otherwise it should return false.
38116          * @param {Function} fn The search function
38117          * @param {Object} scope (optional) The scope in which to execute the function (defaults to the window
38118          * that gets passed to the function if not specified)
38119          * @return {Array} An array of zero or more matching windows
38120          */
38121         getBy : function(fn, scope){
38122             var r = [];
38123             for(var i = accessList.length-1; i >=0; --i) {
38124                 var win = accessList[i];
38125                 if(fn.call(scope||win, win) !== false){
38126                     r.push(win);
38127                 }
38128             }
38129             return r;
38130         },
38131
38132         /**
38133          * Executes the specified function once for every window in the group, passing each
38134          * window as the only parameter. Returning false from the function will stop the iteration.
38135          * @param {Function} fn The function to execute for each item
38136          * @param {Object} scope (optional) The scope in which to execute the function
38137          */
38138         each : function(fn, scope){
38139             for(var id in list){
38140                 if(list[id] && typeof list[id] != "function"){
38141                     if(fn.call(scope || list[id], list[id]) === false){
38142                         return;
38143                     }
38144                 }
38145             }
38146         }
38147     };
38148 };
38149
38150
38151 /**
38152  * @class Ext.WindowMgr
38153  * @extends Ext.WindowGroup
38154  * The default global window group that is available automatically.  To have more than one group of windows
38155  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
38156  * @singleton
38157  */
38158 Ext.WindowMgr = new Ext.WindowGroup();/**\r
38159  * @class Ext.MessageBox\r
38160  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>\r
38161  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt\r
38162  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code\r
38163  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function\r
38164  * (see the <code>function</code> parameter for {@link #show} for more details).</p>\r
38165  * <p>Example usage:</p>\r
38166  *<pre><code>\r
38167 // Basic alert:\r
38168 Ext.Msg.alert('Status', 'Changes saved successfully.');\r
38169 \r
38170 // Prompt for user data and process the result using a callback:\r
38171 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){\r
38172     if (btn == 'ok'){\r
38173         // process text value and close...\r
38174     }\r
38175 });\r
38176 \r
38177 // Show a dialog using config options:\r
38178 Ext.Msg.show({\r
38179    title:'Save Changes?',\r
38180    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',\r
38181    buttons: Ext.Msg.YESNOCANCEL,\r
38182    fn: processResult,\r
38183    animEl: 'elId',\r
38184    icon: Ext.MessageBox.QUESTION\r
38185 });\r
38186 </code></pre>\r
38187  * @singleton\r
38188  */\r
38189 Ext.MessageBox = function(){\r
38190     var dlg, opt, mask, waitTimer,\r
38191         bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,\r
38192         buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '';\r
38193 \r
38194     // private\r
38195     var handleButton = function(button){\r
38196         if(dlg.isVisible()){\r
38197             dlg.hide();\r
38198             handleHide();\r
38199             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);\r
38200         }\r
38201     };\r
38202 \r
38203     // private\r
38204     var handleHide = function(){\r
38205         if(opt && opt.cls){\r
38206             dlg.el.removeClass(opt.cls);\r
38207         }\r
38208         progressBar.reset();\r
38209     };\r
38210 \r
38211     // private\r
38212     var handleEsc = function(d, k, e){\r
38213         if(opt && opt.closable !== false){\r
38214             dlg.hide();\r
38215             handleHide();\r
38216         }\r
38217         if(e){\r
38218             e.stopEvent();\r
38219         }\r
38220     };\r
38221 \r
38222     // private\r
38223     var updateButtons = function(b){\r
38224         var width = 0;\r
38225         if(!b){\r
38226             buttons["ok"].hide();\r
38227             buttons["cancel"].hide();\r
38228             buttons["yes"].hide();\r
38229             buttons["no"].hide();\r
38230             return width;\r
38231         }\r
38232         dlg.footer.dom.style.display = '';\r
38233         for(var k in buttons){\r
38234             if(!Ext.isFunction(buttons[k])){\r
38235                 if(b[k]){\r
38236                     buttons[k].show();\r
38237                     buttons[k].setText(Ext.isString(b[k]) ? b[k] : Ext.MessageBox.buttonText[k]);\r
38238                     width += buttons[k].el.getWidth()+15;\r
38239                 }else{\r
38240                     buttons[k].hide();\r
38241                 }\r
38242             }\r
38243         }\r
38244         return width;\r
38245     };\r
38246 \r
38247     return {\r
38248         /**\r
38249          * Returns a reference to the underlying {@link Ext.Window} element\r
38250          * @return {Ext.Window} The window\r
38251          */\r
38252         getDialog : function(titleText){\r
38253            if(!dlg){\r
38254                 dlg = new Ext.Window({\r
38255                     autoCreate : true,\r
38256                     title:titleText,\r
38257                     resizable:false,\r
38258                     constrain:true,\r
38259                     constrainHeader:true,\r
38260                     minimizable : false,\r
38261                     maximizable : false,\r
38262                     stateful: false,\r
38263                     modal: true,\r
38264                     shim:true,\r
38265                     buttonAlign:"center",\r
38266                     width:400,\r
38267                     height:100,\r
38268                     minHeight: 80,\r
38269                     plain:true,\r
38270                     footer:true,\r
38271                     closable:true,\r
38272                     close : function(){\r
38273                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){\r
38274                             handleButton("no");\r
38275                         }else{\r
38276                             handleButton("cancel");\r
38277                         }\r
38278                     }\r
38279                 });\r
38280                 buttons = {};\r
38281                 var bt = this.buttonText;\r
38282                 //TODO: refactor this block into a buttons config to pass into the Window constructor\r
38283                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));\r
38284                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));\r
38285                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));\r
38286                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));\r
38287                 buttons["ok"].hideMode = buttons["yes"].hideMode = buttons["no"].hideMode = buttons["cancel"].hideMode = 'offsets';\r
38288                 dlg.render(document.body);\r
38289                 dlg.getEl().addClass('x-window-dlg');\r
38290                 mask = dlg.mask;\r
38291                 bodyEl = dlg.body.createChild({\r
38292                     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>'\r
38293                 });\r
38294                 iconEl = Ext.get(bodyEl.dom.firstChild);\r
38295                 var contentEl = bodyEl.dom.childNodes[1];\r
38296                 msgEl = Ext.get(contentEl.firstChild);\r
38297                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);\r
38298                 textboxEl.enableDisplayMode();\r
38299                 textboxEl.addKeyListener([10,13], function(){\r
38300                     if(dlg.isVisible() && opt && opt.buttons){\r
38301                         if(opt.buttons.ok){\r
38302                             handleButton("ok");\r
38303                         }else if(opt.buttons.yes){\r
38304                             handleButton("yes");\r
38305                         }\r
38306                     }\r
38307                 });\r
38308                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);\r
38309                 textareaEl.enableDisplayMode();\r
38310                 progressBar = new Ext.ProgressBar({\r
38311                     renderTo:bodyEl\r
38312                 });\r
38313                bodyEl.createChild({cls:'x-clear'});\r
38314             }\r
38315             return dlg;\r
38316         },\r
38317 \r
38318         /**\r
38319          * Updates the message box body text\r
38320          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to\r
38321          * the XHTML-compliant non-breaking space character '&amp;#160;')\r
38322          * @return {Ext.MessageBox} this\r
38323          */\r
38324         updateText : function(text){\r
38325             if(!dlg.isVisible() && !opt.width){\r
38326                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows\r
38327             }\r
38328             msgEl.update(text || '&#160;');\r
38329 \r
38330             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0;\r
38331             var mw = msgEl.getWidth() + msgEl.getMargins('lr');\r
38332             var fw = dlg.getFrameWidth('lr');\r
38333             var bw = dlg.body.getFrameWidth('lr');\r
38334             if (Ext.isIE && iw > 0){\r
38335                 //3 pixels get subtracted in the icon CSS for an IE margin issue,\r
38336                 //so we have to add it back here for the overall width to be consistent\r
38337                 iw += 3;\r
38338             }\r
38339             var w = Math.max(Math.min(opt.width || iw+mw+fw+bw, this.maxWidth),\r
38340                         Math.max(opt.minWidth || this.minWidth, bwidth || 0));\r
38341 \r
38342             if(opt.prompt === true){\r
38343                 activeTextEl.setWidth(w-iw-fw-bw);\r
38344             }\r
38345             if(opt.progress === true || opt.wait === true){\r
38346                 progressBar.setSize(w-iw-fw-bw);\r
38347             }\r
38348             if(Ext.isIE && w == bwidth){\r
38349                 w += 4; //Add offset when the content width is smaller than the buttons.    \r
38350             }\r
38351             dlg.setSize(w, 'auto').center();\r
38352             return this;\r
38353         },\r
38354 \r
38355         /**\r
38356          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes\r
38357          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},\r
38358          * or by calling {@link Ext.MessageBox#show} with progress: true.\r
38359          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)\r
38360          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')\r
38361          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined\r
38362          * so that any existing body text will not get overwritten by default unless a new value is passed in)\r
38363          * @return {Ext.MessageBox} this\r
38364          */\r
38365         updateProgress : function(value, progressText, msg){\r
38366             progressBar.updateProgress(value, progressText);\r
38367             if(msg){\r
38368                 this.updateText(msg);\r
38369             }\r
38370             return this;\r
38371         },\r
38372 \r
38373         /**\r
38374          * Returns true if the message box is currently displayed\r
38375          * @return {Boolean} True if the message box is visible, else false\r
38376          */\r
38377         isVisible : function(){\r
38378             return dlg && dlg.isVisible();\r
38379         },\r
38380 \r
38381         /**\r
38382          * Hides the message box if it is displayed\r
38383          * @return {Ext.MessageBox} this\r
38384          */\r
38385         hide : function(){\r
38386             var proxy = dlg ? dlg.activeGhost : null;\r
38387             if(this.isVisible() || proxy){\r
38388                 dlg.hide();\r
38389                 handleHide();\r
38390                 if (proxy){\r
38391                     // unghost is a private function, but i saw no better solution\r
38392                     // to fix the locking problem when dragging while it closes\r
38393                     dlg.unghost(false, false);\r
38394                 } \r
38395             }\r
38396             return this;\r
38397         },\r
38398 \r
38399         /**\r
38400          * Displays a new message box, or reinitializes an existing message box, based on the config options\r
38401          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,\r
38402          * although those calls are basic shortcuts and do not support all of the config options allowed here.\r
38403          * @param {Object} config The following config options are supported: <ul>\r
38404          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it\r
38405          * opens and closes (defaults to undefined)</div></li>\r
38406          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',\r
38407          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>\r
38408          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that\r
38409          * progress and wait dialogs will ignore this property and always hide the close button as they can only\r
38410          * be closed programmatically.</div></li>\r
38411          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>\r
38412          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea\r
38413          * if displayed (defaults to 75)</div></li>\r
38414          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either\r
38415          * by clicking on the configured buttons, or on the dialog close button, or by pressing\r
38416          * the return button to enter input.\r
38417          * <p>Progress and wait dialogs will ignore this option since they do not respond to user\r
38418          * actions and can only be closed programmatically, so any required function should be called\r
38419          * by the same code after it closes the dialog. Parameters passed:<ul>\r
38420          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>\r
38421          * <li><tt>ok</tt></li>\r
38422          * <li><tt>yes</tt></li>\r
38423          * <li><tt>no</tt></li>\r
38424          * <li><tt>cancel</tt></li>\r
38425          * </ul></div></div></li>\r
38426          * <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>\r
38427          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>\r
38428          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>\r
38429          * </ul></p></div></li>\r
38430          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>\r
38431          * <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\r
38432          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>\r
38433          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to\r
38434          * add an optional header icon (defaults to '')</div></li>\r
38435          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>\r
38436          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>\r
38437          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is\r
38438          * displayed (defaults to true)</div></li>\r
38439          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the\r
38440          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>\r
38441          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">\r
38442          * True to prompt the user to enter multi-line text (defaults to false)</div></li>\r
38443          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>\r
38444          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>\r
38445          * <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>\r
38446          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>\r
38447          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>\r
38448          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>\r
38449          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>\r
38450          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>\r
38451          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>\r
38452          * </ul>\r
38453          * Example usage:\r
38454          * <pre><code>\r
38455 Ext.Msg.show({\r
38456    title: 'Address',\r
38457    msg: 'Please enter your address:',\r
38458    width: 300,\r
38459    buttons: Ext.MessageBox.OKCANCEL,\r
38460    multiline: true,\r
38461    fn: saveAddress,\r
38462    animEl: 'addAddressBtn',\r
38463    icon: Ext.MessageBox.INFO\r
38464 });\r
38465 </code></pre>\r
38466          * @return {Ext.MessageBox} this\r
38467          */\r
38468         show : function(options){\r
38469             if(this.isVisible()){\r
38470                 this.hide();\r
38471             }\r
38472             opt = options;\r
38473             var d = this.getDialog(opt.title || "&#160;");\r
38474 \r
38475             d.setTitle(opt.title || "&#160;");\r
38476             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);\r
38477             d.tools.close.setDisplayed(allowClose);\r
38478             activeTextEl = textboxEl;\r
38479             opt.prompt = opt.prompt || (opt.multiline ? true : false);\r
38480             if(opt.prompt){\r
38481                 if(opt.multiline){\r
38482                     textboxEl.hide();\r
38483                     textareaEl.show();\r
38484                     textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);\r
38485                     activeTextEl = textareaEl;\r
38486                 }else{\r
38487                     textboxEl.show();\r
38488                     textareaEl.hide();\r
38489                 }\r
38490             }else{\r
38491                 textboxEl.hide();\r
38492                 textareaEl.hide();\r
38493             }\r
38494             activeTextEl.dom.value = opt.value || "";\r
38495             if(opt.prompt){\r
38496                 d.focusEl = activeTextEl;\r
38497             }else{\r
38498                 var bs = opt.buttons;\r
38499                 var db = null;\r
38500                 if(bs && bs.ok){\r
38501                     db = buttons["ok"];\r
38502                 }else if(bs && bs.yes){\r
38503                     db = buttons["yes"];\r
38504                 }\r
38505                 if (db){\r
38506                     d.focusEl = db;\r
38507                 }\r
38508             }\r
38509             if(opt.iconCls){\r
38510               d.setIconClass(opt.iconCls);\r
38511             }\r
38512             this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);\r
38513             bwidth = updateButtons(opt.buttons);\r
38514             progressBar.setVisible(opt.progress === true || opt.wait === true);\r
38515             this.updateProgress(0, opt.progressText);\r
38516             this.updateText(opt.msg);\r
38517             if(opt.cls){\r
38518                 d.el.addClass(opt.cls);\r
38519             }\r
38520             d.proxyDrag = opt.proxyDrag === true;\r
38521             d.modal = opt.modal !== false;\r
38522             d.mask = opt.modal !== false ? mask : false;\r
38523             if(!d.isVisible()){\r
38524                 // force it to the end of the z-index stack so it gets a cursor in FF\r
38525                 document.body.appendChild(dlg.el.dom);\r
38526                 d.setAnimateTarget(opt.animEl);\r
38527                 d.show(opt.animEl);\r
38528             }\r
38529 \r
38530             //workaround for window internally enabling keymap in afterShow\r
38531             d.on('show', function(){\r
38532                 if(allowClose === true){\r
38533                     d.keyMap.enable();\r
38534                 }else{\r
38535                     d.keyMap.disable();\r
38536                 }\r
38537             }, this, {single:true});\r
38538 \r
38539             if(opt.wait === true){\r
38540                 progressBar.wait(opt.waitConfig);\r
38541             }\r
38542             return this;\r
38543         },\r
38544 \r
38545         /**\r
38546          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default\r
38547          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')\r
38548          * to clear any existing icon. This method must be called before the MessageBox is shown.\r
38549          * The following built-in icon classes are supported, but you can also pass in a custom class name:\r
38550          * <pre>\r
38551 Ext.MessageBox.INFO\r
38552 Ext.MessageBox.WARNING\r
38553 Ext.MessageBox.QUESTION\r
38554 Ext.MessageBox.ERROR\r
38555          *</pre>\r
38556          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon\r
38557          * @return {Ext.MessageBox} this\r
38558          */\r
38559         setIcon : function(icon){\r
38560             if(!dlg){\r
38561                 bufferIcon = icon;\r
38562                 return;\r
38563             }\r
38564             bufferIcon = undefined;\r
38565             if(icon && icon != ''){\r
38566                 iconEl.removeClass('x-hidden');\r
38567                 iconEl.replaceClass(iconCls, icon);\r
38568                 bodyEl.addClass('x-dlg-icon');\r
38569                 iconCls = icon;\r
38570             }else{\r
38571                 iconEl.replaceClass(iconCls, 'x-hidden');\r
38572                 bodyEl.removeClass('x-dlg-icon');\r
38573                 iconCls = '';\r
38574             }\r
38575             return this;\r
38576         },\r
38577 \r
38578         /**\r
38579          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by\r
38580          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}\r
38581          * and closing the message box when the process is complete.\r
38582          * @param {String} title The title bar text\r
38583          * @param {String} msg The message box body text\r
38584          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')\r
38585          * @return {Ext.MessageBox} this\r
38586          */\r
38587         progress : function(title, msg, progressText){\r
38588             this.show({\r
38589                 title : title,\r
38590                 msg : msg,\r
38591                 buttons: false,\r
38592                 progress:true,\r
38593                 closable:false,\r
38594                 minWidth: this.minProgressWidth,\r
38595                 progressText: progressText\r
38596             });\r
38597             return this;\r
38598         },\r
38599 \r
38600         /**\r
38601          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user\r
38602          * interaction while waiting for a long-running process to complete that does not have defined intervals.\r
38603          * You are responsible for closing the message box when the process is complete.\r
38604          * @param {String} msg The message box body text\r
38605          * @param {String} title (optional) The title bar text\r
38606          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object\r
38607          * @return {Ext.MessageBox} this\r
38608          */\r
38609         wait : function(msg, title, config){\r
38610             this.show({\r
38611                 title : title,\r
38612                 msg : msg,\r
38613                 buttons: false,\r
38614                 closable:false,\r
38615                 wait:true,\r
38616                 modal:true,\r
38617                 minWidth: this.minProgressWidth,\r
38618                 waitConfig: config\r
38619             });\r
38620             return this;\r
38621         },\r
38622 \r
38623         /**\r
38624          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).\r
38625          * If a callback function is passed it will be called after the user clicks the button, and the\r
38626          * id of the button that was clicked will be passed as the only parameter to the callback\r
38627          * (could also be the top-right close button).\r
38628          * @param {String} title The title bar text\r
38629          * @param {String} msg The message box body text\r
38630          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
38631          * @param {Object} scope (optional) The scope of the callback function\r
38632          * @return {Ext.MessageBox} this\r
38633          */\r
38634         alert : function(title, msg, fn, scope){\r
38635             this.show({\r
38636                 title : title,\r
38637                 msg : msg,\r
38638                 buttons: this.OK,\r
38639                 fn: fn,\r
38640                 scope : scope\r
38641             });\r
38642             return this;\r
38643         },\r
38644 \r
38645         /**\r
38646          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).\r
38647          * If a callback function is passed it will be called after the user clicks either button,\r
38648          * and the id of the button that was clicked will be passed as the only parameter to the callback\r
38649          * (could also be the top-right close button).\r
38650          * @param {String} title The title bar text\r
38651          * @param {String} msg The message box body text\r
38652          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
38653          * @param {Object} scope (optional) The scope of the callback function\r
38654          * @return {Ext.MessageBox} this\r
38655          */\r
38656         confirm : function(title, msg, fn, scope){\r
38657             this.show({\r
38658                 title : title,\r
38659                 msg : msg,\r
38660                 buttons: this.YESNO,\r
38661                 fn: fn,\r
38662                 scope : scope,\r
38663                 icon: this.QUESTION\r
38664             });\r
38665             return this;\r
38666         },\r
38667 \r
38668         /**\r
38669          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).\r
38670          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user\r
38671          * clicks either button, and the id of the button that was clicked (could also be the top-right\r
38672          * close button) and the text that was entered will be passed as the two parameters to the callback.\r
38673          * @param {String} title The title bar text\r
38674          * @param {String} msg The message box body text\r
38675          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
38676          * @param {Object} scope (optional) The scope of the callback function\r
38677          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight\r
38678          * property, or the height in pixels to create the textbox (defaults to false / single-line)\r
38679          * @param {String} value (optional) Default value of the text input element (defaults to '')\r
38680          * @return {Ext.MessageBox} this\r
38681          */\r
38682         prompt : function(title, msg, fn, scope, multiline, value){\r
38683             this.show({\r
38684                 title : title,\r
38685                 msg : msg,\r
38686                 buttons: this.OKCANCEL,\r
38687                 fn: fn,\r
38688                 minWidth:250,\r
38689                 scope : scope,\r
38690                 prompt:true,\r
38691                 multiline: multiline,\r
38692                 value: value\r
38693             });\r
38694             return this;\r
38695         },\r
38696 \r
38697         /**\r
38698          * Button config that displays a single OK button\r
38699          * @type Object\r
38700          */\r
38701         OK : {ok:true},\r
38702         /**\r
38703          * Button config that displays a single Cancel button\r
38704          * @type Object\r
38705          */\r
38706         CANCEL : {cancel:true},\r
38707         /**\r
38708          * Button config that displays OK and Cancel buttons\r
38709          * @type Object\r
38710          */\r
38711         OKCANCEL : {ok:true, cancel:true},\r
38712         /**\r
38713          * Button config that displays Yes and No buttons\r
38714          * @type Object\r
38715          */\r
38716         YESNO : {yes:true, no:true},\r
38717         /**\r
38718          * Button config that displays Yes, No and Cancel buttons\r
38719          * @type Object\r
38720          */\r
38721         YESNOCANCEL : {yes:true, no:true, cancel:true},\r
38722         /**\r
38723          * The CSS class that provides the INFO icon image\r
38724          * @type String\r
38725          */\r
38726         INFO : 'ext-mb-info',\r
38727         /**\r
38728          * The CSS class that provides the WARNING icon image\r
38729          * @type String\r
38730          */\r
38731         WARNING : 'ext-mb-warning',\r
38732         /**\r
38733          * The CSS class that provides the QUESTION icon image\r
38734          * @type String\r
38735          */\r
38736         QUESTION : 'ext-mb-question',\r
38737         /**\r
38738          * The CSS class that provides the ERROR icon image\r
38739          * @type String\r
38740          */\r
38741         ERROR : 'ext-mb-error',\r
38742 \r
38743         /**\r
38744          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)\r
38745          * @type Number\r
38746          */\r
38747         defaultTextHeight : 75,\r
38748         /**\r
38749          * The maximum width in pixels of the message box (defaults to 600)\r
38750          * @type Number\r
38751          */\r
38752         maxWidth : 600,\r
38753         /**\r
38754          * The minimum width in pixels of the message box (defaults to 100)\r
38755          * @type Number\r
38756          */\r
38757         minWidth : 100,\r
38758         /**\r
38759          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful\r
38760          * for setting a different minimum width than text-only dialogs may need (defaults to 250)\r
38761          * @type Number\r
38762          */\r
38763         minProgressWidth : 250,\r
38764         /**\r
38765          * An object containing the default button text strings that can be overriden for localized language support.\r
38766          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific\r
38767          * resource file for handling language support across the framework.\r
38768          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french\r
38769          * @type Object\r
38770          */\r
38771         buttonText : {\r
38772             ok : "OK",\r
38773             cancel : "Cancel",\r
38774             yes : "Yes",\r
38775             no : "No"\r
38776         }\r
38777     };\r
38778 }();\r
38779 \r
38780 /**\r
38781  * Shorthand for {@link Ext.MessageBox}\r
38782  */\r
38783 Ext.Msg = Ext.MessageBox;/**\r
38784  * @class Ext.dd.PanelProxy\r
38785  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally\r
38786  * for the Panel's drag drop implementation, and should never need to be created directly.\r
38787  * @constructor\r
38788  * @param panel The {@link Ext.Panel} to proxy for\r
38789  * @param config Configuration options\r
38790  */\r
38791 Ext.dd.PanelProxy = function(panel, config){\r
38792     this.panel = panel;\r
38793     this.id = this.panel.id +'-ddproxy';\r
38794     Ext.apply(this, config);\r
38795 };\r
38796 \r
38797 Ext.dd.PanelProxy.prototype = {\r
38798     /**\r
38799      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,\r
38800      * false to drag with no proxy (defaults to true).\r
38801      */\r
38802     insertProxy : true,\r
38803 \r
38804     // private overrides\r
38805     setStatus : Ext.emptyFn,\r
38806     reset : Ext.emptyFn,\r
38807     update : Ext.emptyFn,\r
38808     stop : Ext.emptyFn,\r
38809     sync: Ext.emptyFn,\r
38810 \r
38811     /**\r
38812      * Gets the proxy's element\r
38813      * @return {Element} The proxy's element\r
38814      */\r
38815     getEl : function(){\r
38816         return this.ghost;\r
38817     },\r
38818 \r
38819     /**\r
38820      * Gets the proxy's ghost element\r
38821      * @return {Element} The proxy's ghost element\r
38822      */\r
38823     getGhost : function(){\r
38824         return this.ghost;\r
38825     },\r
38826 \r
38827     /**\r
38828      * Gets the proxy's element\r
38829      * @return {Element} The proxy's element\r
38830      */\r
38831     getProxy : function(){\r
38832         return this.proxy;\r
38833     },\r
38834 \r
38835     /**\r
38836      * Hides the proxy\r
38837      */\r
38838     hide : function(){\r
38839         if(this.ghost){\r
38840             if(this.proxy){\r
38841                 this.proxy.remove();\r
38842                 delete this.proxy;\r
38843             }\r
38844             this.panel.el.dom.style.display = '';\r
38845             this.ghost.remove();\r
38846             delete this.ghost;\r
38847         }\r
38848     },\r
38849 \r
38850     /**\r
38851      * Shows the proxy\r
38852      */\r
38853     show : function(){\r
38854         if(!this.ghost){\r
38855             this.ghost = this.panel.createGhost(undefined, undefined, Ext.getBody());\r
38856             this.ghost.setXY(this.panel.el.getXY())\r
38857             if(this.insertProxy){\r
38858                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});\r
38859                 this.proxy.setSize(this.panel.getSize());\r
38860             }\r
38861             this.panel.el.dom.style.display = 'none';\r
38862         }\r
38863     },\r
38864 \r
38865     // private\r
38866     repair : function(xy, callback, scope){\r
38867         this.hide();\r
38868         if(typeof callback == "function"){\r
38869             callback.call(scope || this);\r
38870         }\r
38871     },\r
38872 \r
38873     /**\r
38874      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel\r
38875      * to keep the proxy sync'd to the Panel's location.\r
38876      * @param {HTMLElement} parentNode The proxy's parent DOM node\r
38877      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults\r
38878      * to the parent's last child if not specified)\r
38879      */\r
38880     moveProxy : function(parentNode, before){\r
38881         if(this.proxy){\r
38882             parentNode.insertBefore(this.proxy.dom, before);\r
38883         }\r
38884     }\r
38885 };\r
38886 \r
38887 // private - DD implementation for Panels\r
38888 Ext.Panel.DD = function(panel, cfg){\r
38889     this.panel = panel;\r
38890     this.dragData = {panel: panel};\r
38891     this.proxy = new Ext.dd.PanelProxy(panel, cfg);\r
38892     Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);\r
38893     var h = panel.header;\r
38894     if(h){\r
38895         this.setHandleElId(h.id);\r
38896     }\r
38897     (h ? h : this.panel.body).setStyle('cursor', 'move');\r
38898     this.scroll = false;\r
38899 };\r
38900 \r
38901 Ext.extend(Ext.Panel.DD, Ext.dd.DragSource, {\r
38902     showFrame: Ext.emptyFn,\r
38903     startDrag: Ext.emptyFn,\r
38904     b4StartDrag: function(x, y) {\r
38905         this.proxy.show();\r
38906     },\r
38907     b4MouseDown: function(e) {\r
38908         var x = e.getPageX();\r
38909         var y = e.getPageY();\r
38910         this.autoOffset(x, y);\r
38911     },\r
38912     onInitDrag : function(x, y){\r
38913         this.onStartDrag(x, y);\r
38914         return true;\r
38915     },\r
38916     createFrame : Ext.emptyFn,\r
38917     getDragEl : function(e){\r
38918         return this.proxy.ghost.dom;\r
38919     },\r
38920     endDrag : function(e){\r
38921         this.proxy.hide();\r
38922         this.panel.saveState();\r
38923     },\r
38924 \r
38925     autoOffset : function(x, y) {\r
38926         x -= this.startPageX;\r
38927         y -= this.startPageY;\r
38928         this.setDelta(x, y);\r
38929     }\r
38930 });/**
38931  * @class Ext.state.Provider
38932  * Abstract base class for state provider implementations. This class provides methods
38933  * for encoding and decoding <b>typed</b> variables including dates and defines the
38934  * Provider interface.
38935  */
38936 Ext.state.Provider = function(){
38937     /**
38938      * @event statechange
38939      * Fires when a state change occurs.
38940      * @param {Provider} this This state provider
38941      * @param {String} key The state key which was changed
38942      * @param {String} value The encoded value for the state
38943      */
38944     this.addEvents("statechange");
38945     this.state = {};
38946     Ext.state.Provider.superclass.constructor.call(this);
38947 };
38948 Ext.extend(Ext.state.Provider, Ext.util.Observable, {
38949     /**
38950      * Returns the current value for a key
38951      * @param {String} name The key name
38952      * @param {Mixed} defaultValue A default value to return if the key's value is not found
38953      * @return {Mixed} The state data
38954      */
38955     get : function(name, defaultValue){
38956         return typeof this.state[name] == "undefined" ?
38957             defaultValue : this.state[name];
38958     },
38959
38960     /**
38961      * Clears a value from the state
38962      * @param {String} name The key name
38963      */
38964     clear : function(name){
38965         delete this.state[name];
38966         this.fireEvent("statechange", this, name, null);
38967     },
38968
38969     /**
38970      * Sets the value for a key
38971      * @param {String} name The key name
38972      * @param {Mixed} value The value to set
38973      */
38974     set : function(name, value){
38975         this.state[name] = value;
38976         this.fireEvent("statechange", this, name, value);
38977     },
38978
38979     /**
38980      * Decodes a string previously encoded with {@link #encodeValue}.
38981      * @param {String} value The value to decode
38982      * @return {Mixed} The decoded value
38983      */
38984     decodeValue : function(cookie){
38985         var re = /^(a|n|d|b|s|o)\:(.*)$/;
38986         var matches = re.exec(unescape(cookie));
38987         if(!matches || !matches[1]) return; // non state cookie
38988         var type = matches[1];
38989         var v = matches[2];
38990         switch(type){
38991             case "n":
38992                 return parseFloat(v);
38993             case "d":
38994                 return new Date(Date.parse(v));
38995             case "b":
38996                 return (v == "1");
38997             case "a":
38998                 var all = [];
38999                 if(v != ''){
39000                     Ext.each(v.split('^'), function(val){
39001                         all.push(this.decodeValue(val));
39002                     }, this);
39003                 }
39004                 return all;
39005            case "o":
39006                 var all = {};
39007                 if(v != ''){
39008                     Ext.each(v.split('^'), function(val){
39009                         var kv = val.split('=');
39010                         all[kv[0]] = this.decodeValue(kv[1]);
39011                     }, this);
39012                 }
39013                 return all;
39014            default:
39015                 return v;
39016         }
39017     },
39018
39019     /**
39020      * Encodes a value including type information.  Decode with {@link #decodeValue}.
39021      * @param {Mixed} value The value to encode
39022      * @return {String} The encoded value
39023      */
39024     encodeValue : function(v){
39025         var enc;
39026         if(typeof v == "number"){
39027             enc = "n:" + v;
39028         }else if(typeof v == "boolean"){
39029             enc = "b:" + (v ? "1" : "0");
39030         }else if(Ext.isDate(v)){
39031             enc = "d:" + v.toGMTString();
39032         }else if(Ext.isArray(v)){
39033             var flat = "";
39034             for(var i = 0, len = v.length; i < len; i++){
39035                 flat += this.encodeValue(v[i]);
39036                 if(i != len-1) flat += "^";
39037             }
39038             enc = "a:" + flat;
39039         }else if(typeof v == "object"){
39040             var flat = "";
39041             for(var key in v){
39042                 if(typeof v[key] != "function" && v[key] !== undefined){
39043                     flat += key + "=" + this.encodeValue(v[key]) + "^";
39044                 }
39045             }
39046             enc = "o:" + flat.substring(0, flat.length-1);
39047         }else{
39048             enc = "s:" + v;
39049         }
39050         return escape(enc);
39051     }
39052 });
39053 /**\r
39054  * @class Ext.state.Manager\r
39055  * This is the global state manager. By default all components that are "state aware" check this class\r
39056  * for state information if you don't pass them a custom state provider. In order for this class\r
39057  * to be useful, it must be initialized with a provider when your application initializes. Example usage:\r
39058  <pre><code>\r
39059 // in your initialization function\r
39060 init : function(){\r
39061    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());\r
39062    var win = new Window(...);\r
39063    win.restoreState();\r
39064 }\r
39065  </code></pre>\r
39066  * @singleton\r
39067  */\r
39068 Ext.state.Manager = function(){\r
39069     var provider = new Ext.state.Provider();\r
39070 \r
39071     return {\r
39072         /**\r
39073          * Configures the default state provider for your application\r
39074          * @param {Provider} stateProvider The state provider to set\r
39075          */\r
39076         setProvider : function(stateProvider){\r
39077             provider = stateProvider;\r
39078         },\r
39079 \r
39080         /**\r
39081          * Returns the current value for a key\r
39082          * @param {String} name The key name\r
39083          * @param {Mixed} defaultValue The default value to return if the key lookup does not match\r
39084          * @return {Mixed} The state data\r
39085          */\r
39086         get : function(key, defaultValue){\r
39087             return provider.get(key, defaultValue);\r
39088         },\r
39089 \r
39090         /**\r
39091          * Sets the value for a key\r
39092          * @param {String} name The key name\r
39093          * @param {Mixed} value The state data\r
39094          */\r
39095          set : function(key, value){\r
39096             provider.set(key, value);\r
39097         },\r
39098 \r
39099         /**\r
39100          * Clears a value from the state\r
39101          * @param {String} name The key name\r
39102          */\r
39103         clear : function(key){\r
39104             provider.clear(key);\r
39105         },\r
39106 \r
39107         /**\r
39108          * Gets the currently configured state provider\r
39109          * @return {Provider} The state provider\r
39110          */\r
39111         getProvider : function(){\r
39112             return provider;\r
39113         }\r
39114     };\r
39115 }();\r
39116 /**\r
39117  * @class Ext.state.CookieProvider\r
39118  * @extends Ext.state.Provider\r
39119  * The default Provider implementation which saves state via cookies.\r
39120  * <br />Usage:\r
39121  <pre><code>\r
39122    var cp = new Ext.state.CookieProvider({\r
39123        path: "/cgi-bin/",\r
39124        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days\r
39125        domain: "extjs.com"\r
39126    });\r
39127    Ext.state.Manager.setProvider(cp);\r
39128  </code></pre>\r
39129  * @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
39130  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)\r
39131  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than\r
39132  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include\r
39133  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same\r
39134  * domain the page is running on including the 'www' like 'www.extjs.com')\r
39135  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)\r
39136  * @constructor\r
39137  * Create a new CookieProvider\r
39138  * @param {Object} config The configuration object\r
39139  */\r
39140 Ext.state.CookieProvider = function(config){\r
39141     Ext.state.CookieProvider.superclass.constructor.call(this);\r
39142     this.path = "/";\r
39143     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days\r
39144     this.domain = null;\r
39145     this.secure = false;\r
39146     Ext.apply(this, config);\r
39147     this.state = this.readCookies();\r
39148 };\r
39149 \r
39150 Ext.extend(Ext.state.CookieProvider, Ext.state.Provider, {\r
39151     // private\r
39152     set : function(name, value){\r
39153         if(typeof value == "undefined" || value === null){\r
39154             this.clear(name);\r
39155             return;\r
39156         }\r
39157         this.setCookie(name, value);\r
39158         Ext.state.CookieProvider.superclass.set.call(this, name, value);\r
39159     },\r
39160 \r
39161     // private\r
39162     clear : function(name){\r
39163         this.clearCookie(name);\r
39164         Ext.state.CookieProvider.superclass.clear.call(this, name);\r
39165     },\r
39166 \r
39167     // private\r
39168     readCookies : function(){\r
39169         var cookies = {};\r
39170         var c = document.cookie + ";";\r
39171         var re = /\s?(.*?)=(.*?);/g;\r
39172         var matches;\r
39173         while((matches = re.exec(c)) != null){\r
39174             var name = matches[1];\r
39175             var value = matches[2];\r
39176             if(name && name.substring(0,3) == "ys-"){\r
39177                 cookies[name.substr(3)] = this.decodeValue(value);\r
39178             }\r
39179         }\r
39180         return cookies;\r
39181     },\r
39182 \r
39183     // private\r
39184     setCookie : function(name, value){\r
39185         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +\r
39186            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +\r
39187            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
39188            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
39189            ((this.secure == true) ? "; secure" : "");\r
39190     },\r
39191 \r
39192     // private\r
39193     clearCookie : function(name){\r
39194         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +\r
39195            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
39196            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
39197            ((this.secure == true) ? "; secure" : "");\r
39198     }\r
39199 });/**
39200  * @class Ext.DataView
39201  * @extends Ext.BoxComponent
39202  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
39203  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
39204  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
39205  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
39206  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
39207  * config must be provided for the DataView to determine what nodes it will be working with.</b>
39208  *
39209  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
39210  * <pre><code>
39211 var store = new Ext.data.JsonStore({
39212     url: 'get-images.php',
39213     root: 'images',
39214     fields: [
39215         'name', 'url',
39216         {name:'size', type: 'float'},
39217         {name:'lastmod', type:'date', dateFormat:'timestamp'}
39218     ]
39219 });
39220 store.load();
39221
39222 var tpl = new Ext.XTemplate(
39223     '&lt;tpl for="."&gt;',
39224         '&lt;div class="thumb-wrap" id="{name}"&gt;',
39225         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
39226         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
39227     '&lt;/tpl&gt;',
39228     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
39229 );
39230
39231 var panel = new Ext.Panel({
39232     id:'images-view',
39233     frame:true,
39234     width:535,
39235     autoHeight:true,
39236     collapsible:true,
39237     layout:'fit',
39238     title:'Simple DataView',
39239
39240     items: new Ext.DataView({
39241         store: store,
39242         tpl: tpl,
39243         autoHeight:true,
39244         multiSelect: true,
39245         overClass:'x-view-over',
39246         itemSelector:'div.thumb-wrap',
39247         emptyText: 'No images to display'
39248     })
39249 });
39250 panel.render(document.body);
39251 </code></pre>
39252  * @constructor
39253  * Create a new DataView
39254  * @param {Object} config The config object
39255  * @xtype dataview
39256  */
39257 Ext.DataView = Ext.extend(Ext.BoxComponent, {
39258     /**
39259      * @cfg {String/Array} tpl
39260      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
39261      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
39262      */
39263     /**
39264      * @cfg {Ext.data.Store} store
39265      * The {@link Ext.data.Store} to bind this DataView to.
39266      */
39267     /**
39268      * @cfg {String} itemSelector
39269      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
39270      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
39271      * working with.
39272      */
39273     /**
39274      * @cfg {Boolean} multiSelect
39275      * True to allow selection of more than one item at a time, false to allow selection of only a single item
39276      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
39277      */
39278     /**
39279      * @cfg {Boolean} singleSelect
39280      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
39281      * Note that if {@link #multiSelect} = true, this value will be ignored.
39282      */
39283     /**
39284      * @cfg {Boolean} simpleSelect
39285      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
39286      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
39287      */
39288     /**
39289      * @cfg {String} overClass
39290      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
39291      */
39292     /**
39293      * @cfg {String} loadingText
39294      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
39295      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
39296      * contents will continue to display normally until the new data is loaded and the contents are replaced.
39297      */
39298     /**
39299      * @cfg {String} selectedClass
39300      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
39301      */
39302     selectedClass : "x-view-selected",
39303     /**
39304      * @cfg {String} emptyText
39305      * The text to display in the view when there is no data to display (defaults to '').
39306      */
39307     emptyText : "",
39308
39309     /**
39310      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
39311      */
39312     deferEmptyText: true,
39313     /**
39314      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
39315      */
39316     trackOver: false,
39317
39318     //private
39319     last: false,
39320
39321     // private
39322     initComponent : function(){
39323         Ext.DataView.superclass.initComponent.call(this);
39324         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
39325             this.tpl = new Ext.XTemplate(this.tpl);
39326         }
39327
39328         this.addEvents(
39329             /**
39330              * @event beforeclick
39331              * Fires before a click is processed. Returns false to cancel the default action.
39332              * @param {Ext.DataView} this
39333              * @param {Number} index The index of the target node
39334              * @param {HTMLElement} node The target node
39335              * @param {Ext.EventObject} e The raw event object
39336              */
39337             "beforeclick",
39338             /**
39339              * @event click
39340              * Fires when a template node is clicked.
39341              * @param {Ext.DataView} this
39342              * @param {Number} index The index of the target node
39343              * @param {HTMLElement} node The target node
39344              * @param {Ext.EventObject} e The raw event object
39345              */
39346             "click",
39347             /**
39348              * @event mouseenter
39349              * Fires when the mouse enters a template node. trackOver:true or an overCls must be set to enable this event.
39350              * @param {Ext.DataView} this
39351              * @param {Number} index The index of the target node
39352              * @param {HTMLElement} node The target node
39353              * @param {Ext.EventObject} e The raw event object
39354              */
39355             "mouseenter",
39356             /**
39357              * @event mouseleave
39358              * Fires when the mouse leaves a template node. trackOver:true or an overCls must be set to enable this event.
39359              * @param {Ext.DataView} this
39360              * @param {Number} index The index of the target node
39361              * @param {HTMLElement} node The target node
39362              * @param {Ext.EventObject} e The raw event object
39363              */
39364             "mouseleave",
39365             /**
39366              * @event containerclick
39367              * Fires when a click occurs and it is not on a template node.
39368              * @param {Ext.DataView} this
39369              * @param {Ext.EventObject} e The raw event object
39370              */
39371             "containerclick",
39372             /**
39373              * @event dblclick
39374              * Fires when a template node is double clicked.
39375              * @param {Ext.DataView} this
39376              * @param {Number} index The index of the target node
39377              * @param {HTMLElement} node The target node
39378              * @param {Ext.EventObject} e The raw event object
39379              */
39380             "dblclick",
39381             /**
39382              * @event contextmenu
39383              * Fires when a template node is right clicked.
39384              * @param {Ext.DataView} this
39385              * @param {Number} index The index of the target node
39386              * @param {HTMLElement} node The target node
39387              * @param {Ext.EventObject} e The raw event object
39388              */
39389             "contextmenu",
39390             /**
39391              * @event containercontextmenu
39392              * Fires when a right click occurs that is not on a template node.
39393              * @param {Ext.DataView} this
39394              * @param {Ext.EventObject} e The raw event object
39395              */
39396             "containercontextmenu",
39397             /**
39398              * @event selectionchange
39399              * Fires when the selected nodes change.
39400              * @param {Ext.DataView} this
39401              * @param {Array} selections Array of the selected nodes
39402              */
39403             "selectionchange",
39404
39405             /**
39406              * @event beforeselect
39407              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
39408              * @param {Ext.DataView} this
39409              * @param {HTMLElement} node The node to be selected
39410              * @param {Array} selections Array of currently selected nodes
39411              */
39412             "beforeselect"
39413         );
39414
39415         this.store = Ext.StoreMgr.lookup(this.store);
39416         this.all = new Ext.CompositeElementLite();
39417         this.selected = new Ext.CompositeElementLite();
39418     },
39419
39420     // private
39421     afterRender : function(){
39422         Ext.DataView.superclass.afterRender.call(this);
39423
39424                 this.mon(this.getTemplateTarget(), {
39425             "click": this.onClick,
39426             "dblclick": this.onDblClick,
39427             "contextmenu": this.onContextMenu,
39428             scope:this
39429         });
39430
39431         if(this.overClass || this.trackOver){
39432             this.mon(this.getTemplateTarget(), {
39433                 "mouseover": this.onMouseOver,
39434                 "mouseout": this.onMouseOut,
39435                 scope:this
39436             });
39437         }
39438
39439         if(this.store){
39440             this.bindStore(this.store, true);
39441         }
39442     },
39443
39444     /**
39445      * Refreshes the view by reloading the data from the store and re-rendering the template.
39446      */
39447     refresh : function(){
39448         this.clearSelections(false, true);
39449         var el = this.getTemplateTarget();
39450         el.update("");
39451         var records = this.store.getRange();
39452         if(records.length < 1){
39453             if(!this.deferEmptyText || this.hasSkippedEmptyText){
39454                 el.update(this.emptyText);
39455             }
39456             this.all.clear();
39457         }else{
39458             this.tpl.overwrite(el, this.collectData(records, 0));
39459             this.all.fill(Ext.query(this.itemSelector, el.dom));
39460             this.updateIndexes(0);
39461         }
39462         this.hasSkippedEmptyText = true;
39463     },
39464
39465     getTemplateTarget: function(){
39466         return this.el;
39467     },
39468
39469     /**
39470      * Function which can be overridden to provide custom formatting for each Record that is used by this
39471      * DataView's {@link #tpl template} to render each node.
39472      * @param {Array/Object} data The raw data object that was used to create the Record.
39473      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
39474      * @param {Record} record The Record being prepared for rendering.
39475      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
39476      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
39477      */
39478     prepareData : function(data){
39479         return data;
39480     },
39481
39482     /**
39483      * <p>Function which can be overridden which returns the data object passed to this
39484      * DataView's {@link #tpl template} to render the whole DataView.</p>
39485      * <p>This is usually an Array of data objects, each element of which is processed by an
39486      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
39487      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
39488      * provide non-repeating data such as headings, totals etc.</p>
39489      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
39490      * @param {Number} startIndex the index number of the Record being prepared for rendering.
39491      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
39492      * contain <i>named</i> properties.
39493      */
39494     collectData : function(records, startIndex){
39495         var r = [];
39496         for(var i = 0, len = records.length; i < len; i++){
39497             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
39498         }
39499         return r;
39500     },
39501
39502     // private
39503     bufferRender : function(records){
39504         var div = document.createElement('div');
39505         this.tpl.overwrite(div, this.collectData(records));
39506         return Ext.query(this.itemSelector, div);
39507     },
39508
39509     // private
39510     onUpdate : function(ds, record){
39511         var index = this.store.indexOf(record);
39512         if(index > -1){
39513             var sel = this.isSelected(index);
39514             var original = this.all.elements[index];
39515             var node = this.bufferRender([record], index)[0];
39516
39517             this.all.replaceElement(index, node, true);
39518             if(sel){
39519                 this.selected.replaceElement(original, node);
39520                 this.all.item(index).addClass(this.selectedClass);
39521             }
39522             this.updateIndexes(index, index);
39523         }
39524     },
39525
39526     // private
39527     onAdd : function(ds, records, index){
39528         if(this.all.getCount() === 0){
39529             this.refresh();
39530             return;
39531         }
39532         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
39533         if(index < this.all.getCount()){
39534             n = this.all.item(index).insertSibling(nodes, 'before', true);
39535             a.splice.apply(a, [index, 0].concat(nodes));
39536         }else{
39537             n = this.all.last().insertSibling(nodes, 'after', true);
39538             a.push.apply(a, nodes);
39539         }
39540         this.updateIndexes(index);
39541     },
39542
39543     // private
39544     onRemove : function(ds, record, index){
39545         this.deselect(index);
39546         this.all.removeElement(index, true);
39547         this.updateIndexes(index);
39548         if (this.store.getCount() === 0){
39549             this.refresh();
39550         }
39551     },
39552
39553     /**
39554      * Refreshes an individual node's data from the store.
39555      * @param {Number} index The item's data index in the store
39556      */
39557     refreshNode : function(index){
39558         this.onUpdate(this.store, this.store.getAt(index));
39559     },
39560
39561     // private
39562     updateIndexes : function(startIndex, endIndex){
39563         var ns = this.all.elements;
39564         startIndex = startIndex || 0;
39565         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
39566         for(var i = startIndex; i <= endIndex; i++){
39567             ns[i].viewIndex = i;
39568         }
39569     },
39570     
39571     /**
39572      * Returns the store associated with this DataView.
39573      * @return {Ext.data.Store} The store
39574      */
39575     getStore : function(){
39576         return this.store;
39577     },
39578
39579     /**
39580      * Changes the data store bound to this view and refreshes it.
39581      * @param {Store} store The store to bind to this view
39582      */
39583     bindStore : function(store, initial){
39584         if(!initial && this.store){
39585             if(store !== this.store && this.store.autoDestroy){
39586                 this.store.destroy();
39587             }else{
39588                 this.store.un("beforeload", this.onBeforeLoad, this);
39589                 this.store.un("datachanged", this.refresh, this);
39590                 this.store.un("add", this.onAdd, this);
39591                 this.store.un("remove", this.onRemove, this);
39592                 this.store.un("update", this.onUpdate, this);
39593                 this.store.un("clear", this.refresh, this);
39594             }
39595             if(!store){
39596                 this.store = null;
39597             }
39598         }
39599         if(store){
39600             store = Ext.StoreMgr.lookup(store);
39601             store.on({
39602                 scope: this,
39603                 beforeload: this.onBeforeLoad,
39604                 datachanged: this.refresh,
39605                 add: this.onAdd,
39606                 remove: this.onRemove,
39607                 update: this.onUpdate,
39608                 clear: this.refresh
39609             });
39610         }
39611         this.store = store;
39612         if(store){
39613             this.refresh();
39614         }
39615     },
39616
39617     /**
39618      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
39619      * @param {HTMLElement} node
39620      * @return {HTMLElement} The template node
39621      */
39622     findItemFromChild : function(node){
39623         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
39624     },
39625
39626     // private
39627     onClick : function(e){
39628         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
39629         if(item){
39630             var index = this.indexOf(item);
39631             if(this.onItemClick(item, index, e) !== false){
39632                 this.fireEvent("click", this, index, item, e);
39633             }
39634         }else{
39635             if(this.fireEvent("containerclick", this, e) !== false){
39636                 this.onContainerClick(e);
39637             }
39638         }
39639     },
39640
39641     onContainerClick : function(e){
39642         this.clearSelections();
39643     },
39644
39645     // private
39646     onContextMenu : function(e){
39647         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
39648         if(item){
39649             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
39650         }else{
39651             this.fireEvent("containercontextmenu", this, e);
39652         }
39653     },
39654
39655     // private
39656     onDblClick : function(e){
39657         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
39658         if(item){
39659             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
39660         }
39661     },
39662
39663     // private
39664     onMouseOver : function(e){
39665         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
39666         if(item && item !== this.lastItem){
39667             this.lastItem = item;
39668             Ext.fly(item).addClass(this.overClass);
39669             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
39670         }
39671     },
39672
39673     // private
39674     onMouseOut : function(e){
39675         if(this.lastItem){
39676             if(!e.within(this.lastItem, true, true)){
39677                 Ext.fly(this.lastItem).removeClass(this.overClass);
39678                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
39679                 delete this.lastItem;
39680             }
39681         }
39682     },
39683
39684     // private
39685     onItemClick : function(item, index, e){
39686         if(this.fireEvent("beforeclick", this, index, item, e) === false){
39687             return false;
39688         }
39689         if(this.multiSelect){
39690             this.doMultiSelection(item, index, e);
39691             e.preventDefault();
39692         }else if(this.singleSelect){
39693             this.doSingleSelection(item, index, e);
39694             e.preventDefault();
39695         }
39696         return true;
39697     },
39698
39699     // private
39700     doSingleSelection : function(item, index, e){
39701         if(e.ctrlKey && this.isSelected(index)){
39702             this.deselect(index);
39703         }else{
39704             this.select(index, false);
39705         }
39706     },
39707
39708     // private
39709     doMultiSelection : function(item, index, e){
39710         if(e.shiftKey && this.last !== false){
39711             var last = this.last;
39712             this.selectRange(last, index, e.ctrlKey);
39713             this.last = last; // reset the last
39714         }else{
39715             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
39716                 this.deselect(index);
39717             }else{
39718                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
39719             }
39720         }
39721     },
39722
39723     /**
39724      * Gets the number of selected nodes.
39725      * @return {Number} The node count
39726      */
39727     getSelectionCount : function(){
39728         return this.selected.getCount();
39729     },
39730
39731     /**
39732      * Gets the currently selected nodes.
39733      * @return {Array} An array of HTMLElements
39734      */
39735     getSelectedNodes : function(){
39736         return this.selected.elements;
39737     },
39738
39739     /**
39740      * Gets the indexes of the selected nodes.
39741      * @return {Array} An array of numeric indexes
39742      */
39743     getSelectedIndexes : function(){
39744         var indexes = [], s = this.selected.elements;
39745         for(var i = 0, len = s.length; i < len; i++){
39746             indexes.push(s[i].viewIndex);
39747         }
39748         return indexes;
39749     },
39750
39751     /**
39752      * Gets an array of the selected records
39753      * @return {Array} An array of {@link Ext.data.Record} objects
39754      */
39755     getSelectedRecords : function(){
39756         var r = [], s = this.selected.elements;
39757         for(var i = 0, len = s.length; i < len; i++){
39758             r[r.length] = this.store.getAt(s[i].viewIndex);
39759         }
39760         return r;
39761     },
39762
39763     /**
39764      * Gets an array of the records from an array of nodes
39765      * @param {Array} nodes The nodes to evaluate
39766      * @return {Array} records The {@link Ext.data.Record} objects
39767      */
39768     getRecords : function(nodes){
39769         var r = [], s = nodes;
39770         for(var i = 0, len = s.length; i < len; i++){
39771             r[r.length] = this.store.getAt(s[i].viewIndex);
39772         }
39773         return r;
39774     },
39775
39776     /**
39777      * Gets a record from a node
39778      * @param {HTMLElement} node The node to evaluate
39779      * @return {Record} record The {@link Ext.data.Record} object
39780      */
39781     getRecord : function(node){
39782         return this.store.getAt(node.viewIndex);
39783     },
39784
39785     /**
39786      * Clears all selections.
39787      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
39788      */
39789     clearSelections : function(suppressEvent, skipUpdate){
39790         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
39791             if(!skipUpdate){
39792                 this.selected.removeClass(this.selectedClass);
39793             }
39794             this.selected.clear();
39795             this.last = false;
39796             if(!suppressEvent){
39797                 this.fireEvent("selectionchange", this, this.selected.elements);
39798             }
39799         }
39800     },
39801
39802     /**
39803      * Returns true if the passed node is selected, else false.
39804      * @param {HTMLElement/Number} node The node or node index to check
39805      * @return {Boolean} True if selected, else false
39806      */
39807     isSelected : function(node){
39808         return this.selected.contains(this.getNode(node));
39809     },
39810
39811     /**
39812      * Deselects a node.
39813      * @param {HTMLElement/Number} node The node to deselect
39814      */
39815     deselect : function(node){
39816         if(this.isSelected(node)){
39817             node = this.getNode(node);
39818             this.selected.removeElement(node);
39819             if(this.last == node.viewIndex){
39820                 this.last = false;
39821             }
39822             Ext.fly(node).removeClass(this.selectedClass);
39823             this.fireEvent("selectionchange", this, this.selected.elements);
39824         }
39825     },
39826
39827     /**
39828      * Selects a set of nodes.
39829      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node,
39830      * id of a template node or an array of any of those to select
39831      * @param {Boolean} keepExisting (optional) true to keep existing selections
39832      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
39833      */
39834     select : function(nodeInfo, keepExisting, suppressEvent){
39835         if(Ext.isArray(nodeInfo)){
39836             if(!keepExisting){
39837                 this.clearSelections(true);
39838             }
39839             for(var i = 0, len = nodeInfo.length; i < len; i++){
39840                 this.select(nodeInfo[i], true, true);
39841             }
39842             if(!suppressEvent){
39843                 this.fireEvent("selectionchange", this, this.selected.elements);
39844             }
39845         } else{
39846             var node = this.getNode(nodeInfo);
39847             if(!keepExisting){
39848                 this.clearSelections(true);
39849             }
39850             if(node && !this.isSelected(node)){
39851                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
39852                     Ext.fly(node).addClass(this.selectedClass);
39853                     this.selected.add(node);
39854                     this.last = node.viewIndex;
39855                     if(!suppressEvent){
39856                         this.fireEvent("selectionchange", this, this.selected.elements);
39857                     }
39858                 }
39859             }
39860         }
39861     },
39862
39863     /**
39864      * Selects a range of nodes. All nodes between start and end are selected.
39865      * @param {Number} start The index of the first node in the range
39866      * @param {Number} end The index of the last node in the range
39867      * @param {Boolean} keepExisting (optional) True to retain existing selections
39868      */
39869     selectRange : function(start, end, keepExisting){
39870         if(!keepExisting){
39871             this.clearSelections(true);
39872         }
39873         this.select(this.getNodes(start, end), true);
39874     },
39875
39876     /**
39877      * Gets a template node.
39878      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
39879      * @return {HTMLElement} The node or null if it wasn't found
39880      */
39881     getNode : function(nodeInfo){
39882         if(Ext.isString(nodeInfo)){
39883             return document.getElementById(nodeInfo);
39884         }else if(Ext.isNumber(nodeInfo)){
39885             return this.all.elements[nodeInfo];
39886         }
39887         return nodeInfo;
39888     },
39889
39890     /**
39891      * Gets a range nodes.
39892      * @param {Number} start (optional) The index of the first node in the range
39893      * @param {Number} end (optional) The index of the last node in the range
39894      * @return {Array} An array of nodes
39895      */
39896     getNodes : function(start, end){
39897         var ns = this.all.elements;
39898         start = start || 0;
39899         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
39900         var nodes = [], i;
39901         if(start <= end){
39902             for(i = start; i <= end && ns[i]; i++){
39903                 nodes.push(ns[i]);
39904             }
39905         } else{
39906             for(i = start; i >= end && ns[i]; i--){
39907                 nodes.push(ns[i]);
39908             }
39909         }
39910         return nodes;
39911     },
39912
39913     /**
39914      * Finds the index of the passed node.
39915      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
39916      * @return {Number} The index of the node or -1
39917      */
39918     indexOf : function(node){
39919         node = this.getNode(node);
39920         if(Ext.isNumber(node.viewIndex)){
39921             return node.viewIndex;
39922         }
39923         return this.all.indexOf(node);
39924     },
39925
39926     // private
39927     onBeforeLoad : function(){
39928         if(this.loadingText){
39929             this.clearSelections(false, true);
39930             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
39931             this.all.clear();
39932         }
39933     },
39934
39935     onDestroy : function(){
39936         Ext.DataView.superclass.onDestroy.call(this);
39937         this.bindStore(null);
39938     }
39939 });
39940
39941 /**
39942  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
39943  * @param {Store} store The store to bind to this view
39944  */
39945 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
39946
39947 Ext.reg('dataview', Ext.DataView);/**\r
39948  * @class Ext.ListView\r
39949  * @extends Ext.DataView\r
39950  * <p>Ext.ListView is a fast and light-weight implentation of a\r
39951  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>\r
39952  * <div class="mdetail-params"><ul>\r
39953  * <li>resizable columns</li>\r
39954  * <li>selectable</li>\r
39955  * <li>column widths are initially proportioned by percentage based on the container\r
39956  * width and number of columns</li>\r
39957  * <li>uses templates to render the data in any required format</li>\r
39958  * <li>no horizontal scrolling</li>\r
39959  * <li>no editing</li>\r
39960  * </ul></div>\r
39961  * <p>Example usage:</p>\r
39962  * <pre><code>\r
39963 // consume JSON of this form:\r
39964 {\r
39965    "images":[\r
39966       {\r
39967          "name":"dance_fever.jpg",\r
39968          "size":2067,\r
39969          "lastmod":1236974993000,\r
39970          "url":"images\/thumbs\/dance_fever.jpg"\r
39971       },\r
39972       {\r
39973          "name":"zack_sink.jpg",\r
39974          "size":2303,\r
39975          "lastmod":1236974993000,\r
39976          "url":"images\/thumbs\/zack_sink.jpg"\r
39977       }\r
39978    ]\r
39979\r
39980 var store = new Ext.data.JsonStore({\r
39981     url: 'get-images.php',\r
39982     root: 'images',\r
39983     fields: [\r
39984         'name', 'url',\r
39985         {name:'size', type: 'float'},\r
39986         {name:'lastmod', type:'date', dateFormat:'timestamp'}\r
39987     ]\r
39988 });\r
39989 store.load();\r
39990 \r
39991 var listView = new Ext.ListView({\r
39992     store: store,\r
39993     multiSelect: true,\r
39994     emptyText: 'No images to display',\r
39995     reserveScrollOffset: true,\r
39996     columns: [{\r
39997         header: 'File',\r
39998         width: .5,\r
39999         dataIndex: 'name'\r
40000     },{\r
40001         header: 'Last Modified',\r
40002         width: .35, \r
40003         dataIndex: 'lastmod',\r
40004         tpl: '{lastmod:date("m-d h:i a")}'\r
40005     },{\r
40006         header: 'Size',\r
40007         dataIndex: 'size',\r
40008         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()\r
40009         align: 'right'\r
40010     }]\r
40011 });\r
40012 \r
40013 // put it in a Panel so it looks pretty\r
40014 var panel = new Ext.Panel({\r
40015     id:'images-view',\r
40016     width:425,\r
40017     height:250,\r
40018     collapsible:true,\r
40019     layout:'fit',\r
40020     title:'Simple ListView <i>(0 items selected)</i>',\r
40021     items: listView\r
40022 });\r
40023 panel.render(document.body);\r
40024 \r
40025 // little bit of feedback\r
40026 listView.on('selectionchange', function(view, nodes){\r
40027     var l = nodes.length;\r
40028     var s = l != 1 ? 's' : '';\r
40029     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');\r
40030 });\r
40031  * </code></pre>\r
40032  * @constructor\r
40033  * @param {Object} config\r
40034  * @xtype listview\r
40035  */\r
40036 Ext.ListView = Ext.extend(Ext.DataView, {\r
40037     /**\r
40038      * Set this property to <tt>true</tt> to disable the header click handler disabling sort\r
40039      * (defaults to <tt>false</tt>).\r
40040      * @type Boolean\r
40041      * @property disableHeaders\r
40042      */\r
40043     /**\r
40044      * @cfg {Boolean} hideHeaders\r
40045      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so\r
40046      * the {@link #internalTpl header row} will be shown).\r
40047      */\r
40048     /**\r
40049      * @cfg {String} itemSelector\r
40050      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.\r
40051      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)\r
40052      * that will be used to determine what nodes the ListView will be working with.   \r
40053      */\r
40054     itemSelector: 'dl',\r
40055     /**\r
40056      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to\r
40057      * <tt>'x-list-selected'</tt>). An example overriding the default styling:\r
40058     <pre><code>\r
40059     .x-list-selected {background-color: yellow;}\r
40060     </code></pre>\r
40061      * @type String\r
40062      */\r
40063     selectedClass:'x-list-selected',\r
40064     /**\r
40065      * @cfg {String} overClass The CSS class applied when over a row (defaults to\r
40066      * <tt>'x-list-over'</tt>). An example overriding the default styling:\r
40067     <pre><code>\r
40068     .x-list-over {background-color: orange;}\r
40069     </code></pre>\r
40070      * @type String\r
40071      */\r
40072     overClass:'x-list-over',\r
40073     /**\r
40074      * @cfg {Boolean} reserveScrollOffset\r
40075      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>\r
40076      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured\r
40077      * <b><tt>{@link #scrollOffset}</tt></b> immediately.\r
40078      */\r
40079     /**\r
40080      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to\r
40081      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically\r
40082      * calculated.\r
40083      */\r
40084     scrollOffset : undefined,\r
40085     /**\r
40086      * @cfg {Boolean/Object} columnResize\r
40087      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.ColumnResizer}\r
40088      * to enable the columns to be resizable (defaults to <tt>true</tt>).\r
40089      */\r
40090     columnResize: true,\r
40091     /**\r
40092      * @cfg {Array} columns An array of column configuration objects, for example:\r
40093      * <pre><code>\r
40094 {\r
40095     align: 'right',\r
40096     dataIndex: 'size',\r
40097     header: 'Size',\r
40098     tpl: '{size:fileSize}',\r
40099     width: .35\r
40100 }\r
40101      * </code></pre> \r
40102      * Acceptable properties for each column configuration object are:\r
40103      * <div class="mdetail-params"><ul>\r
40104      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property\r
40105      * of the column. Defaults to <tt>'left'</tt>.</div></li>\r
40106      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
40107      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>\r
40108      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
40109      * {@link Ext.grid.Column#header header} for details.</div></li>\r
40110      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the\r
40111      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}\r
40112      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>\r
40113      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width\r
40114      * this column should be allocated.  Columns that have no width specified will be\r
40115      * allocated with an equal percentage to fill 100% of the container width.  To easily take\r
40116      * advantage of the full container width, leave the width of at least one column undefined.\r
40117      * Note that if you do not want to take up the full width of the container, the width of\r
40118      * every column needs to be explicitly defined.</div></li>\r
40119      * </ul></div>\r
40120      */\r
40121     /**\r
40122      * @cfg {Boolean/Object} columnSort\r
40123      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.Sorter}\r
40124      * to enable the columns to be sortable (defaults to <tt>true</tt>).\r
40125      */\r
40126     columnSort: true,\r
40127     /**\r
40128      * @cfg {String/Array} internalTpl\r
40129      * The template to be used for the header row.  See {@link #tpl} for more details.\r
40130      */\r
40131 \r
40132     /*\r
40133      * IE has issues when setting percentage based widths to 100%. Default to 99.\r
40134      */\r
40135     maxWidth: Ext.isIE ? 99 : 100,\r
40136     \r
40137     initComponent : function(){\r
40138         if(this.columnResize){\r
40139             this.colResizer = new Ext.ListView.ColumnResizer(this.colResizer);\r
40140             this.colResizer.init(this);\r
40141         }\r
40142         if(this.columnSort){\r
40143             this.colSorter = new Ext.ListView.Sorter(this.columnSort);\r
40144             this.colSorter.init(this);\r
40145         }\r
40146         if(!this.internalTpl){\r
40147             this.internalTpl = new Ext.XTemplate(\r
40148                 '<div class="x-list-header"><div class="x-list-header-inner">',\r
40149                     '<tpl for="columns">',\r
40150                     '<div style="width:{width}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',\r
40151                         '{header}',\r
40152                     '</em></div>',\r
40153                     '</tpl>',\r
40154                     '<div class="x-clear"></div>',\r
40155                 '</div></div>',\r
40156                 '<div class="x-list-body"><div class="x-list-body-inner">',\r
40157                 '</div></div>'\r
40158             );\r
40159         }\r
40160         if(!this.tpl){\r
40161             this.tpl = new Ext.XTemplate(\r
40162                 '<tpl for="rows">',\r
40163                     '<dl>',\r
40164                         '<tpl for="parent.columns">',\r
40165                         '<dt style="width:{width}%;text-align:{align};"><em unselectable="on">',\r
40166                             '{[values.tpl.apply(parent)]}',\r
40167                         '</em></dt>',\r
40168                         '</tpl>',\r
40169                         '<div class="x-clear"></div>',\r
40170                     '</dl>',\r
40171                 '</tpl>'\r
40172             );\r
40173         };\r
40174         var cs = this.columns, \r
40175             allocatedWidth = 0, \r
40176             colsWithWidth = 0, \r
40177             len = cs.length, \r
40178             columns = [];\r
40179         for(var i = 0; i < len; i++){\r
40180             var c = Ext.apply({}, cs[i]);\r
40181             if(!c.tpl){\r
40182                 c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');\r
40183             }else if(Ext.isString(c.tpl)){\r
40184                 c.tpl = new Ext.XTemplate(c.tpl);\r
40185             }\r
40186             c.align = c.align || 'left';\r
40187             if(Ext.isNumber(c.width)){\r
40188                 c.width *= 100;\r
40189                 allocatedWidth += c.width;\r
40190                 colsWithWidth++;\r
40191             }\r
40192             columns.push(c);\r
40193         }\r
40194         cs = this.columns = columns;\r
40195         // auto calculate missing column widths\r
40196         if(colsWithWidth < len){\r
40197             var remaining = len - colsWithWidth;\r
40198             if(allocatedWidth < this.maxWidth){\r
40199                 var perCol = ((this.maxWidth-allocatedWidth) / remaining);\r
40200                 for(var j = 0; j < len; j++){\r
40201                     var c = cs[j];\r
40202                     if(!Ext.isNumber(c.width)){\r
40203                         c.width = perCol;\r
40204                     }\r
40205                 }\r
40206             }\r
40207         }\r
40208         Ext.ListView.superclass.initComponent.call(this);\r
40209     },\r
40210 \r
40211     onRender : function(){\r
40212         this.autoEl = {\r
40213             cls: 'x-list-wrap'  \r
40214         };\r
40215         Ext.ListView.superclass.onRender.apply(this, arguments);\r
40216 \r
40217         this.internalTpl.overwrite(this.el, {columns: this.columns});\r
40218         \r
40219         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);\r
40220         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);\r
40221 \r
40222         if(this.hideHeaders){\r
40223             this.el.dom.firstChild.style.display = 'none';\r
40224         }\r
40225     },\r
40226 \r
40227     getTemplateTarget : function(){\r
40228         return this.innerBody;\r
40229     },\r
40230 \r
40231     /**\r
40232      * <p>Function which can be overridden which returns the data object passed to this\r
40233      * view's {@link #tpl template} to render the whole ListView. The returned object \r
40234      * shall contain the following properties:</p>\r
40235      * <div class="mdetail-params"><ul>\r
40236      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>\r
40237      * <li><b>rows</b> : String<div class="sub-desc">See\r
40238      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>\r
40239      * </ul></div>\r
40240      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.\r
40241      * @param {Number} startIndex the index number of the Record being prepared for rendering.\r
40242      * @return {Object} A data object containing properties to be processed by a repeating\r
40243      * XTemplate as described above.\r
40244      */\r
40245     collectData : function(){\r
40246         var rs = Ext.ListView.superclass.collectData.apply(this, arguments);\r
40247         return {\r
40248             columns: this.columns,\r
40249             rows: rs\r
40250         }\r
40251     },\r
40252 \r
40253     verifyInternalSize : function(){\r
40254         if(this.lastSize){\r
40255             this.onResize(this.lastSize.width, this.lastSize.height);\r
40256         }\r
40257     },\r
40258 \r
40259     // private\r
40260     onResize : function(w, h){\r
40261         var bd = this.innerBody.dom;\r
40262         var hd = this.innerHd.dom\r
40263         if(!bd){\r
40264             return;\r
40265         }\r
40266         var bdp = bd.parentNode;\r
40267         if(Ext.isNumber(w)){\r
40268             var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());\r
40269             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){\r
40270                 bd.style.width = sw + 'px';\r
40271                 hd.style.width = sw + 'px';\r
40272             }else{\r
40273                 bd.style.width = w + 'px';\r
40274                 hd.style.width = w + 'px';\r
40275                 setTimeout(function(){\r
40276                     if((bdp.offsetWidth - bdp.clientWidth) > 10){\r
40277                         bd.style.width = sw + 'px';\r
40278                         hd.style.width = sw + 'px';\r
40279                     }\r
40280                 }, 10);\r
40281             }\r
40282         }\r
40283         if(Ext.isNumber(h)){\r
40284             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';\r
40285         }\r
40286     },\r
40287 \r
40288     updateIndexes : function(){\r
40289         Ext.ListView.superclass.updateIndexes.apply(this, arguments);\r
40290         this.verifyInternalSize();\r
40291     },\r
40292 \r
40293     findHeaderIndex : function(hd){\r
40294         hd = hd.dom || hd;\r
40295         var pn = hd.parentNode, cs = pn.parentNode.childNodes;\r
40296         for(var i = 0, c; c = cs[i]; i++){\r
40297             if(c == pn){\r
40298                 return i;\r
40299             }\r
40300         }\r
40301         return -1;\r
40302     },\r
40303 \r
40304     setHdWidths : function(){\r
40305         var els = this.innerHd.dom.getElementsByTagName('div');\r
40306         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){\r
40307             els[i].style.width = cs[i].width + '%';\r
40308         }\r
40309     }\r
40310 });\r
40311 \r
40312 Ext.reg('listview', Ext.ListView);/**\r
40313  * @class Ext.ListView.ColumnResizer\r
40314  * @extends Ext.util.Observable\r
40315  * <p>Supporting Class for Ext.ListView.</p>\r
40316  * @constructor\r
40317  * @param {Object} config\r
40318  */\r
40319 Ext.ListView.ColumnResizer = Ext.extend(Ext.util.Observable, {\r
40320     /**\r
40321      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)\r
40322      */\r
40323     minPct: .05,\r
40324 \r
40325     constructor: function(config){\r
40326         Ext.apply(this, config);\r
40327         Ext.ListView.ColumnResizer.superclass.constructor.call(this);\r
40328     },\r
40329     init : function(listView){\r
40330         this.view = listView;\r
40331         listView.on('render', this.initEvents, this);\r
40332     },\r
40333 \r
40334     initEvents : function(view){\r
40335         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);\r
40336         this.tracker = new Ext.dd.DragTracker({\r
40337             onBeforeStart: this.onBeforeStart.createDelegate(this),\r
40338             onStart: this.onStart.createDelegate(this),\r
40339             onDrag: this.onDrag.createDelegate(this),\r
40340             onEnd: this.onEnd.createDelegate(this),\r
40341             tolerance: 3,\r
40342             autoStart: 300\r
40343         });\r
40344         this.tracker.initEl(view.innerHd);\r
40345         view.on('beforedestroy', this.tracker.destroy, this.tracker);\r
40346     },\r
40347 \r
40348     handleHdMove : function(e, t){\r
40349         var hw = 5,\r
40350             x = e.getPageX(),\r
40351             hd = e.getTarget('em', 3, true);\r
40352         if(hd){\r
40353             var r = hd.getRegion(),\r
40354                 ss = hd.dom.style,\r
40355                 pn = hd.dom.parentNode;\r
40356 \r
40357             if(x - r.left <= hw && pn != pn.parentNode.firstChild){\r
40358                 this.activeHd = Ext.get(pn.previousSibling.firstChild);\r
40359                                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';\r
40360             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){\r
40361                 this.activeHd = hd;\r
40362                                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';\r
40363             } else{\r
40364                 delete this.activeHd;\r
40365                 ss.cursor = '';\r
40366             }\r
40367         }\r
40368     },\r
40369 \r
40370     onBeforeStart : function(e){\r
40371         this.dragHd = this.activeHd;\r
40372         return !!this.dragHd;\r
40373     },\r
40374 \r
40375     onStart: function(e){\r
40376         this.view.disableHeaders = true;\r
40377         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});\r
40378         this.proxy.setHeight(this.view.el.getHeight());\r
40379 \r
40380         var x = this.tracker.getXY()[0],\r
40381             w = this.view.innerHd.getWidth();\r
40382 \r
40383         this.hdX = this.dragHd.getX();\r
40384         this.hdIndex = this.view.findHeaderIndex(this.dragHd);\r
40385 \r
40386         this.proxy.setX(this.hdX);\r
40387         this.proxy.setWidth(x-this.hdX);\r
40388 \r
40389         this.minWidth = w*this.minPct;\r
40390         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));\r
40391     },\r
40392 \r
40393     onDrag: function(e){\r
40394         var cursorX = this.tracker.getXY()[0];\r
40395         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));\r
40396     },\r
40397 \r
40398     onEnd: function(e){\r
40399         var nw = this.proxy.getWidth();\r
40400         this.proxy.remove();\r
40401 \r
40402         var index = this.hdIndex,\r
40403             vw = this.view, \r
40404             cs = vw.columns, \r
40405             len = cs.length,\r
40406             w = this.view.innerHd.getWidth(), \r
40407             minPct = this.minPct * 100;\r
40408             pct = Math.ceil((nw * vw.maxWidth) / w),\r
40409             diff = cs[index].width - pct,\r
40410             each = Math.floor(diff / (len-1-index)),\r
40411             mod = diff - (each * (len-1-index));\r
40412 \r
40413         for(var i = index+1; i < len; i++){\r
40414             var cw = cs[i].width + each,\r
40415                 ncw = Math.max(minPct, cw);\r
40416             if(cw != ncw){\r
40417                 mod += cw - ncw;\r
40418             }\r
40419             cs[i].width = ncw;\r
40420         }\r
40421         cs[index].width = pct;\r
40422         cs[index+1].width += mod;\r
40423         delete this.dragHd;\r
40424         vw.setHdWidths();\r
40425         vw.refresh();\r
40426         setTimeout(function(){\r
40427             vw.disableHeaders = false;\r
40428         }, 100);\r
40429     }\r
40430 });/**\r
40431  * @class Ext.ListView.Sorter\r
40432  * @extends Ext.util.Observable\r
40433  * <p>Supporting Class for Ext.ListView.</p>\r
40434  * @constructor\r
40435  * @param {Object} config\r
40436  */\r
40437 Ext.ListView.Sorter = Ext.extend(Ext.util.Observable, {\r
40438     /**\r
40439      * @cfg {Array} sortClasses\r
40440      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)\r
40441      */\r
40442     sortClasses : ["sort-asc", "sort-desc"],\r
40443 \r
40444     constructor: function(config){\r
40445         Ext.apply(this, config);\r
40446         Ext.ListView.Sorter.superclass.constructor.call(this);\r
40447     },\r
40448 \r
40449     init : function(listView){\r
40450         this.view = listView;\r
40451         listView.on('render', this.initEvents, this);\r
40452     },\r
40453 \r
40454     initEvents : function(view){\r
40455         view.mon(view.innerHd, 'click', this.onHdClick, this);\r
40456         view.innerHd.setStyle('cursor', 'pointer');\r
40457         view.mon(view.store, 'datachanged', this.updateSortState, this);\r
40458         this.updateSortState.defer(10, this, [view.store]);\r
40459     },\r
40460 \r
40461     updateSortState : function(store){\r
40462         var state = store.getSortState();\r
40463         if(!state){\r
40464             return;\r
40465         }\r
40466         this.sortState = state;\r
40467         var cs = this.view.columns, sortColumn = -1;\r
40468         for(var i = 0, len = cs.length; i < len; i++){\r
40469             if(cs[i].dataIndex == state.field){\r
40470                 sortColumn = i;\r
40471                 break;\r
40472             }\r
40473         }\r
40474         if(sortColumn != -1){\r
40475             var sortDir = state.direction;\r
40476             this.updateSortIcon(sortColumn, sortDir);\r
40477         }\r
40478     },\r
40479 \r
40480     updateSortIcon : function(col, dir){\r
40481         var sc = this.sortClasses;\r
40482         var hds = this.view.innerHd.select('em').removeClass(sc);\r
40483         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);\r
40484     },\r
40485 \r
40486     onHdClick : function(e){\r
40487         var hd = e.getTarget('em', 3);\r
40488         if(hd && !this.view.disableHeaders){\r
40489             var index = this.view.findHeaderIndex(hd);\r
40490             this.view.store.sort(this.view.columns[index].dataIndex);\r
40491         }\r
40492     }\r
40493 });/**
40494  * @class Ext.TabPanel
40495  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
40496  * for layout purposes, but also have special support for containing child Components
40497  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
40498  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
40499  *
40500  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
40501  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
40502  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
40503  *
40504  * <p><b><u>TabPanel header/footer elements</u></b></p>
40505  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
40506  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
40507  * This means that a TabPanel will not display any configured title, and will not display any
40508  * configured header {@link Ext.Panel#tools tools}.</p>
40509  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
40510  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
40511  *
40512  * <p><b><u>Tab Events</u></b></p>
40513  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
40514  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
40515  * can fire additional events that only exist for tabs and are not available from other Components.
40516  * These events are:</p>
40517  * <div><ul class="mdetail-params">
40518  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
40519  * the active tab.</li>
40520  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
40521  * was the active tab becomes deactivated.</li>
40522  * </ul></div>
40523  * <p><b><u>Creating TabPanels from Code</u></b></p>
40524  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
40525  * <pre><code>
40526 var tabs = new Ext.TabPanel({
40527     renderTo: Ext.getBody(),
40528     activeTab: 0,
40529     items: [{
40530         title: 'Tab 1',
40531         html: 'A simple tab'
40532     },{
40533         title: 'Tab 2',
40534         html: 'Another one'
40535     }]
40536 });
40537 </code></pre>
40538  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
40539  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
40540  * <div><ul class="mdetail-params">
40541  *
40542  * <li>Pre-Structured Markup</li>
40543  * <div class="sub-desc">
40544  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
40545  * from existing markup (See the {@link #autoTabs} example).</p>
40546  * </div>
40547  *
40548  * <li>Un-Structured Markup</li>
40549  * <div class="sub-desc">
40550  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
40551  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
40552  * elements within the page by id regardless of page structure. For example:</p>
40553  * <pre><code>
40554 var tabs = new Ext.TabPanel({
40555     renderTo: 'my-tabs',
40556     activeTab: 0,
40557     items:[
40558         {contentEl:'tab1', title:'Tab 1'},
40559         {contentEl:'tab2', title:'Tab 2'}
40560     ]
40561 });
40562
40563 // Note that the tabs do not have to be nested within the container (although they can be)
40564 &lt;div id="my-tabs">&lt;/div>
40565 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
40566 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
40567 </code></pre>
40568  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
40569  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
40570  * to render all content tabs on page load.
40571  * </div>
40572  *
40573  * </ul></div>
40574  *
40575  * @extends Ext.Panel
40576  * @constructor
40577  * @param {Object} config The configuration options
40578  * @xtype tabpanel
40579  */
40580 Ext.TabPanel = Ext.extend(Ext.Panel,  {
40581     /**
40582      * @cfg {Boolean} layoutOnTabChange
40583      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
40584      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
40585      */
40586     /**
40587      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
40588      * class name applied to the tab strip item representing the child Component, allowing special
40589      * styling to be applied.
40590      */
40591     /**
40592      * @cfg {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on
40593      * browser resize (defaults to true).
40594      */
40595     monitorResize : true,
40596     /**
40597      * @cfg {Boolean} deferredRender
40598      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
40599      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
40600      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
40601      * is rendered. If there is a significant amount of content or a lot of heavy controls being
40602      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
40603      * improve performance.</p>
40604      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
40605      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
40606      * configuration value.</p>
40607      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
40608      * within an unactivated tab will not be available. For example, this means that if the TabPanel
40609      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
40610      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
40611      * available to either {@link Ext.form.BasicForm#getValues getValues} or
40612      * {@link Ext.form.BasicForm#setValues setValues}.</p>
40613      */
40614     deferredRender : true,
40615     /**
40616      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
40617      */
40618     tabWidth : 120,
40619     /**
40620      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
40621      */
40622     minTabWidth : 30,
40623     /**
40624      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
40625      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
40626      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
40627      */
40628     resizeTabs : false,
40629     /**
40630      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
40631      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
40632      */
40633     enableTabScroll : false,
40634     /**
40635      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
40636      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
40637      * applies when <tt>{@link #enableTabScroll} = true</tt>.
40638      */
40639     scrollIncrement : 0,
40640     /**
40641      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
40642      * continuously pressed (defaults to <tt>400</tt>).
40643      */
40644     scrollRepeatInterval : 400,
40645     /**
40646      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
40647      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
40648      */
40649     scrollDuration : 0.35,
40650     /**
40651      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
40652      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
40653      */
40654     animScroll : true,
40655     /**
40656      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
40657      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
40658      * <tt>tabPosition: 'top'</tt>.
40659      */
40660     tabPosition : 'top',
40661     /**
40662      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
40663      */
40664     baseCls : 'x-tab-panel',
40665     /**
40666      * @cfg {Boolean} autoTabs
40667      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
40668      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
40669      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
40670      * method).</p>
40671      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
40672      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
40673      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
40674      * <p>There are a couple of things to note when using this method:<ul>
40675      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
40676      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
40677      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
40678      * components.</li>
40679      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
40680      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
40681      * until each tab is activated and will be visible outside the TabPanel.</li>
40682      * </ul>Example usage:</p>
40683      * <pre><code>
40684 var tabs = new Ext.TabPanel({
40685     applyTo: 'my-tabs',
40686     activeTab: 0,
40687     deferredRender: false,
40688     autoTabs: true
40689 });
40690
40691 // This markup will be converted to a TabPanel from the code above
40692 &lt;div id="my-tabs">
40693     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
40694     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
40695 &lt;/div>
40696 </code></pre>
40697      */
40698     autoTabs : false,
40699     /**
40700      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
40701      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
40702      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
40703      * tab panel only (so that multiple tab panels from markup can be supported on a page).
40704      */
40705     autoTabSelector : 'div.x-tab',
40706     /**
40707      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
40708      * activated on render (defaults to none).
40709      */
40710     activeTab : null,
40711     /**
40712      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
40713      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
40714      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
40715      */
40716     tabMargin : 2,
40717     /**
40718      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
40719      * (defaults to <tt>false</tt>).
40720      */
40721     plain : false,
40722     /**
40723      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
40724      * scrolling (defaults to <tt>20</tt>).
40725      */
40726     wheelIncrement : 20,
40727
40728     /*
40729      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
40730      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
40731      * potentially clash with this one.
40732      */
40733     idDelimiter : '__',
40734
40735     // private
40736     itemCls : 'x-tab-item',
40737
40738     // private config overrides
40739     elements : 'body',
40740     headerAsText : false,
40741     frame : false,
40742     hideBorders :true,
40743
40744     // private
40745     initComponent : function(){
40746         this.frame = false;
40747         Ext.TabPanel.superclass.initComponent.call(this);
40748         this.addEvents(
40749             /**
40750              * @event beforetabchange
40751              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
40752              * @param {TabPanel} this
40753              * @param {Panel} newTab The tab being activated
40754              * @param {Panel} currentTab The current active tab
40755              */
40756             'beforetabchange',
40757             /**
40758              * @event tabchange
40759              * Fires after the active tab has changed.
40760              * @param {TabPanel} this
40761              * @param {Panel} tab The new active tab
40762              */
40763             'tabchange',
40764             /**
40765              * @event contextmenu
40766              * Relays the contextmenu event from a tab selector element in the tab strip.
40767              * @param {TabPanel} this
40768              * @param {Panel} tab The target tab
40769              * @param {EventObject} e
40770              */
40771             'contextmenu'
40772         );
40773         /**
40774          * @cfg {Object} layoutConfig
40775          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
40776          * <code>layoutConfig</code> may be used to configure this layout manager.
40777          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
40778          * configured on the TabPanel will be applied as configs to the layout manager.
40779          */
40780         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
40781             layoutOnCardChange: this.layoutOnTabChange,
40782             deferredRender: this.deferredRender
40783         }, this.layoutConfig)));
40784
40785         if(this.tabPosition == 'top'){
40786             this.elements += ',header';
40787             this.stripTarget = 'header';
40788         }else {
40789             this.elements += ',footer';
40790             this.stripTarget = 'footer';
40791         }
40792         if(!this.stack){
40793             this.stack = Ext.TabPanel.AccessStack();
40794         }
40795         this.initItems();
40796     },
40797
40798     // private
40799     onRender : function(ct, position){
40800         Ext.TabPanel.superclass.onRender.call(this, ct, position);
40801
40802         if(this.plain){
40803             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
40804             this[pos].addClass('x-tab-panel-'+pos+'-plain');
40805         }
40806
40807         var st = this[this.stripTarget];
40808
40809         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
40810             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
40811
40812         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
40813         this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
40814         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
40815
40816         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
40817         this.strip.createChild({cls:'x-clear'});
40818
40819         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
40820
40821         /**
40822          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
40823          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
40824          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
40825          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
40826          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
40827          * native <tt>{@link #getTemplateArgs}</tt>.</p>
40828          * <p>The child element which contains the title text must be marked by the CSS class
40829          * <tt>x-tab-strip-inner</tt>.</p>
40830          * <p>To enable closability, the created element should contain an element marked by the CSS class
40831          * <tt>x-tab-strip-close</tt>.</p>
40832          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
40833          * style rules to create the desired appearance.</p>
40834          * Below is an example of how to create customized tab selector items:<pre><code>
40835 new Ext.TabPanel({
40836     renderTo: document.body,
40837     minTabWidth: 115,
40838     tabWidth: 135,
40839     enableTabScroll: true,
40840     width: 600,
40841     height: 250,
40842     defaults: {autoScroll:true},
40843     itemTpl: new Ext.XTemplate(
40844     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
40845          '&lt;tpl if="closable">',
40846             '&lt;a class="x-tab-strip-close" onclick="return false;">&lt;/a>',
40847          '&lt;/tpl>',
40848          '&lt;a class="x-tab-right" href="#" onclick="return false;" style="padding-left:6px">',
40849             '&lt;em class="x-tab-left">',
40850                 '&lt;span class="x-tab-strip-inner">',
40851                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
40852                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
40853                 '&lt;/span>',
40854             '&lt;/em>',
40855         '&lt;/a>',
40856     '&lt;/li>'
40857     ),
40858     getTemplateArgs: function(item) {
40859 //      Call the native method to collect the base data. Like the ID!
40860         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
40861
40862 //      Add stuff used in our template
40863         return Ext.apply(result, {
40864             closable: item.closable,
40865             src: item.iconSrc,
40866             extra: item.extraText || ''
40867         });
40868     },
40869     items: [{
40870         title: 'New Tab 1',
40871         iconSrc: '../shared/icons/fam/grid.png',
40872         html: 'Tab Body 1',
40873         closable: true
40874     }, {
40875         title: 'New Tab 2',
40876         iconSrc: '../shared/icons/fam/grid.png',
40877         html: 'Tab Body 2',
40878         extraText: 'Extra stuff in the tab button'
40879     }]
40880 });
40881 </code></pre>
40882          */
40883         if(!this.itemTpl){
40884             var tt = new Ext.Template(
40885                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close" onclick="return false;"></a>',
40886                  '<a class="x-tab-right" href="#" onclick="return false;"><em class="x-tab-left">',
40887                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
40888                  '</em></a></li>'
40889             );
40890             tt.disableFormats = true;
40891             tt.compile();
40892             Ext.TabPanel.prototype.itemTpl = tt;
40893         }
40894
40895         this.items.each(this.initTab, this);
40896     },
40897
40898     // private
40899     afterRender : function(){
40900         Ext.TabPanel.superclass.afterRender.call(this);
40901         if(this.autoTabs){
40902             this.readTabs(false);
40903         }
40904         if(this.activeTab !== undefined){
40905             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
40906             delete this.activeTab;
40907             this.setActiveTab(item);
40908         }
40909     },
40910
40911     // private
40912     initEvents : function(){
40913         Ext.TabPanel.superclass.initEvents.call(this);
40914         this.mon(this.strip, {
40915             scope: this,
40916             mousedown: this.onStripMouseDown,
40917             contextmenu: this.onStripContextMenu
40918         });
40919         if(this.enableTabScroll){
40920             this.mon(this.strip, 'mousewheel', this.onWheel, this);
40921         }
40922     },
40923
40924     // private
40925     findTargets : function(e){
40926         var item = null;
40927         var itemEl = e.getTarget('li', this.strip);
40928         if(itemEl){
40929             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
40930             if(item.disabled){
40931                 return {
40932                     close : null,
40933                     item : null,
40934                     el : null
40935                 };
40936             }
40937         }
40938         return {
40939             close : e.getTarget('.x-tab-strip-close', this.strip),
40940             item : item,
40941             el : itemEl
40942         };
40943     },
40944
40945     // private
40946     onStripMouseDown : function(e){
40947         if(e.button !== 0){
40948             return;
40949         }
40950         e.preventDefault();
40951         var t = this.findTargets(e);
40952         if(t.close){
40953             if (t.item.fireEvent('beforeclose', t.item) !== false) {
40954                 t.item.fireEvent('close', t.item);
40955                 delete t.item.tabEl;
40956                 this.remove(t.item);
40957             }
40958             return;
40959         }
40960         if(t.item && t.item != this.activeTab){
40961             this.setActiveTab(t.item);
40962         }
40963     },
40964
40965     // private
40966     onStripContextMenu : function(e){
40967         e.preventDefault();
40968         var t = this.findTargets(e);
40969         if(t.item){
40970             this.fireEvent('contextmenu', this, t.item, e);
40971         }
40972     },
40973
40974     /**
40975      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
40976      * <tt>{@link #autoTabSelector}</tt>
40977      * @param {Boolean} removeExisting True to remove existing tabs
40978      */
40979     readTabs : function(removeExisting){
40980         if(removeExisting === true){
40981             this.items.each(function(item){
40982                 this.remove(item);
40983             }, this);
40984         }
40985         var tabs = this.el.query(this.autoTabSelector);
40986         for(var i = 0, len = tabs.length; i < len; i++){
40987             var tab = tabs[i];
40988             var title = tab.getAttribute('title');
40989             tab.removeAttribute('title');
40990             this.add({
40991                 title: title,
40992                 contentEl: tab
40993             });
40994         }
40995     },
40996
40997     // private
40998     initTab : function(item, index){
40999         var before = this.strip.dom.childNodes[index];
41000         var p = this.getTemplateArgs(item);
41001         var el = before ?
41002                  this.itemTpl.insertBefore(before, p) :
41003                  this.itemTpl.append(this.strip, p);
41004
41005         Ext.fly(el).addClassOnOver('x-tab-strip-over');
41006
41007         if(item.tabTip){
41008             Ext.fly(el).child('span.x-tab-strip-text', true).qtip = item.tabTip;
41009         }
41010         item.tabEl = el;
41011
41012         item.on('disable', this.onItemDisabled, this);
41013         item.on('enable', this.onItemEnabled, this);
41014         item.on('titlechange', this.onItemTitleChanged, this);
41015         item.on('iconchange', this.onItemIconChanged, this);
41016         item.on('beforeshow', this.onBeforeShowItem, this);
41017     },
41018
41019     /**
41020      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
41021      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
41022      * to create a formatted, clickable tab selector element. The properties which must be returned
41023      * are:</p><div class="mdetail-params"><ul>
41024      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
41025      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
41026      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
41027      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
41028      * </ul></div>
41029      * @param {BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
41030      * @return {Object} An object hash containing the properties required to render the selector element.
41031      */
41032     getTemplateArgs : function(item) {
41033         var cls = item.closable ? 'x-tab-strip-closable' : '';
41034         if(item.disabled){
41035             cls += ' x-item-disabled';
41036         }
41037         if(item.iconCls){
41038             cls += ' x-tab-with-icon';
41039         }
41040         if(item.tabCls){
41041             cls += ' ' + item.tabCls;
41042         }
41043
41044         return {
41045             id: this.id + this.idDelimiter + item.getItemId(),
41046             text: item.title,
41047             cls: cls,
41048             iconCls: item.iconCls || ''
41049         };
41050     },
41051
41052     // private
41053     onAdd : function(c){
41054         Ext.TabPanel.superclass.onAdd.call(this, c);
41055         if(this.rendered){
41056             var items = this.items;
41057             this.initTab(c, items.indexOf(c));
41058             if(items.getCount() == 1){
41059                 this.syncSize();
41060             }
41061             this.delegateUpdates();
41062         }
41063     },
41064
41065     // private
41066     onBeforeAdd : function(item){
41067         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
41068         if(existing){
41069             this.setActiveTab(item);
41070             return false;
41071         }
41072         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
41073         var es = item.elements;
41074         item.elements = es ? es.replace(',header', '') : es;
41075         item.border = (item.border === true);
41076     },
41077
41078     // private
41079     onRemove : function(c){
41080         Ext.TabPanel.superclass.onRemove.call(this, c);
41081         Ext.destroy(Ext.get(this.getTabEl(c)));
41082         this.stack.remove(c);
41083         c.un('disable', this.onItemDisabled, this);
41084         c.un('enable', this.onItemEnabled, this);
41085         c.un('titlechange', this.onItemTitleChanged, this);
41086         c.un('iconchange', this.onItemIconChanged, this);
41087         c.un('beforeshow', this.onBeforeShowItem, this);
41088         if(c == this.activeTab){
41089             var next = this.stack.next();
41090             if(next){
41091                 this.setActiveTab(next);
41092             }else if(this.items.getCount() > 0){
41093                 this.setActiveTab(0);
41094             }else{
41095                 this.activeTab = null;
41096             }
41097         }
41098         this.delegateUpdates();
41099     },
41100
41101     // private
41102     onBeforeShowItem : function(item){
41103         if(item != this.activeTab){
41104             this.setActiveTab(item);
41105             return false;
41106         }
41107     },
41108
41109     // private
41110     onItemDisabled : function(item){
41111         var el = this.getTabEl(item);
41112         if(el){
41113             Ext.fly(el).addClass('x-item-disabled');
41114         }
41115         this.stack.remove(item);
41116     },
41117
41118     // private
41119     onItemEnabled : function(item){
41120         var el = this.getTabEl(item);
41121         if(el){
41122             Ext.fly(el).removeClass('x-item-disabled');
41123         }
41124     },
41125
41126     // private
41127     onItemTitleChanged : function(item){
41128         var el = this.getTabEl(item);
41129         if(el){
41130             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
41131         }
41132     },
41133
41134     //private
41135     onItemIconChanged : function(item, iconCls, oldCls){
41136         var el = this.getTabEl(item);
41137         if(el){
41138             el = Ext.get(el);
41139             el.child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
41140             el[Ext.isEmpty(iconCls) ? 'removeClass' : 'addClass']('x-tab-with-icon');
41141         }
41142     },
41143
41144     /**
41145      * Gets the DOM element for the tab strip item which activates the child panel with the specified
41146      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
41147      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
41148      * @return {HTMLElement} The DOM node
41149      */
41150     getTabEl : function(item){
41151         return document.getElementById(this.id + this.idDelimiter + this.getComponent(item).getItemId());
41152     },
41153
41154     // private
41155     onResize : function(){
41156         Ext.TabPanel.superclass.onResize.apply(this, arguments);
41157         this.delegateUpdates();
41158     },
41159
41160     /**
41161      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
41162      */
41163     beginUpdate : function(){
41164         this.suspendUpdates = true;
41165     },
41166
41167     /**
41168      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
41169      */
41170     endUpdate : function(){
41171         this.suspendUpdates = false;
41172         this.delegateUpdates();
41173     },
41174
41175     /**
41176      * Hides the tab strip item for the passed tab
41177      * @param {Number/String/Panel} item The tab index, id or item
41178      */
41179     hideTabStripItem : function(item){
41180         item = this.getComponent(item);
41181         var el = this.getTabEl(item);
41182         if(el){
41183             el.style.display = 'none';
41184             this.delegateUpdates();
41185         }
41186         this.stack.remove(item);
41187     },
41188
41189     /**
41190      * Unhides the tab strip item for the passed tab
41191      * @param {Number/String/Panel} item The tab index, id or item
41192      */
41193     unhideTabStripItem : function(item){
41194         item = this.getComponent(item);
41195         var el = this.getTabEl(item);
41196         if(el){
41197             el.style.display = '';
41198             this.delegateUpdates();
41199         }
41200     },
41201
41202     // private
41203     delegateUpdates : function(){
41204         if(this.suspendUpdates){
41205             return;
41206         }
41207         if(this.resizeTabs && this.rendered){
41208             this.autoSizeTabs();
41209         }
41210         if(this.enableTabScroll && this.rendered){
41211             this.autoScrollTabs();
41212         }
41213     },
41214
41215     // private
41216     autoSizeTabs : function(){
41217         var count = this.items.length;
41218         var ce = this.tabPosition != 'bottom' ? 'header' : 'footer';
41219         var ow = this[ce].dom.offsetWidth;
41220         var aw = this[ce].dom.clientWidth;
41221
41222         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
41223             return;
41224         }
41225
41226         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
41227         this.lastTabWidth = each;
41228         var lis = this.strip.query("li:not([className^=x-tab-edge])");
41229         for(var i = 0, len = lis.length; i < len; i++) {
41230             var li = lis[i];
41231             var inner = Ext.fly(li).child('.x-tab-strip-inner', true);
41232             var tw = li.offsetWidth;
41233             var iw = inner.offsetWidth;
41234             inner.style.width = (each - (tw-iw)) + 'px';
41235         }
41236     },
41237
41238     // private
41239     adjustBodyWidth : function(w){
41240         if(this.header){
41241             this.header.setWidth(w);
41242         }
41243         if(this.footer){
41244             this.footer.setWidth(w);
41245         }
41246         return w;
41247     },
41248
41249     /**
41250      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
41251      * can <tt>return false</tt> to cancel the tab change.
41252      * @param {String/Number} item
41253      * The id or tab Panel to activate. This parameter may be any of the following:
41254      * <div><ul class="mdetail-params">
41255      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
41256      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
41257      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
41258      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
41259      * </ul></div>
41260      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
41261      */
41262     setActiveTab : function(item){
41263         item = this.getComponent(item);
41264         if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
41265             return;
41266         }
41267         if(!this.rendered){
41268             this.activeTab = item;
41269             return;
41270         }
41271         if(this.activeTab != item){
41272             if(this.activeTab){
41273                 var oldEl = this.getTabEl(this.activeTab);
41274                 if(oldEl){
41275                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
41276                 }
41277                 this.activeTab.fireEvent('deactivate', this.activeTab);
41278             }
41279             var el = this.getTabEl(item);
41280             Ext.fly(el).addClass('x-tab-strip-active');
41281             this.activeTab = item;
41282             this.stack.add(item);
41283
41284             this.layout.setActiveItem(item);
41285             if(this.scrolling){
41286                 this.scrollToTab(item, this.animScroll);
41287             }
41288
41289             item.fireEvent('activate', item);
41290             this.fireEvent('tabchange', this, item);
41291         }
41292     },
41293
41294     /**
41295      * Gets the currently active tab.
41296      * @return {Panel} The active tab
41297      */
41298     getActiveTab : function(){
41299         return this.activeTab || null;
41300     },
41301
41302     /**
41303      * Gets the specified tab by id.
41304      * @param {String} id The tab id
41305      * @return {Panel} The tab
41306      */
41307     getItem : function(item){
41308         return this.getComponent(item);
41309     },
41310
41311     // private
41312     autoScrollTabs : function(){
41313         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
41314         var count = this.items.length;
41315         var ow = this.pos.dom.offsetWidth;
41316         var tw = this.pos.dom.clientWidth;
41317
41318         var wrap = this.stripWrap;
41319         var wd = wrap.dom;
41320         var cw = wd.offsetWidth;
41321         var pos = this.getScrollPos();
41322         var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
41323
41324         if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
41325             return;
41326         }
41327         if(l <= tw){
41328             wd.scrollLeft = 0;
41329             wrap.setWidth(tw);
41330             if(this.scrolling){
41331                 this.scrolling = false;
41332                 this.pos.removeClass('x-tab-scrolling');
41333                 this.scrollLeft.hide();
41334                 this.scrollRight.hide();
41335                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
41336                 if(Ext.isAir || Ext.isWebKit){
41337                     wd.style.marginLeft = '';
41338                     wd.style.marginRight = '';
41339                 }
41340             }
41341         }else{
41342             if(!this.scrolling){
41343                 this.pos.addClass('x-tab-scrolling');
41344                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
41345                 if(Ext.isAir || Ext.isWebKit){
41346                     wd.style.marginLeft = '18px';
41347                     wd.style.marginRight = '18px';
41348                 }
41349             }
41350             tw -= wrap.getMargins('lr');
41351             wrap.setWidth(tw > 20 ? tw : 20);
41352             if(!this.scrolling){
41353                 if(!this.scrollLeft){
41354                     this.createScrollers();
41355                 }else{
41356                     this.scrollLeft.show();
41357                     this.scrollRight.show();
41358                 }
41359             }
41360             this.scrolling = true;
41361             if(pos > (l-tw)){ // ensure it stays within bounds
41362                 wd.scrollLeft = l-tw;
41363             }else{ // otherwise, make sure the active tab is still visible
41364                 this.scrollToTab(this.activeTab, false);
41365             }
41366             this.updateScrollButtons();
41367         }
41368     },
41369
41370     // private
41371     createScrollers : function(){
41372         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
41373         var h = this.stripWrap.dom.offsetHeight;
41374
41375         // left
41376         var sl = this.pos.insertFirst({
41377             cls:'x-tab-scroller-left'
41378         });
41379         sl.setHeight(h);
41380         sl.addClassOnOver('x-tab-scroller-left-over');
41381         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
41382             interval : this.scrollRepeatInterval,
41383             handler: this.onScrollLeft,
41384             scope: this
41385         });
41386         this.scrollLeft = sl;
41387
41388         // right
41389         var sr = this.pos.insertFirst({
41390             cls:'x-tab-scroller-right'
41391         });
41392         sr.setHeight(h);
41393         sr.addClassOnOver('x-tab-scroller-right-over');
41394         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
41395             interval : this.scrollRepeatInterval,
41396             handler: this.onScrollRight,
41397             scope: this
41398         });
41399         this.scrollRight = sr;
41400     },
41401
41402     // private
41403     getScrollWidth : function(){
41404         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
41405     },
41406
41407     // private
41408     getScrollPos : function(){
41409         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
41410     },
41411
41412     // private
41413     getScrollArea : function(){
41414         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
41415     },
41416
41417     // private
41418     getScrollAnim : function(){
41419         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
41420     },
41421
41422     // private
41423     getScrollIncrement : function(){
41424         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
41425     },
41426
41427     /**
41428      * Scrolls to a particular tab if tab scrolling is enabled
41429      * @param {Panel} item The item to scroll to
41430      * @param {Boolean} animate True to enable animations
41431      */
41432
41433     scrollToTab : function(item, animate){
41434         if(!item){ return; }
41435         var el = this.getTabEl(item);
41436         var pos = this.getScrollPos(), area = this.getScrollArea();
41437         var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
41438         var right = left + el.offsetWidth;
41439         if(left < pos){
41440             this.scrollTo(left, animate);
41441         }else if(right > (pos + area)){
41442             this.scrollTo(right - area, animate);
41443         }
41444     },
41445
41446     // private
41447     scrollTo : function(pos, animate){
41448         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
41449         if(!animate){
41450             this.updateScrollButtons();
41451         }
41452     },
41453
41454     onWheel : function(e){
41455         var d = e.getWheelDelta()*this.wheelIncrement*-1;
41456         e.stopEvent();
41457
41458         var pos = this.getScrollPos();
41459         var newpos = pos + d;
41460         var sw = this.getScrollWidth()-this.getScrollArea();
41461
41462         var s = Math.max(0, Math.min(sw, newpos));
41463         if(s != pos){
41464             this.scrollTo(s, false);
41465         }
41466     },
41467
41468     // private
41469     onScrollRight : function(){
41470         var sw = this.getScrollWidth()-this.getScrollArea();
41471         var pos = this.getScrollPos();
41472         var s = Math.min(sw, pos + this.getScrollIncrement());
41473         if(s != pos){
41474             this.scrollTo(s, this.animScroll);
41475         }
41476     },
41477
41478     // private
41479     onScrollLeft : function(){
41480         var pos = this.getScrollPos();
41481         var s = Math.max(0, pos - this.getScrollIncrement());
41482         if(s != pos){
41483             this.scrollTo(s, this.animScroll);
41484         }
41485     },
41486
41487     // private
41488     updateScrollButtons : function(){
41489         var pos = this.getScrollPos();
41490         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
41491         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
41492     },
41493
41494     // private
41495     beforeDestroy : function() {
41496         if(this.items){
41497             this.items.each(function(item){
41498                 if(item && item.tabEl){
41499                     Ext.get(item.tabEl).removeAllListeners();
41500                     item.tabEl = null;
41501                 }
41502             }, this);
41503         }
41504         if(this.strip){
41505             this.strip.removeAllListeners();
41506         }
41507         Ext.TabPanel.superclass.beforeDestroy.apply(this);
41508     }
41509
41510     /**
41511      * @cfg {Boolean} collapsible
41512      * @hide
41513      */
41514     /**
41515      * @cfg {String} header
41516      * @hide
41517      */
41518     /**
41519      * @cfg {Boolean} headerAsText
41520      * @hide
41521      */
41522     /**
41523      * @property header
41524      * @hide
41525      */
41526     /**
41527      * @cfg title
41528      * @hide
41529      */
41530     /**
41531      * @cfg {Array} tools
41532      * @hide
41533      */
41534     /**
41535      * @cfg {Array} toolTemplate
41536      * @hide
41537      */
41538     /**
41539      * @cfg {Boolean} hideCollapseTool
41540      * @hide
41541      */
41542     /**
41543      * @cfg {Boolean} titleCollapse
41544      * @hide
41545      */
41546     /**
41547      * @cfg {Boolean} collapsed
41548      * @hide
41549      */
41550     /**
41551      * @cfg {String} layout
41552      * @hide
41553      */
41554     /**
41555      * @cfg {Boolean} preventBodyReset
41556      * @hide
41557      */
41558 });
41559 Ext.reg('tabpanel', Ext.TabPanel);
41560
41561 /**
41562  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
41563  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
41564  * @param {String/Panel} tab The id or tab Panel to activate
41565  * @method activate
41566  */
41567 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
41568
41569 // private utility class used by TabPanel
41570 Ext.TabPanel.AccessStack = function(){
41571     var items = [];
41572     return {
41573         add : function(item){
41574             items.push(item);
41575             if(items.length > 10){
41576                 items.shift();
41577             }
41578         },
41579
41580         remove : function(item){
41581             var s = [];
41582             for(var i = 0, len = items.length; i < len; i++) {
41583                 if(items[i] != item){
41584                     s.push(items[i]);
41585                 }
41586             }
41587             items = s;
41588         },
41589
41590         next : function(){
41591             return items.pop();
41592         }
41593     };
41594 };
41595 /**
41596  * @class Ext.Button
41597  * @extends Ext.BoxComponent
41598  * Simple Button class
41599  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
41600  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
41601  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
41602  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
41603  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
41604  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
41605  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
41606  * </ul></div>
41607  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
41608  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
41609  * @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
41610  * @cfg {Boolean} hidden True to start hidden (defaults to false)
41611  * @cfg {Boolean} disabled True to start disabled (defaults to false)
41612  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
41613  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
41614  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
41615  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
41616  * @constructor
41617  * Create a new button
41618  * @param {Object} config The config object
41619  * @xtype button
41620  */
41621 Ext.Button = Ext.extend(Ext.BoxComponent, {
41622     /**
41623      * Read-only. True if this button is hidden
41624      * @type Boolean
41625      */
41626     hidden : false,
41627     /**
41628      * Read-only. True if this button is disabled
41629      * @type Boolean
41630      */
41631     disabled : false,
41632     /**
41633      * Read-only. True if this button is pressed (only if enableToggle = true)
41634      * @type Boolean
41635      */
41636     pressed : false,
41637
41638     /**
41639      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
41640      */
41641
41642     /**
41643      * @cfg {Boolean} allowDepress
41644      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
41645      */
41646
41647     /**
41648      * @cfg {Boolean} enableToggle
41649      * True to enable pressed/not pressed toggling (defaults to false)
41650      */
41651     enableToggle : false,
41652     /**
41653      * @cfg {Function} toggleHandler
41654      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
41655      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
41656      * <li><b>state</b> : Boolean<div class="sub-desc">The next state if the Button, true means pressed.</div></li>
41657      * </ul>
41658      */
41659     /**
41660      * @cfg {Mixed} menu
41661      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
41662      */
41663     /**
41664      * @cfg {String} menuAlign
41665      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
41666      */
41667     menuAlign : 'tl-bl?',
41668
41669     /**
41670      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
41671      * text to be used if this item is shown in the overflow menu. See also
41672      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
41673      */
41674     /**
41675      * @cfg {String} iconCls
41676      * A css class which sets a background image to be used as the icon for this button
41677      */
41678     /**
41679      * @cfg {String} type
41680      * submit, reset or button - defaults to 'button'
41681      */
41682     type : 'button',
41683
41684     // private
41685     menuClassTarget : 'tr:nth(2)',
41686
41687     /**
41688      * @cfg {String} clickEvent
41689      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu). 
41690      * Defaults to <tt>'click'</tt>.
41691      */
41692     clickEvent : 'click',
41693
41694     /**
41695      * @cfg {Boolean} handleMouseEvents
41696      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
41697      */
41698     handleMouseEvents : true,
41699
41700     /**
41701      * @cfg {String} tooltipType
41702      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
41703      */
41704     tooltipType : 'qtip',
41705
41706     /**
41707      * @cfg {String} buttonSelector
41708      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
41709      * DOM structure created.</p>
41710      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
41711      * a focussable element.</p>
41712      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
41713      */
41714     buttonSelector : 'button:first-child',
41715
41716     /**
41717      * @cfg {String} scale
41718      * <p>(Optional) The size of the Button. Three values are allowed:</p>
41719      * <ul class="mdetail-params">
41720      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
41721      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
41722      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
41723      * </ul>
41724      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
41725      */
41726     scale : 'small',
41727
41728     /**
41729      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
41730      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
41731      * executed. Defaults to this Button.
41732      */
41733
41734     /**
41735      * @cfg {String} iconAlign
41736      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
41737      * <ul class="mdetail-params">
41738      * <li>'top'<div class="sub-desc"></div></li>
41739      * <li>'right'<div class="sub-desc"></div></li>
41740      * <li>'bottom'<div class="sub-desc"></div></li>
41741      * <li>'left'<div class="sub-desc"></div></li>
41742      * </ul>
41743      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
41744      */
41745     iconAlign : 'left',
41746
41747     /**
41748      * @cfg {String} arrowAlign
41749      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
41750      * Two values are allowed:</p>
41751      * <ul class="mdetail-params">
41752      * <li>'right'<div class="sub-desc"></div></li>
41753      * <li>'bottom'<div class="sub-desc"></div></li>
41754      * </ul>
41755      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
41756      */
41757     arrowAlign : 'right',
41758
41759     /**
41760      * @cfg {Ext.Template} template (Optional)
41761      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
41762      * Instances, or subclasses which need a different DOM structure may provide a different
41763      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
41764      * @type Ext.Template
41765      * @property template
41766      */
41767     /**
41768      * @cfg {String} cls
41769      * A CSS class string to apply to the button's main element.
41770      */
41771     /**
41772      * @property menu
41773      * @type Menu
41774      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
41775      */
41776
41777     initComponent : function(){
41778         Ext.Button.superclass.initComponent.call(this);
41779
41780         this.addEvents(
41781             /**
41782              * @event click
41783              * Fires when this button is clicked
41784              * @param {Button} this
41785              * @param {EventObject} e The click event
41786              */
41787             'click',
41788             /**
41789              * @event toggle
41790              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
41791              * @param {Button} this
41792              * @param {Boolean} pressed
41793              */
41794             'toggle',
41795             /**
41796              * @event mouseover
41797              * Fires when the mouse hovers over the button
41798              * @param {Button} this
41799              * @param {Event} e The event object
41800              */
41801             'mouseover',
41802             /**
41803              * @event mouseout
41804              * Fires when the mouse exits the button
41805              * @param {Button} this
41806              * @param {Event} e The event object
41807              */
41808             'mouseout',
41809             /**
41810              * @event menushow
41811              * If this button has a menu, this event fires when it is shown
41812              * @param {Button} this
41813              * @param {Menu} menu
41814              */
41815             'menushow',
41816             /**
41817              * @event menuhide
41818              * If this button has a menu, this event fires when it is hidden
41819              * @param {Button} this
41820              * @param {Menu} menu
41821              */
41822             'menuhide',
41823             /**
41824              * @event menutriggerover
41825              * If this button has a menu, this event fires when the mouse enters the menu triggering element
41826              * @param {Button} this
41827              * @param {Menu} menu
41828              * @param {EventObject} e
41829              */
41830             'menutriggerover',
41831             /**
41832              * @event menutriggerout
41833              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
41834              * @param {Button} this
41835              * @param {Menu} menu
41836              * @param {EventObject} e
41837              */
41838             'menutriggerout'
41839         );
41840         if(this.menu){
41841             this.menu = Ext.menu.MenuMgr.get(this.menu);
41842         }
41843         if(Ext.isString(this.toggleGroup)){
41844             this.enableToggle = true;
41845         }
41846     },
41847
41848 /**
41849   * <p>This method returns an object which provides substitution parameters for the {@link #template Template} used
41850   * to create this Button's DOM structure.</p>
41851   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
41852   * own implementation of this method.</p>
41853   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
41854   * following items:</p><div class="mdetail-params"><ul>
41855   * <li>The Button's {@link #text}</li>
41856   * <li>The &lt;button&gt;'s {@link #type}</li>
41857   * <li>The {@link iconCls} applied to the &lt;button&gt; {@link #btnEl element}</li>
41858   * <li>The {@link #cls} applied to the Button's main {@link #getEl Element}</li>
41859   * <li>A CSS class name controlling the Button's {@link #scale} and {@link #iconAlign icon alignment}</li>
41860   * <li>A CSS class name which applies an arrow to the Button if configured with a {@link #menu}</li>
41861   * </ul></div>
41862   * @return {Object} Substitution data for a Template.
41863  */
41864     getTemplateArgs : function(){
41865         var cls = (this.cls || '');
41866         cls += (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';
41867         if(this.pressed){
41868             cls += ' x-btn-pressed';
41869         }
41870         return [this.text || '&#160;', this.type, this.iconCls || '', cls, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass()];
41871     },
41872
41873     // protected
41874     getMenuClass : function(){
41875         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
41876     },
41877
41878     // private
41879     onRender : function(ct, position){
41880         if(!this.template){
41881             if(!Ext.Button.buttonTemplate){
41882                 // hideous table template
41883                 Ext.Button.buttonTemplate = new Ext.Template(
41884                     '<table cellspacing="0" class="x-btn {3}"><tbody class="{4}">',
41885                     '<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>',
41886                     '<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>',
41887                     '<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>',
41888                     "</tbody></table>");
41889                 Ext.Button.buttonTemplate.compile();
41890             }
41891             this.template = Ext.Button.buttonTemplate;
41892         }
41893
41894         var btn, targs = this.getTemplateArgs();
41895
41896         if(position){
41897             btn = this.template.insertBefore(position, targs, true);
41898         }else{
41899             btn = this.template.append(ct, targs, true);
41900         }
41901         /**
41902          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
41903          * this references a <tt>&lt;button&gt;</tt> element. Read only.
41904          * @type Ext.Element
41905          * @property btnEl
41906          */
41907         this.btnEl = btn.child(this.buttonSelector);
41908         this.mon(this.btnEl, {
41909             scope: this,
41910             focus: this.onFocus,
41911             blur: this.onBlur
41912         });
41913
41914         this.initButtonEl(btn, this.btnEl);
41915
41916         Ext.ButtonToggleMgr.register(this);
41917     },
41918
41919     // private
41920     initButtonEl : function(btn, btnEl){
41921         this.el = btn;
41922
41923         if(this.id){
41924             var d = this.el.dom,
41925                 c = Ext.Element.cache;
41926                 
41927             delete c[d.id];
41928             d.id = this.el.id = this.id;
41929             c[d.id] = this.el;
41930         }
41931         if(this.icon){
41932             btnEl.setStyle('background-image', 'url(' +this.icon +')');
41933         }
41934         if(this.tabIndex !== undefined){
41935             btnEl.dom.tabIndex = this.tabIndex;
41936         }
41937         if(this.tooltip){
41938             this.setTooltip(this.tooltip, true);
41939         }
41940
41941         if(this.handleMouseEvents){
41942             this.mon(btn, {
41943                 scope: this,
41944                 mouseover: this.onMouseOver,
41945                 mousedown: this.onMouseDown
41946             });
41947
41948             // new functionality for monitoring on the document level
41949             //this.mon(btn, 'mouseout', this.onMouseOut, this);
41950         }
41951
41952         if(this.menu){
41953             this.mon(this.menu, {
41954                 scope: this,
41955                 show: this.onMenuShow,
41956                 hide: this.onMenuHide
41957             });
41958         }
41959
41960         if(this.repeat){
41961             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
41962             this.mon(repeater, 'click', this.onClick, this);
41963         }
41964         this.mon(btn, this.clickEvent, this.onClick, this);
41965     },
41966
41967     // private
41968     afterRender : function(){
41969         Ext.Button.superclass.afterRender.call(this);
41970         this.doAutoWidth();
41971     },
41972
41973     /**
41974      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
41975      * the value of the {@link iconCls} config internally.
41976      * @param {String} cls The CSS class providing the icon image
41977      * @return {Ext.Button} this
41978      */
41979     setIconClass : function(cls){
41980         if(this.el){
41981             this.btnEl.replaceClass(this.iconCls, cls);
41982         }
41983         this.iconCls = cls;
41984         return this;
41985     },
41986
41987     /**
41988      * Sets the tooltip for this Button.
41989      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
41990      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
41991      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
41992      * </ul></div>
41993      * @return {Ext.Button} this
41994      */
41995     setTooltip : function(tooltip, /* private */ initial){
41996         if(this.rendered){
41997             if(!initial){
41998                 this.clearTip();
41999             }
42000             if(Ext.isObject(tooltip)){
42001                 Ext.QuickTips.register(Ext.apply({
42002                       target: this.btnEl.id
42003                 }, tooltip));
42004                 this.tooltip = tooltip;
42005             }else{
42006                 this.btnEl.dom[this.tooltipType] = tooltip;
42007             }
42008         }else{
42009             this.tooltip = tooltip;
42010         }
42011         return this;
42012     },
42013
42014     // private
42015     clearTip : function(){
42016         if(Ext.isObject(this.tooltip)){
42017             Ext.QuickTips.unregister(this.btnEl);
42018         }
42019     },
42020
42021     // private
42022     beforeDestroy : function(){
42023         if(this.rendered){
42024             this.clearTip();
42025         }
42026         Ext.destroy(this.menu, this.repeater);
42027     },
42028
42029     // private
42030     onDestroy : function(){
42031         var doc = Ext.getDoc();
42032         doc.un('mouseover', this.monitorMouseOver, this);
42033         doc.un('mouseup', this.onMouseUp, this);
42034         if(this.rendered){
42035             Ext.ButtonToggleMgr.unregister(this);
42036         }
42037     },
42038
42039     // private
42040     doAutoWidth : function(){
42041         if(this.el && this.text && this.width === undefined){
42042             this.el.setWidth('auto');
42043             if(Ext.isIE7 && Ext.isStrict){
42044                 var ib = this.btnEl;
42045                 if(ib && ib.getWidth() > 20){
42046                     ib.clip();
42047                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
42048                 }
42049             }
42050             if(this.minWidth){
42051                 if(this.el.getWidth() < this.minWidth){
42052                     this.el.setWidth(this.minWidth);
42053                 }
42054             }
42055         }
42056     },
42057
42058     /**
42059      * Assigns this Button's click handler
42060      * @param {Function} handler The function to call when the button is clicked
42061      * @param {Object} scope (optional) Scope for the function passed in. Defaults to this Button.
42062      * @return {Ext.Button} this
42063      */
42064     setHandler : function(handler, scope){
42065         this.handler = handler;
42066         this.scope = scope;
42067         return this;
42068     },
42069
42070     /**
42071      * Sets this Button's text
42072      * @param {String} text The button text
42073      * @return {Ext.Button} this
42074      */
42075     setText : function(text){
42076         this.text = text;
42077         if(this.el){
42078             this.el.child('td.x-btn-mc ' + this.buttonSelector).update(text);
42079         }
42080         this.doAutoWidth();
42081         return this;
42082     },
42083
42084     /**
42085      * Gets the text for this Button
42086      * @return {String} The button text
42087      */
42088     getText : function(){
42089         return this.text;
42090     },
42091
42092     /**
42093      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
42094      * @param {Boolean} state (optional) Force a particular state
42095      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
42096      * @return {Ext.Button} this
42097      */
42098     toggle : function(state, suppressEvent){
42099         state = state === undefined ? !this.pressed : !!state;
42100         if(state != this.pressed){
42101             if(this.rendered){
42102                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
42103             }
42104             this.pressed = state;
42105             if(!suppressEvent){
42106                 this.fireEvent('toggle', this, state);
42107                 if(this.toggleHandler){
42108                     this.toggleHandler.call(this.scope || this, this, state);
42109                 }
42110             }
42111         }
42112         return this;
42113     },
42114
42115     /**
42116      * Focus the button
42117      */
42118     focus : function(){
42119         this.btnEl.focus();
42120     },
42121
42122     // private
42123     onDisable : function(){
42124         this.onDisableChange(true);
42125     },
42126
42127     // private
42128     onEnable : function(){
42129         this.onDisableChange(false);
42130     },
42131
42132     onDisableChange : function(disabled){
42133         if(this.el){
42134             if(!Ext.isIE6 || !this.text){
42135                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
42136             }
42137             this.el.dom.disabled = disabled;
42138         }
42139         this.disabled = disabled;
42140     },
42141
42142     /**
42143      * Show this button's menu (if it has one)
42144      */
42145     showMenu : function(){
42146         if(this.rendered && this.menu){
42147             if(this.tooltip){
42148                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
42149             }
42150             this.menu.show(this.el, this.menuAlign);
42151         }
42152         return this;
42153     },
42154
42155     /**
42156      * Hide this button's menu (if it has one)
42157      */
42158     hideMenu : function(){
42159         if(this.menu){
42160             this.menu.hide();
42161         }
42162         return this;
42163     },
42164
42165     /**
42166      * Returns true if the button has a menu and it is visible
42167      * @return {Boolean}
42168      */
42169     hasVisibleMenu : function(){
42170         return this.menu && this.menu.isVisible();
42171     },
42172
42173     // private
42174     onClick : function(e){
42175         if(e){
42176             e.preventDefault();
42177         }
42178         if(e.button !== 0){
42179             return;
42180         }
42181         if(!this.disabled){
42182             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
42183                 this.toggle();
42184             }
42185             if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
42186                 this.showMenu();
42187             }
42188             this.fireEvent('click', this, e);
42189             if(this.handler){
42190                 //this.el.removeClass('x-btn-over');
42191                 this.handler.call(this.scope || this, this, e);
42192             }
42193         }
42194     },
42195
42196     // private
42197     isMenuTriggerOver : function(e, internal){
42198         return this.menu && !internal;
42199     },
42200
42201     // private
42202     isMenuTriggerOut : function(e, internal){
42203         return this.menu && !internal;
42204     },
42205
42206     // private
42207     onMouseOver : function(e){
42208         if(!this.disabled){
42209             var internal = e.within(this.el,  true);
42210             if(!internal){
42211                 this.el.addClass('x-btn-over');
42212                 if(!this.monitoringMouseOver){
42213                     Ext.getDoc().on('mouseover', this.monitorMouseOver, this);
42214                     this.monitoringMouseOver = true;
42215                 }
42216                 this.fireEvent('mouseover', this, e);
42217             }
42218             if(this.isMenuTriggerOver(e, internal)){
42219                 this.fireEvent('menutriggerover', this, this.menu, e);
42220             }
42221         }
42222     },
42223
42224     // private
42225     monitorMouseOver : function(e){
42226         if(e.target != this.el.dom && !e.within(this.el)){
42227             if(this.monitoringMouseOver){
42228                 Ext.getDoc().un('mouseover', this.monitorMouseOver, this);
42229                 this.monitoringMouseOver = false;
42230             }
42231             this.onMouseOut(e);
42232         }
42233     },
42234
42235     // private
42236     onMouseOut : function(e){
42237         var internal = e.within(this.el) && e.target != this.el.dom;
42238         this.el.removeClass('x-btn-over');
42239         this.fireEvent('mouseout', this, e);
42240         if(this.isMenuTriggerOut(e, internal)){
42241             this.fireEvent('menutriggerout', this, this.menu, e);
42242         }
42243     },
42244     // private
42245     onFocus : function(e){
42246         if(!this.disabled){
42247             this.el.addClass('x-btn-focus');
42248         }
42249     },
42250     // private
42251     onBlur : function(e){
42252         this.el.removeClass('x-btn-focus');
42253     },
42254
42255     // private
42256     getClickEl : function(e, isUp){
42257        return this.el;
42258     },
42259
42260     // private
42261     onMouseDown : function(e){
42262         if(!this.disabled && e.button === 0){
42263             this.getClickEl(e).addClass('x-btn-click');
42264             Ext.getDoc().on('mouseup', this.onMouseUp, this);
42265         }
42266     },
42267     // private
42268     onMouseUp : function(e){
42269         if(e.button === 0){
42270             this.getClickEl(e, true).removeClass('x-btn-click');
42271             Ext.getDoc().un('mouseup', this.onMouseUp, this);
42272         }
42273     },
42274     // private
42275     onMenuShow : function(e){
42276         this.ignoreNextClick = 0;
42277         this.el.addClass('x-btn-menu-active');
42278         this.fireEvent('menushow', this, this.menu);
42279     },
42280     // private
42281     onMenuHide : function(e){
42282         this.el.removeClass('x-btn-menu-active');
42283         this.ignoreNextClick = this.restoreClick.defer(250, this);
42284         this.fireEvent('menuhide', this, this.menu);
42285     },
42286
42287     // private
42288     restoreClick : function(){
42289         this.ignoreNextClick = 0;
42290     }
42291
42292
42293
42294     /**
42295      * @cfg {String} autoEl @hide
42296      */
42297 });
42298 Ext.reg('button', Ext.Button);
42299
42300 // Private utility class used by Button
42301 Ext.ButtonToggleMgr = function(){
42302    var groups = {};
42303
42304    function toggleGroup(btn, state){
42305        if(state){
42306            var g = groups[btn.toggleGroup];
42307            for(var i = 0, l = g.length; i < l; i++){
42308                if(g[i] != btn){
42309                    g[i].toggle(false);
42310                }
42311            }
42312        }
42313    }
42314
42315    return {
42316        register : function(btn){
42317            if(!btn.toggleGroup){
42318                return;
42319            }
42320            var g = groups[btn.toggleGroup];
42321            if(!g){
42322                g = groups[btn.toggleGroup] = [];
42323            }
42324            g.push(btn);
42325            btn.on('toggle', toggleGroup);
42326        },
42327
42328        unregister : function(btn){
42329            if(!btn.toggleGroup){
42330                return;
42331            }
42332            var g = groups[btn.toggleGroup];
42333            if(g){
42334                g.remove(btn);
42335                btn.un('toggle', toggleGroup);
42336            }
42337        },
42338
42339        /**
42340         * Gets the pressed button in the passed group or null
42341         * @param {String} group
42342         * @return Button
42343         */
42344        getPressed : function(group){
42345            var g = groups[group];
42346            if(g){
42347                for(var i = 0, len = g.length; i < len; i++){
42348                    if(g[i].pressed === true){
42349                        return g[i];
42350                    }
42351                }
42352            }
42353            return null;
42354        }
42355    };
42356 }();/**\r
42357  * @class Ext.SplitButton\r
42358  * @extends Ext.Button\r
42359  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default\r
42360  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional\r
42361  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:\r
42362  * <pre><code>\r
42363 // display a dropdown menu:\r
42364 new Ext.SplitButton({\r
42365         renderTo: 'button-ct', // the container id\r
42366         text: 'Options',\r
42367         handler: optionsHandler, // handle a click on the button itself\r
42368         menu: new Ext.menu.Menu({\r
42369         items: [\r
42370                 // these items will render as dropdown menu items when the arrow is clicked:\r
42371                 {text: 'Item 1', handler: item1Handler},\r
42372                 {text: 'Item 2', handler: item2Handler}\r
42373         ]\r
42374         })\r
42375 });\r
42376 \r
42377 // Instead of showing a menu, you provide any type of custom\r
42378 // functionality you want when the dropdown arrow is clicked:\r
42379 new Ext.SplitButton({\r
42380         renderTo: 'button-ct',\r
42381         text: 'Options',\r
42382         handler: optionsHandler,\r
42383         arrowHandler: myCustomHandler\r
42384 });\r
42385 </code></pre>\r
42386  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)\r
42387  * @cfg {String} arrowTooltip The title attribute of the arrow\r
42388  * @constructor\r
42389  * Create a new menu button\r
42390  * @param {Object} config The config object\r
42391  * @xtype splitbutton\r
42392  */\r
42393 Ext.SplitButton = Ext.extend(Ext.Button, {\r
42394         // private\r
42395     arrowSelector : 'em',\r
42396     split: true,\r
42397 \r
42398     // private\r
42399     initComponent : function(){\r
42400         Ext.SplitButton.superclass.initComponent.call(this);\r
42401         /**\r
42402          * @event arrowclick\r
42403          * Fires when this button's arrow is clicked\r
42404          * @param {MenuButton} this\r
42405          * @param {EventObject} e The click event\r
42406          */\r
42407         this.addEvents("arrowclick");\r
42408     },\r
42409 \r
42410     // private\r
42411     onRender : function(){\r
42412         Ext.SplitButton.superclass.onRender.apply(this, arguments);\r
42413         if(this.arrowTooltip){\r
42414             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;\r
42415         }\r
42416     },\r
42417 \r
42418     /**\r
42419      * Sets this button's arrow click handler.\r
42420      * @param {Function} handler The function to call when the arrow is clicked\r
42421      * @param {Object} scope (optional) Scope for the function passed above\r
42422      */\r
42423     setArrowHandler : function(handler, scope){\r
42424         this.arrowHandler = handler;\r
42425         this.scope = scope;\r
42426     },\r
42427 \r
42428     getMenuClass : function(){\r
42429         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');\r
42430     },\r
42431 \r
42432     isClickOnArrow : function(e){\r
42433         return this.arrowAlign != 'bottom' ?\r
42434                e.getPageX() > this.el.child(this.buttonSelector).getRegion().right :\r
42435                e.getPageY() > this.el.child(this.buttonSelector).getRegion().bottom;\r
42436     },\r
42437 \r
42438     // private\r
42439     onClick : function(e, t){\r
42440         e.preventDefault();\r
42441         if(!this.disabled){\r
42442             if(this.isClickOnArrow(e)){\r
42443                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){\r
42444                     this.showMenu();\r
42445                 }\r
42446                 this.fireEvent("arrowclick", this, e);\r
42447                 if(this.arrowHandler){\r
42448                     this.arrowHandler.call(this.scope || this, this, e);\r
42449                 }\r
42450             }else{\r
42451                 if(this.enableToggle){\r
42452                     this.toggle();\r
42453                 }\r
42454                 this.fireEvent("click", this, e);\r
42455                 if(this.handler){\r
42456                     this.handler.call(this.scope || this, this, e);\r
42457                 }\r
42458             }\r
42459         }\r
42460     },\r
42461 \r
42462     // private\r
42463     isMenuTriggerOver : function(e){\r
42464         return this.menu && e.target.tagName == 'em';\r
42465     },\r
42466 \r
42467     // private\r
42468     isMenuTriggerOut : function(e, internal){\r
42469         return this.menu && e.target.tagName != 'em';\r
42470     }\r
42471 });\r
42472 \r
42473 Ext.reg('splitbutton', Ext.SplitButton);/**\r
42474  * @class Ext.CycleButton\r
42475  * @extends Ext.SplitButton\r
42476  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically\r
42477  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's\r
42478  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the\r
42479  * button displays the dropdown menu just like a normal SplitButton.  Example usage:\r
42480  * <pre><code>\r
42481 var btn = new Ext.CycleButton({\r
42482     showText: true,\r
42483     prependText: 'View as ',\r
42484     items: [{\r
42485         text:'text only',\r
42486         iconCls:'view-text',\r
42487         checked:true\r
42488     },{\r
42489         text:'HTML',\r
42490         iconCls:'view-html'\r
42491     }],\r
42492     changeHandler:function(btn, item){\r
42493         Ext.Msg.alert('Change View', item.text);\r
42494     }\r
42495 });\r
42496 </code></pre>\r
42497  * @constructor\r
42498  * Create a new split button\r
42499  * @param {Object} config The config object\r
42500  * @xtype cycle\r
42501  */\r
42502 Ext.CycleButton = Ext.extend(Ext.SplitButton, {\r
42503     /**\r
42504      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the\r
42505      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})\r
42506      */\r
42507     /**\r
42508      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)\r
42509      */\r
42510     /**\r
42511      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the\r
42512      * button's text (only applies when showText = true, defaults to '')\r
42513      */\r
42514     /**\r
42515      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu\r
42516      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead\r
42517      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the\r
42518      * following argument list: (SplitButton this, Ext.menu.CheckItem item)\r
42519      */\r
42520     /**\r
42521      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This\r
42522      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the \r
42523      * default behavior of changing the button's icon to match the selected item's icon on change.\r
42524      */\r
42525     /**\r
42526      * @property menu\r
42527      * @type Menu\r
42528      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.\r
42529      */\r
42530 \r
42531     // private\r
42532     getItemText : function(item){\r
42533         if(item && this.showText === true){\r
42534             var text = '';\r
42535             if(this.prependText){\r
42536                 text += this.prependText;\r
42537             }\r
42538             text += item.text;\r
42539             return text;\r
42540         }\r
42541         return undefined;\r
42542     },\r
42543 \r
42544     /**\r
42545      * Sets the button's active menu item.\r
42546      * @param {Ext.menu.CheckItem} item The item to activate\r
42547      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)\r
42548      */\r
42549     setActiveItem : function(item, suppressEvent){\r
42550         if(!Ext.isObject(item)){\r
42551             item = this.menu.getComponent(item);\r
42552         }\r
42553         if(item){\r
42554             if(!this.rendered){\r
42555                 this.text = this.getItemText(item);\r
42556                 this.iconCls = item.iconCls;\r
42557             }else{\r
42558                 var t = this.getItemText(item);\r
42559                 if(t){\r
42560                     this.setText(t);\r
42561                 }\r
42562                 this.setIconClass(item.iconCls);\r
42563             }\r
42564             this.activeItem = item;\r
42565             if(!item.checked){\r
42566                 item.setChecked(true, true);\r
42567             }\r
42568             if(this.forceIcon){\r
42569                 this.setIconClass(this.forceIcon);\r
42570             }\r
42571             if(!suppressEvent){\r
42572                 this.fireEvent('change', this, item);\r
42573             }\r
42574         }\r
42575     },\r
42576 \r
42577     /**\r
42578      * Gets the currently active menu item.\r
42579      * @return {Ext.menu.CheckItem} The active item\r
42580      */\r
42581     getActiveItem : function(){\r
42582         return this.activeItem;\r
42583     },\r
42584 \r
42585     // private\r
42586     initComponent : function(){\r
42587         this.addEvents(\r
42588             /**\r
42589              * @event change\r
42590              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function\r
42591              * is set on this CycleButton, it will be called instead on active item change and this change event will\r
42592              * not be fired.\r
42593              * @param {Ext.CycleButton} this\r
42594              * @param {Ext.menu.CheckItem} item The menu item that was selected\r
42595              */\r
42596             "change"\r
42597         );\r
42598 \r
42599         if(this.changeHandler){\r
42600             this.on('change', this.changeHandler, this.scope||this);\r
42601             delete this.changeHandler;\r
42602         }\r
42603 \r
42604         this.itemCount = this.items.length;\r
42605 \r
42606         this.menu = {cls:'x-cycle-menu', items:[]};\r
42607         var checked;\r
42608         Ext.each(this.items, function(item, i){\r
42609             Ext.apply(item, {\r
42610                 group: item.group || this.id,\r
42611                 itemIndex: i,\r
42612                 checkHandler: this.checkHandler,\r
42613                 scope: this,\r
42614                 checked: item.checked || false\r
42615             });\r
42616             this.menu.items.push(item);\r
42617             if(item.checked){\r
42618                 checked = item;\r
42619             }\r
42620         }, this);\r
42621         this.setActiveItem(checked, true);\r
42622         Ext.CycleButton.superclass.initComponent.call(this);\r
42623 \r
42624         this.on('click', this.toggleSelected, this);\r
42625     },\r
42626 \r
42627     // private\r
42628     checkHandler : function(item, pressed){\r
42629         if(pressed){\r
42630             this.setActiveItem(item);\r
42631         }\r
42632     },\r
42633 \r
42634     /**\r
42635      * This is normally called internally on button click, but can be called externally to advance the button's\r
42636      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu\r
42637      * the active item will be set to the first item in the menu.\r
42638      */\r
42639     toggleSelected : function(){\r
42640         var m = this.menu;\r
42641         m.render();\r
42642         // layout if we haven't before so the items are active\r
42643         if(!m.hasLayout){\r
42644             m.doLayout();\r
42645         }\r
42646         \r
42647         var nextIdx, checkItem;\r
42648         for (var i = 1; i < this.itemCount; i++) {\r
42649             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;\r
42650             // check the potential item\r
42651             checkItem = m.items.itemAt(nextIdx);\r
42652             // if its not disabled then check it.\r
42653             if (!checkItem.disabled) {\r
42654                 checkItem.setChecked(true);\r
42655                 break;\r
42656             }\r
42657         }\r
42658     }\r
42659 });\r
42660 Ext.reg('cycle', Ext.CycleButton);/**\r
42661  * @class Ext.layout.ToolbarLayout\r
42662  * @extends Ext.layout.ContainerLayout\r
42663  * Layout manager implicitly used by Ext.Toolbar.\r
42664  */\r
42665 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
42666     monitorResize : true,\r
42667     triggerWidth : 18,\r
42668     lastOverflow : false,\r
42669 \r
42670     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',\r
42671     // private\r
42672     onLayout : function(ct, target){\r
42673         if(!this.leftTr){\r
42674             target.addClass('x-toolbar-layout-ct');\r
42675             target.insertHtml('beforeEnd',\r
42676                  '<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
42677             this.leftTr = target.child('tr.x-toolbar-left-row', true);\r
42678             this.rightTr = target.child('tr.x-toolbar-right-row', true);\r
42679             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);\r
42680         }\r
42681         var side = this.leftTr;\r
42682         var pos = 0;\r
42683 \r
42684         var items = ct.items.items;\r
42685         for(var i = 0, len = items.length, c; i < len; i++, pos++) {\r
42686             c = items[i];\r
42687             if(c.isFill){\r
42688                 side = this.rightTr;\r
42689                 pos = -1;\r
42690             }else if(!c.rendered){\r
42691                 c.render(this.insertCell(c, side, pos));\r
42692             }else{\r
42693                 if(!c.xtbHidden && !this.isValidParent(c, side.childNodes[pos])){\r
42694                     var td = this.insertCell(c, side, pos);\r
42695                     td.appendChild(c.getDomPositionEl().dom);\r
42696                     c.container = Ext.get(td);\r
42697                 }\r
42698             }\r
42699         }\r
42700         //strip extra empty cells\r
42701         this.cleanup(this.leftTr);\r
42702         this.cleanup(this.rightTr);\r
42703         this.cleanup(this.extrasTr);\r
42704         this.fitToSize(target);\r
42705     },\r
42706 \r
42707     cleanup : function(row){\r
42708         var cn = row.childNodes;\r
42709         for(var i = cn.length-1, c; i >= 0 && (c = cn[i]); i--){\r
42710             if(!c.firstChild){\r
42711                 row.removeChild(c);\r
42712             }\r
42713         }\r
42714     },\r
42715 \r
42716     insertCell : function(c, side, pos){\r
42717         var td = document.createElement('td');\r
42718         td.className='x-toolbar-cell';\r
42719         side.insertBefore(td, side.childNodes[pos]||null);\r
42720         return td;\r
42721     },\r
42722 \r
42723     hideItem : function(item){\r
42724         var h = (this.hiddens = this.hiddens || []);\r
42725         h.push(item);\r
42726         item.xtbHidden = true;\r
42727         item.xtbWidth = item.getDomPositionEl().dom.parentNode.offsetWidth;\r
42728         item.hide();\r
42729     },\r
42730 \r
42731     unhideItem : function(item){\r
42732         item.show();\r
42733         item.xtbHidden = false;\r
42734         this.hiddens.remove(item);\r
42735         if(this.hiddens.length < 1){\r
42736             delete this.hiddens;\r
42737         }\r
42738     },\r
42739 \r
42740     getItemWidth : function(c){\r
42741         return c.hidden ? (c.xtbWidth || 0) : c.getDomPositionEl().dom.parentNode.offsetWidth;\r
42742     },\r
42743 \r
42744     fitToSize : function(t){\r
42745         if(this.container.enableOverflow === false){\r
42746             return;\r
42747         }\r
42748         var w = t.dom.clientWidth;\r
42749         var lw = this.lastWidth || 0;\r
42750         this.lastWidth = w;\r
42751         var iw = t.dom.firstChild.offsetWidth;\r
42752 \r
42753         var clipWidth = w - this.triggerWidth;\r
42754         var hideIndex = -1;\r
42755 \r
42756         if(iw > w || (this.hiddens && w >= lw)){\r
42757             var i, items = this.container.items.items, len = items.length, c;\r
42758             var loopWidth = 0;\r
42759             for(i = 0; i < len; i++) {\r
42760                 c = items[i];\r
42761                 if(!c.isFill){\r
42762                     loopWidth += this.getItemWidth(c);\r
42763                     if(loopWidth > clipWidth){\r
42764                         if(!c.xtbHidden){\r
42765                             this.hideItem(c);\r
42766                         }\r
42767                     }else{\r
42768                         if(c.xtbHidden){\r
42769                             this.unhideItem(c);\r
42770                         }\r
42771                     }\r
42772                 }\r
42773             }\r
42774         }\r
42775         if(this.hiddens){\r
42776             this.initMore();\r
42777             if(!this.lastOverflow){\r
42778                 this.container.fireEvent('overflowchange', this.container, true);\r
42779                 this.lastOverflow = true;\r
42780             }\r
42781         }else if(this.more){\r
42782             this.clearMenu();\r
42783             this.more.destroy();\r
42784             delete this.more;\r
42785             if(this.lastOverflow){\r
42786                 this.container.fireEvent('overflowchange', this.container, false);\r
42787                 this.lastOverflow = false;\r
42788             }\r
42789         }\r
42790     },\r
42791 \r
42792     createMenuConfig : function(c, hideOnClick){\r
42793         var cfg = Ext.apply({}, c.initialConfig),\r
42794             group = c.toggleGroup;\r
42795 \r
42796         Ext.apply(cfg, {\r
42797             text: c.overflowText || c.text,\r
42798             iconCls: c.iconCls,\r
42799             icon: c.icon,\r
42800             itemId: c.itemId,\r
42801             disabled: c.disabled,\r
42802             handler: c.handler,\r
42803             scope: c.scope,\r
42804             menu: c.menu,\r
42805             hideOnClick: hideOnClick\r
42806         });\r
42807         if(group || c.enableToggle){\r
42808             Ext.apply(cfg, {\r
42809                 group: group,\r
42810                 checked: c.pressed,\r
42811                 listeners: {\r
42812                     checkchange: function(item, checked){\r
42813                         c.toggle(checked);\r
42814                     }\r
42815                 }\r
42816             });\r
42817         }\r
42818         delete cfg.xtype;\r
42819         delete cfg.id;\r
42820         return cfg;\r
42821     },\r
42822 \r
42823     // private\r
42824     addComponentToMenu : function(m, c){\r
42825         if(c instanceof Ext.Toolbar.Separator){\r
42826             m.add('-');\r
42827         }else if(Ext.isFunction(c.isXType)){\r
42828             if(c.isXType('splitbutton')){\r
42829                 m.add(this.createMenuConfig(c, true));\r
42830             }else if(c.isXType('button')){\r
42831                 m.add(this.createMenuConfig(c, !c.menu));\r
42832             }else if(c.isXType('buttongroup')){\r
42833                 c.items.each(function(item){\r
42834                      this.addComponentToMenu(m, item);\r
42835                 }, this);\r
42836             }\r
42837         }\r
42838     },\r
42839 \r
42840     clearMenu : function(){\r
42841         var m = this.moreMenu;\r
42842         if(m && m.items){\r
42843             m.items.each(function(item){\r
42844                 delete item.menu;\r
42845             });\r
42846         }\r
42847     },\r
42848 \r
42849     // private\r
42850     beforeMoreShow : function(m){\r
42851         var h = this.container.items.items,\r
42852             len = h.length,\r
42853             c,\r
42854             prev,\r
42855             needsSep = function(group, item){\r
42856                 return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);\r
42857             };\r
42858 \r
42859         this.clearMenu();\r
42860         m.removeAll();\r
42861         for(var i = 0; i < len; i++){\r
42862             c = h[i];\r
42863             if(c.xtbHidden){\r
42864                 if(prev && (needsSep(c, prev) || needsSep(prev, c))){\r
42865                     m.add('-');\r
42866                 }\r
42867                 this.addComponentToMenu(m, c);\r
42868                 prev = c;\r
42869             }\r
42870         }\r
42871         // put something so the menu isn't empty\r
42872         // if no compatible items found\r
42873         if(m.items.length < 1){\r
42874             m.add(this.noItemsMenuText);\r
42875         }\r
42876     },\r
42877 \r
42878     initMore : function(){\r
42879         if(!this.more){\r
42880             this.moreMenu = new Ext.menu.Menu({\r
42881                 listeners: {\r
42882                     beforeshow: this.beforeMoreShow,\r
42883                     scope: this\r
42884                 }\r
42885             });\r
42886             this.moreMenu.ownerCt = this.container;\r
42887             this.more = new Ext.Button({\r
42888                 iconCls: 'x-toolbar-more-icon',\r
42889                 cls: 'x-toolbar-more',\r
42890                 menu: this.moreMenu\r
42891             });\r
42892             var td = this.insertCell(this.more, this.extrasTr, 100);\r
42893             this.more.render(td);\r
42894         }\r
42895     },\r
42896 \r
42897     destroy : function(){\r
42898         Ext.destroy(this.more, this.moreMenu);\r
42899         Ext.layout.ToolbarLayout.superclass.destroy.call(this);\r
42900     }\r
42901     /**\r
42902      * @property activeItem\r
42903      * @hide\r
42904      */\r
42905 });\r
42906 \r
42907 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;\r
42908 \r
42909 /**\r
42910  * @class Ext.Toolbar\r
42911  * @extends Ext.Container\r
42912  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar\r
42913  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may\r
42914  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,\r
42915  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>\r
42916  * <p>Some items have shortcut strings for creation:</p>\r
42917  * <pre>\r
42918 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>\r
42919 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container\r
42920 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items\r
42921 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements\r
42922  * </pre>\r
42923  *\r
42924  * Example usage of various elements:\r
42925  * <pre><code>\r
42926 var tb = new Ext.Toolbar({\r
42927     renderTo: document.body,\r
42928     width: 600,\r
42929     height: 100,\r
42930     items: [\r
42931         {\r
42932             // xtype: 'button', // default for Toolbars, same as 'tbbutton'\r
42933             text: 'Button'\r
42934         },\r
42935         {\r
42936             xtype: 'splitbutton', // same as 'tbsplitbutton'\r
42937             text: 'Split Button'\r
42938         },\r
42939         // begin using the right-justified button container\r
42940         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill\r
42941         {\r
42942             xtype: 'textfield',\r
42943             name: 'field1',\r
42944             emptyText: 'enter search term'\r
42945         },\r
42946         // add a vertical separator bar between toolbar items\r
42947         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator\r
42948         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem\r
42949         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer\r
42950         'text 2',\r
42951         {xtype: 'tbspacer', width: 50}, // add a 50px space\r
42952         'text 3'\r
42953     ]\r
42954 });\r
42955  * </code></pre>\r
42956  * Example adding a ComboBox within a menu of a button:\r
42957  * <pre><code>\r
42958 // ComboBox creation\r
42959 var combo = new Ext.form.ComboBox({\r
42960     store: new Ext.data.ArrayStore({\r
42961         autoDestroy: true,\r
42962         fields: ['initials', 'fullname'],\r
42963         data : [\r
42964             ['FF', 'Fred Flintstone'],\r
42965             ['BR', 'Barney Rubble']\r
42966         ]\r
42967     }),\r
42968     displayField: 'fullname',\r
42969     typeAhead: true,\r
42970     mode: 'local',\r
42971     forceSelection: true,\r
42972     triggerAction: 'all',\r
42973     emptyText: 'Select a name...',\r
42974     selectOnFocus: true,\r
42975     width: 135,\r
42976     getListParent: function() {\r
42977         return this.el.up('.x-menu');\r
42978     },\r
42979     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu\r
42980 });\r
42981 \r
42982 // put ComboBox in a Menu\r
42983 var menu = new Ext.menu.Menu({\r
42984     id: 'mainMenu',\r
42985     items: [\r
42986         combo // A Field in a Menu\r
42987     ]\r
42988 });\r
42989 \r
42990 // add a Button with the menu\r
42991 tb.add({\r
42992         text:'Button w/ Menu',\r
42993         menu: menu  // assign menu by instance\r
42994     });\r
42995 tb.doLayout();\r
42996  * </code></pre>\r
42997  * @constructor\r
42998  * Creates a new Toolbar\r
42999  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>\r
43000  * @xtype toolbar\r
43001  */\r
43002 Ext.Toolbar = function(config){\r
43003     if(Ext.isArray(config)){\r
43004         config = {items: config, layout: 'toolbar'};\r
43005     } else {\r
43006         config = Ext.apply({\r
43007             layout: 'toolbar'\r
43008         }, config);\r
43009         if(config.buttons) {\r
43010             config.items = config.buttons;\r
43011         }\r
43012     }\r
43013     Ext.Toolbar.superclass.constructor.call(this, config);\r
43014 };\r
43015 \r
43016 (function(){\r
43017 \r
43018 var T = Ext.Toolbar;\r
43019 \r
43020 Ext.extend(T, Ext.Container, {\r
43021 \r
43022     defaultType: 'button',\r
43023 \r
43024     /**\r
43025      * @cfg {String/Object} layout\r
43026      * This class assigns a default layout (<code>layout:'<b>toolbar</b>'</code>).\r
43027      * Developers <i>may</i> override this configuration option if another layout\r
43028      * is required (the constructor must be passed a configuration object in this\r
43029      * case instead of an array).\r
43030      * See {@link Ext.Container#layout} for additional information.\r
43031      */\r
43032 \r
43033     trackMenus : true,\r
43034     internalDefaults: {removeMode: 'container', hideParent: true},\r
43035     toolbarCls: 'x-toolbar',\r
43036 \r
43037     initComponent : function(){\r
43038         T.superclass.initComponent.call(this);\r
43039 \r
43040         /**\r
43041          * @event overflowchange\r
43042          * Fires after the overflow state has changed.\r
43043          * @param {Object} c The Container\r
43044          * @param {Boolean} lastOverflow overflow state\r
43045          */\r
43046         this.addEvents('overflowchange');\r
43047     },\r
43048 \r
43049     // private\r
43050     onRender : function(ct, position){\r
43051         if(!this.el){\r
43052             if(!this.autoCreate){\r
43053                 this.autoCreate = {\r
43054                     cls: this.toolbarCls + ' x-small-editor'\r
43055                 };\r
43056             }\r
43057             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);\r
43058             Ext.Toolbar.superclass.onRender.apply(this, arguments);\r
43059         }\r
43060     },\r
43061 \r
43062     /**\r
43063      * <p>Adds element(s) to the toolbar -- this function takes a variable number of\r
43064      * arguments of mixed type and adds them to the toolbar.</p>\r
43065      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43066      * @param {Mixed} arg1 The following types of arguments are all valid:<br />\r
43067      * <ul>\r
43068      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>\r
43069      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>\r
43070      * <li>Field: Any form field (equivalent to {@link #addField})</li>\r
43071      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>\r
43072      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).\r
43073      * Note that there are a few special strings that are treated differently as explained next.</li>\r
43074      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>\r
43075      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>\r
43076      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>\r
43077      * </ul>\r
43078      * @param {Mixed} arg2\r
43079      * @param {Mixed} etc.\r
43080      * @method add\r
43081      */\r
43082 \r
43083     // private\r
43084     lookupComponent : function(c){\r
43085         if(Ext.isString(c)){\r
43086             if(c == '-'){\r
43087                 c = new T.Separator();\r
43088             }else if(c == ' '){\r
43089                 c = new T.Spacer();\r
43090             }else if(c == '->'){\r
43091                 c = new T.Fill();\r
43092             }else{\r
43093                 c = new T.TextItem(c);\r
43094             }\r
43095             this.applyDefaults(c);\r
43096         }else{\r
43097             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item\r
43098                 c = this.constructItem(c);\r
43099             }else if(c.tag){ // DomHelper spec\r
43100                 c = new T.Item({autoEl: c});\r
43101             }else if(c.tagName){ // element\r
43102                 c = new T.Item({el:c});\r
43103             }else if(Ext.isObject(c)){ // must be button config?\r
43104                 c = c.xtype ? this.constructItem(c) : this.constructButton(c);\r
43105             }\r
43106         }\r
43107         return c;\r
43108     },\r
43109 \r
43110     // private\r
43111     applyDefaults : function(c){\r
43112         if(!Ext.isString(c)){\r
43113             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);\r
43114             var d = this.internalDefaults;\r
43115             if(c.events){\r
43116                 Ext.applyIf(c.initialConfig, d);\r
43117                 Ext.apply(c, d);\r
43118             }else{\r
43119                 Ext.applyIf(c, d);\r
43120             }\r
43121         }\r
43122         return c;\r
43123     },\r
43124 \r
43125     // private\r
43126     constructItem : function(item, type){\r
43127         return Ext.create(item, type || this.defaultType);\r
43128     },\r
43129 \r
43130     /**\r
43131      * Adds a separator\r
43132      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43133      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}\r
43134      */\r
43135     addSeparator : function(){\r
43136         return this.add(new T.Separator());\r
43137     },\r
43138 \r
43139     /**\r
43140      * Adds a spacer element\r
43141      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43142      * @return {Ext.Toolbar.Spacer} The spacer item\r
43143      */\r
43144     addSpacer : function(){\r
43145         return this.add(new T.Spacer());\r
43146     },\r
43147 \r
43148     /**\r
43149      * Forces subsequent additions into the float:right toolbar\r
43150      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43151      */\r
43152     addFill : function(){\r
43153         this.add(new T.Fill());\r
43154     },\r
43155 \r
43156     /**\r
43157      * Adds any standard HTML element to the toolbar\r
43158      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43159      * @param {Mixed} el The element or id of the element to add\r
43160      * @return {Ext.Toolbar.Item} The element's item\r
43161      */\r
43162     addElement : function(el){\r
43163         return this.addItem(new T.Item({el:el}));\r
43164     },\r
43165 \r
43166     /**\r
43167      * Adds any Toolbar.Item or subclass\r
43168      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43169      * @param {Ext.Toolbar.Item} item\r
43170      * @return {Ext.Toolbar.Item} The item\r
43171      */\r
43172     addItem : function(item){\r
43173         return this.add.apply(this, arguments);\r
43174     },\r
43175 \r
43176     /**\r
43177      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.\r
43178      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43179      * @param {Object/Array} config A button config or array of configs\r
43180      * @return {Ext.Button/Array}\r
43181      */\r
43182     addButton : function(config){\r
43183         if(Ext.isArray(config)){\r
43184             var buttons = [];\r
43185             for(var i = 0, len = config.length; i < len; i++) {\r
43186                 buttons.push(this.addButton(config[i]));\r
43187             }\r
43188             return buttons;\r
43189         }\r
43190         return this.add(this.constructButton(config));\r
43191     },\r
43192 \r
43193     /**\r
43194      * Adds text to the toolbar\r
43195      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43196      * @param {String} text The text to add\r
43197      * @return {Ext.Toolbar.Item} The element's item\r
43198      */\r
43199     addText : function(text){\r
43200         return this.addItem(new T.TextItem(text));\r
43201     },\r
43202 \r
43203     /**\r
43204      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config\r
43205      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43206      * @param {Object} config\r
43207      * @return {Ext.Toolbar.Item} The element's item\r
43208      */\r
43209     addDom : function(config){\r
43210         return this.add(new T.Item({autoEl: config}));\r
43211     },\r
43212 \r
43213     /**\r
43214      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have\r
43215      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.\r
43216      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43217      * @param {Ext.form.Field} field\r
43218      * @return {Ext.Toolbar.Item}\r
43219      */\r
43220     addField : function(field){\r
43221         return this.add(field);\r
43222     },\r
43223 \r
43224     /**\r
43225      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.\r
43226      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>\r
43227      * @param {Number} index The index where the item is to be inserted\r
43228      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be\r
43229      * inserted, or an array of buttons/configs.\r
43230      * @return {Ext.Button/Item}\r
43231      */\r
43232     insertButton : function(index, item){\r
43233         if(Ext.isArray(item)){\r
43234             var buttons = [];\r
43235             for(var i = 0, len = item.length; i < len; i++) {\r
43236                buttons.push(this.insertButton(index + i, item[i]));\r
43237             }\r
43238             return buttons;\r
43239         }\r
43240         return Ext.Toolbar.superclass.insert.call(this, index, item);\r
43241     },\r
43242 \r
43243     // private\r
43244     initMenuTracking : function(item){\r
43245         if(this.trackMenus && item.menu){\r
43246             this.mon(item, {\r
43247                 'menutriggerover' : this.onButtonTriggerOver,\r
43248                 'menushow' : this.onButtonMenuShow,\r
43249                 'menuhide' : this.onButtonMenuHide,\r
43250                 scope: this\r
43251             });\r
43252         }\r
43253     },\r
43254 \r
43255     // private\r
43256     constructButton : function(item){\r
43257         var b = item.events ? item : this.constructItem(item, item.split ? 'splitbutton' : this.defaultType);\r
43258         this.initMenuTracking(b);\r
43259         return b;\r
43260     },\r
43261 \r
43262     // private\r
43263     onDisable : function(){\r
43264         this.items.each(function(item){\r
43265              if(item.disable){\r
43266                  item.disable();\r
43267              }\r
43268         });\r
43269     },\r
43270 \r
43271     // private\r
43272     onEnable : function(){\r
43273         this.items.each(function(item){\r
43274              if(item.enable){\r
43275                  item.enable();\r
43276              }\r
43277         });\r
43278     },\r
43279 \r
43280     // private\r
43281     onButtonTriggerOver : function(btn){\r
43282         if(this.activeMenuBtn && this.activeMenuBtn != btn){\r
43283             this.activeMenuBtn.hideMenu();\r
43284             btn.showMenu();\r
43285             this.activeMenuBtn = btn;\r
43286         }\r
43287     },\r
43288 \r
43289     // private\r
43290     onButtonMenuShow : function(btn){\r
43291         this.activeMenuBtn = btn;\r
43292     },\r
43293 \r
43294     // private\r
43295     onButtonMenuHide : function(btn){\r
43296         delete this.activeMenuBtn;\r
43297     }\r
43298 });\r
43299 Ext.reg('toolbar', Ext.Toolbar);\r
43300 \r
43301 /**\r
43302  * @class Ext.Toolbar.Item\r
43303  * @extends Ext.BoxComponent\r
43304  * The base class that other non-interacting Toolbar Item classes should extend in order to\r
43305  * get some basic common toolbar item functionality.\r
43306  * @constructor\r
43307  * Creates a new Item\r
43308  * @param {HTMLElement} el\r
43309  * @xtype tbitem\r
43310  */\r
43311 T.Item = Ext.extend(Ext.BoxComponent, {\r
43312     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD\r
43313     enable:Ext.emptyFn,\r
43314     disable:Ext.emptyFn,\r
43315     focus:Ext.emptyFn\r
43316     /**\r
43317      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.\r
43318      */\r
43319 });\r
43320 Ext.reg('tbitem', T.Item);\r
43321 \r
43322 /**\r
43323  * @class Ext.Toolbar.Separator\r
43324  * @extends Ext.Toolbar.Item\r
43325  * A simple class that adds a vertical separator bar between toolbar items\r
43326  * (css class:<tt>'xtb-sep'</tt>). Example usage:\r
43327  * <pre><code>\r
43328 new Ext.Panel({\r
43329     tbar : [\r
43330         'Item 1',\r
43331         {xtype: 'tbseparator'}, // or '-'\r
43332         'Item 2'\r
43333     ]\r
43334 });\r
43335 </code></pre>\r
43336  * @constructor\r
43337  * Creates a new Separator\r
43338  * @xtype tbseparator\r
43339  */\r
43340 T.Separator = Ext.extend(T.Item, {\r
43341     onRender : function(ct, position){\r
43342         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);\r
43343     }\r
43344 });\r
43345 Ext.reg('tbseparator', T.Separator);\r
43346 \r
43347 /**\r
43348  * @class Ext.Toolbar.Spacer\r
43349  * @extends Ext.Toolbar.Item\r
43350  * A simple element that adds extra horizontal space between items in a toolbar.\r
43351  * By default a 2px wide space is added via css specification:<pre><code>\r
43352 .x-toolbar .xtb-spacer {\r
43353     width:2px;\r
43354 }\r
43355  * </code></pre>\r
43356  * <p>Example usage:</p>\r
43357  * <pre><code>\r
43358 new Ext.Panel({\r
43359     tbar : [\r
43360         'Item 1',\r
43361         {xtype: 'tbspacer'}, // or ' '\r
43362         'Item 2',\r
43363         // space width is also configurable via javascript\r
43364         {xtype: 'tbspacer', width: 50}, // add a 50px space\r
43365         'Item 3'\r
43366     ]\r
43367 });\r
43368 </code></pre>\r
43369  * @constructor\r
43370  * Creates a new Spacer\r
43371  * @xtype tbspacer\r
43372  */\r
43373 T.Spacer = Ext.extend(T.Item, {\r
43374     /**\r
43375      * @cfg {Number} width\r
43376      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).\r
43377      */\r
43378 \r
43379     onRender : function(ct, position){\r
43380         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);\r
43381     }\r
43382 });\r
43383 Ext.reg('tbspacer', T.Spacer);\r
43384 \r
43385 /**\r
43386  * @class Ext.Toolbar.Fill\r
43387  * @extends Ext.Toolbar.Spacer\r
43388  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using\r
43389  * the right-justified button container.\r
43390  * <pre><code>\r
43391 new Ext.Panel({\r
43392     tbar : [\r
43393         'Item 1',\r
43394         {xtype: 'tbfill'}, // or '->'\r
43395         'Item 2'\r
43396     ]\r
43397 });\r
43398 </code></pre>\r
43399  * @constructor\r
43400  * Creates a new Fill\r
43401  * @xtype tbfill\r
43402  */\r
43403 T.Fill = Ext.extend(T.Item, {\r
43404     // private\r
43405     render : Ext.emptyFn,\r
43406     isFill : true\r
43407 });\r
43408 Ext.reg('tbfill', T.Fill);\r
43409 \r
43410 /**\r
43411  * @class Ext.Toolbar.TextItem\r
43412  * @extends Ext.Toolbar.Item\r
43413  * A simple class that renders text directly into a toolbar\r
43414  * (with css class:<tt>'xtb-text'</tt>). Example usage:\r
43415  * <pre><code>\r
43416 new Ext.Panel({\r
43417     tbar : [\r
43418         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'\r
43419     ]\r
43420 });\r
43421 </code></pre>\r
43422  * @constructor\r
43423  * Creates a new TextItem\r
43424  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property\r
43425  * @xtype tbtext\r
43426  */\r
43427 T.TextItem = Ext.extend(T.Item, {\r
43428     /**\r
43429      * @cfg {String} text The text to be used as innerHTML (html tags are accepted)\r
43430      */ \r
43431 \r
43432     constructor: function(config){\r
43433         T.TextItem.superclass.constructor.call(this, Ext.isString(config) ? {text: config} : config);\r
43434     },\r
43435     \r
43436     // private\r
43437     onRender : function(ct, position) {\r
43438         this.autoEl = {cls: 'xtb-text', html: this.text || ''};\r
43439         T.TextItem.superclass.onRender.call(this, ct, position);\r
43440     },\r
43441     \r
43442     /**\r
43443      * Updates this item's text, setting the text to be used as innerHTML.\r
43444      * @param {String} t The text to display (html accepted).\r
43445      */\r
43446     setText : function(t) {\r
43447         if(this.rendered){\r
43448             this.el.update(t);\r
43449         }else{\r
43450             this.text = t;\r
43451         }\r
43452     }\r
43453 });\r
43454 Ext.reg('tbtext', T.TextItem);\r
43455 \r
43456 // backwards compat\r
43457 T.Button = Ext.extend(Ext.Button, {});\r
43458 T.SplitButton = Ext.extend(Ext.SplitButton, {});\r
43459 Ext.reg('tbbutton', T.Button);\r
43460 Ext.reg('tbsplit', T.SplitButton);\r
43461 \r
43462 })();\r
43463 /**\r
43464  * @class Ext.ButtonGroup\r
43465  * @extends Ext.Panel\r
43466  * Container for a group of buttons. Example usage:\r
43467  * <pre><code>\r
43468 var p = new Ext.Panel({\r
43469     title: 'Panel with Button Group',\r
43470     width: 300,\r
43471     height:200,\r
43472     renderTo: document.body,\r
43473     html: 'whatever',\r
43474     tbar: [{\r
43475         xtype: 'buttongroup',\r
43476         {@link #columns}: 3,\r
43477         title: 'Clipboard',\r
43478         items: [{\r
43479             text: 'Paste',\r
43480             scale: 'large',\r
43481             rowspan: 3, iconCls: 'add',\r
43482             iconAlign: 'top',\r
43483             cls: 'x-btn-as-arrow'\r
43484         },{\r
43485             xtype:'splitbutton',\r
43486             text: 'Menu Button',\r
43487             scale: 'large',\r
43488             rowspan: 3,\r
43489             iconCls: 'add',\r
43490             iconAlign: 'top',\r
43491             arrowAlign:'bottom',\r
43492             menu: [{text: 'Menu Item 1'}]\r
43493         },{\r
43494             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]\r
43495         },{\r
43496             text: 'Copy', iconCls: 'add16'\r
43497         },{\r
43498             text: 'Format', iconCls: 'add16'\r
43499         }]\r
43500     }]\r
43501 });\r
43502  * </code></pre>\r
43503  * @xtype buttongroup\r
43504  */\r
43505 Ext.ButtonGroup = Ext.extend(Ext.Panel, {\r
43506     /**\r
43507      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the\r
43508      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.\r
43509      */\r
43510     /**\r
43511      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.\r
43512      */\r
43513     baseCls: 'x-btn-group',\r
43514     /**\r
43515      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.\r
43516      */\r
43517     layout:'table',\r
43518     defaultType: 'button',\r
43519     /**\r
43520      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.\r
43521      */\r
43522     frame: true,\r
43523     internalDefaults: {removeMode: 'container', hideParent: true},\r
43524 \r
43525     initComponent : function(){\r
43526         this.layoutConfig = this.layoutConfig || {};\r
43527         Ext.applyIf(this.layoutConfig, {\r
43528             columns : this.columns\r
43529         });\r
43530         if(!this.title){\r
43531             this.addClass('x-btn-group-notitle');\r
43532         }\r
43533         this.on('afterlayout', this.onAfterLayout, this);\r
43534         Ext.ButtonGroup.superclass.initComponent.call(this);\r
43535     },\r
43536 \r
43537     applyDefaults : function(c){\r
43538         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);\r
43539         var d = this.internalDefaults;\r
43540         if(c.events){\r
43541             Ext.applyIf(c.initialConfig, d);\r
43542             Ext.apply(c, d);\r
43543         }else{\r
43544             Ext.applyIf(c, d);\r
43545         }\r
43546         return c;\r
43547     },\r
43548 \r
43549     onAfterLayout : function(){\r
43550         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;\r
43551         this.body.setWidth(bodyWidth);\r
43552         this.el.setWidth(bodyWidth + this.getFrameWidth());\r
43553     }\r
43554     /**\r
43555      * @cfg {Array} tools  @hide\r
43556      */\r
43557 });\r
43558 \r
43559 Ext.reg('buttongroup', Ext.ButtonGroup);\r
43560 /**
43561  * @class Ext.PagingToolbar
43562  * @extends Ext.Toolbar
43563  * <p>As the amount of records increases, the time required for the browser to render
43564  * them increases. Paging is used to reduce the amount of data exchanged with the client.
43565  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
43566  * scrollbars will be added.</p>
43567  * <p>Paging is typically handled on the server side (see exception below). The client sends
43568  * parameters to the server side, which the server needs to interpret and then respond with the
43569  * approprate data.</p>
43570  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
43571  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
43572  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
43573  * paging criteria.</p>
43574  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
43575  * <pre><code>
43576 Ext.QuickTips.init(); // to display button quicktips
43577
43578 var myStore = new Ext.data.Store({
43579     reader: new Ext.data.JsonReader({
43580         {@link Ext.data.JsonReader#totalProperty totalProperty}: 'results', 
43581         ...
43582     }),
43583     ...
43584 });
43585
43586 var myPageSize = 25;  // server script should only send back 25 items at a time
43587
43588 var grid = new Ext.grid.GridPanel({
43589     ...
43590     store: myStore,
43591     bbar: new Ext.PagingToolbar({
43592         {@link #store}: myStore,       // grid and PagingToolbar using same store
43593         {@link #displayInfo}: true,
43594         {@link #pageSize}: myPageSize,
43595         {@link #prependButtons}: true,
43596         items: [
43597             'text 1'
43598         ]
43599     })
43600 });
43601  * </code></pre>
43602  *
43603  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
43604  * <pre><code>
43605 store.load({
43606     params: {
43607         // specify params for the first page load if using paging
43608         start: 0,          
43609         limit: myPageSize,
43610         // other params
43611         foo:   'bar'
43612     }
43613 });
43614  * </code></pre>
43615  * 
43616  * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
43617  * <pre><code>
43618 var myStore = new Ext.data.Store({
43619     {@link Ext.data.Store#autoLoad autoLoad}: {params:{start: 0, limit: 25}},
43620     ...
43621 });
43622  * </code></pre>
43623  * 
43624  * <p>The packet sent back from the server would have this form:</p>
43625  * <pre><code>
43626 {
43627     "success": true,
43628     "results": 2000, 
43629     "rows": [ // <b>*Note:</b> this must be an Array 
43630         { "id":  1, "name": "Bill", "occupation": "Gardener" },
43631         { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
43632         ...
43633         { "id": 25, "name":  "Sue", "occupation": "Botanist" }
43634     ]
43635 }
43636  * </code></pre>
43637  * <p><u>Paging with Local Data</u></p>
43638  * <p>Paging can also be accomplished with local data using extensions:</p>
43639  * <div class="mdetail-params"><ul>
43640  * <li><a href="http://extjs.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
43641  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
43642  * </ul></div>
43643  * @constructor Create a new PagingToolbar
43644  * @param {Object} config The config object
43645  * @xtype paging
43646  */
43647 (function() {
43648
43649 var T = Ext.Toolbar;
43650
43651 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
43652     /**
43653      * @cfg {Ext.data.Store} store
43654      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
43655      */
43656     /**
43657      * @cfg {Boolean} displayInfo
43658      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
43659      */
43660     /**
43661      * @cfg {Number} pageSize
43662      * The number of records to display per page (defaults to <tt>20</tt>)
43663      */
43664     pageSize : 20,
43665     /**
43666      * @cfg {Boolean} prependButtons
43667      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
43668      * Defaults to <tt>false</tt>.
43669      */
43670     /**
43671      * @cfg {String} displayMsg
43672      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
43673      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
43674      * that are replaced by the values for start, end and total respectively. These tokens should
43675      * be preserved when overriding this string if showing those values is desired.
43676      */
43677     displayMsg : 'Displaying {0} - {1} of {2}',
43678     /**
43679      * @cfg {String} emptyMsg
43680      * The message to display when no records are found (defaults to 'No data to display')
43681      */
43682     emptyMsg : 'No data to display',
43683     /**
43684      * @cfg {String} beforePageText
43685      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
43686      */
43687     beforePageText : 'Page',
43688     /**
43689      * @cfg {String} afterPageText
43690      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
43691      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
43692      * total pages. This token should be preserved when overriding this string if showing the
43693      * total page count is desired.
43694      */
43695     afterPageText : 'of {0}',
43696     /**
43697      * @cfg {String} firstText
43698      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
43699      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
43700      */
43701     firstText : 'First Page',
43702     /**
43703      * @cfg {String} prevText
43704      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
43705      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
43706      */
43707     prevText : 'Previous Page',
43708     /**
43709      * @cfg {String} nextText
43710      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
43711      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
43712      */
43713     nextText : 'Next Page',
43714     /**
43715      * @cfg {String} lastText
43716      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
43717      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
43718      */
43719     lastText : 'Last Page',
43720     /**
43721      * @cfg {String} refreshText
43722      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
43723      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
43724      */
43725     refreshText : 'Refresh',
43726
43727     /**
43728      * <p><b>Deprecated</b>. <code>paramNames</code> should be set in the <b>data store</b>
43729      * (see {@link Ext.data.Store#paramNames}).</p>
43730      * <br><p>Object mapping of parameter names used for load calls, initially set to:</p>
43731      * <pre>{start: 'start', limit: 'limit'}</pre>
43732      * @type Object
43733      * @property paramNames
43734      * @deprecated
43735      */
43736
43737     /**
43738      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
43739      * @type Number
43740      * @property pageSize
43741      */
43742
43743     /**
43744      * Indicator for the record position.  This property might be used to get the active page
43745      * number for example:<pre><code>
43746      * // t is reference to the paging toolbar instance
43747      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
43748      * </code></pre>
43749      * @type Number
43750      * @property cursor
43751      */
43752
43753     initComponent : function(){
43754         var pagingItems = [this.first = new T.Button({
43755             tooltip: this.firstText,
43756             overflowText: this.firstText,
43757             iconCls: 'x-tbar-page-first',
43758             disabled: true,
43759             handler: this.moveFirst,
43760             scope: this
43761         }), this.prev = new T.Button({
43762             tooltip: this.prevText,
43763             overflowText: this.prevText,
43764             iconCls: 'x-tbar-page-prev',
43765             disabled: true,
43766             handler: this.movePrevious,
43767             scope: this
43768         }), '-', this.beforePageText,
43769         this.inputItem = new Ext.form.NumberField({
43770             cls: 'x-tbar-page-number',
43771             allowDecimals: false,
43772             allowNegative: false,
43773             enableKeyEvents: true,
43774             selectOnFocus: true,
43775             listeners: {
43776                 scope: this,
43777                 keydown: this.onPagingKeyDown,
43778                 blur: this.onPagingBlur
43779             }
43780         }), this.afterTextItem = new T.TextItem({
43781             text: String.format(this.afterPageText, 1)
43782         }), '-', this.next = new T.Button({
43783             tooltip: this.nextText,
43784             overflowText: this.nextText,
43785             iconCls: 'x-tbar-page-next',
43786             disabled: true,
43787             handler: this.moveNext,
43788             scope: this
43789         }), this.last = new T.Button({
43790             tooltip: this.lastText,
43791             overflowText: this.lastText,
43792             iconCls: 'x-tbar-page-last',
43793             disabled: true,
43794             handler: this.moveLast,
43795             scope: this
43796         }), '-', this.refresh = new T.Button({
43797             tooltip: this.refreshText,
43798             overflowText: this.refreshText,
43799             iconCls: 'x-tbar-loading',
43800             handler: this.doRefresh,
43801             scope: this
43802         })];
43803
43804
43805         var userItems = this.items || this.buttons || [];
43806         if (this.prependButtons) {
43807             this.items = userItems.concat(pagingItems);
43808         }else{
43809             this.items = pagingItems.concat(userItems);
43810         }
43811         delete this.buttons;
43812         if(this.displayInfo){
43813             this.items.push('->');
43814             this.items.push(this.displayItem = new T.TextItem({}));
43815         }
43816         Ext.PagingToolbar.superclass.initComponent.call(this);
43817         this.addEvents(
43818             /**
43819              * @event change
43820              * Fires after the active page has been changed.
43821              * @param {Ext.PagingToolbar} this
43822              * @param {Object} pageData An object that has these properties:<ul>
43823              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
43824              * returned by the server</div></li>
43825              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
43826              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
43827              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
43828              * </ul>
43829              */
43830             'change',
43831             /**
43832              * @event beforechange
43833              * Fires just before the active page is changed.
43834              * Return false to prevent the active page from being changed.
43835              * @param {Ext.PagingToolbar} this
43836              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
43837              * loading the required page. This will contain:<ul>
43838              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
43839              * be retrieved from the server</div></li>
43840              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
43841              * </ul>
43842              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
43843              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
43844              * <p>Parameters may be added as required in the event handler.</p>
43845              */
43846             'beforechange'
43847         );
43848         this.on('afterlayout', this.onFirstLayout, this, {single: true});
43849         this.cursor = 0;
43850         this.bindStore(this.store, true);
43851     },
43852
43853     // private
43854     onFirstLayout : function(){
43855         if(this.dsLoaded){
43856             this.onLoad.apply(this, this.dsLoaded);
43857         }
43858     },
43859
43860     // private
43861     updateInfo : function(){
43862         if(this.displayItem){
43863             var count = this.store.getCount();
43864             var msg = count == 0 ?
43865                 this.emptyMsg :
43866                 String.format(
43867                     this.displayMsg,
43868                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
43869                 );
43870             this.displayItem.setText(msg);
43871         }
43872     },
43873
43874     // private
43875     onLoad : function(store, r, o){
43876         if(!this.rendered){
43877             this.dsLoaded = [store, r, o];
43878             return;
43879         }
43880         var p = this.getParams();
43881         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
43882         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
43883
43884         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
43885         this.inputItem.setValue(ap);
43886         this.first.setDisabled(ap == 1);
43887         this.prev.setDisabled(ap == 1);
43888         this.next.setDisabled(ap == ps);
43889         this.last.setDisabled(ap == ps);
43890         this.refresh.enable();
43891         this.updateInfo();
43892         this.fireEvent('change', this, d);
43893     },
43894
43895     // private
43896     getPageData : function(){
43897         var total = this.store.getTotalCount();
43898         return {
43899             total : total,
43900             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
43901             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
43902         };
43903     },
43904
43905     /**
43906      * Change the active page
43907      * @param {Integer} page The page to display
43908      */
43909     changePage : function(page){
43910         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
43911     },
43912
43913     // private
43914     onLoadError : function(){
43915         if(!this.rendered){
43916             return;
43917         }
43918         this.refresh.enable();
43919     },
43920
43921     // private
43922     readPage : function(d){
43923         var v = this.inputItem.getValue(), pageNum;
43924         if (!v || isNaN(pageNum = parseInt(v, 10))) {
43925             this.inputItem.setValue(d.activePage);
43926             return false;
43927         }
43928         return pageNum;
43929     },
43930
43931     onPagingFocus : function(){
43932         this.inputItem.select();
43933     },
43934
43935     //private
43936     onPagingBlur : function(e){
43937         this.inputItem.setValue(this.getPageData().activePage);
43938     },
43939
43940     // private
43941     onPagingKeyDown : function(field, e){
43942         var k = e.getKey(), d = this.getPageData(), pageNum;
43943         if (k == e.RETURN) {
43944             e.stopEvent();
43945             pageNum = this.readPage(d);
43946             if(pageNum !== false){
43947                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
43948                 this.doLoad(pageNum * this.pageSize);
43949             }
43950         }else if (k == e.HOME || k == e.END){
43951             e.stopEvent();
43952             pageNum = k == e.HOME ? 1 : d.pages;
43953             field.setValue(pageNum);
43954         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
43955             e.stopEvent();
43956             if((pageNum = this.readPage(d))){
43957                 var increment = e.shiftKey ? 10 : 1;
43958                 if(k == e.DOWN || k == e.PAGEDOWN){
43959                     increment *= -1;
43960                 }
43961                 pageNum += increment;
43962                 if(pageNum >= 1 & pageNum <= d.pages){
43963                     field.setValue(pageNum);
43964                 }
43965             }
43966         }
43967     },
43968
43969     // private
43970     getParams : function(){
43971         //retain backwards compat, allow params on the toolbar itself, if they exist.
43972         return this.paramNames || this.store.paramNames;
43973     },
43974
43975     // private
43976     getParams : function(){
43977         //retain backwards compat, allow params on the toolbar itself, if they exist.
43978         return this.paramNames || this.store.paramNames;
43979     },
43980
43981     // private
43982     beforeLoad : function(){
43983         if(this.rendered && this.refresh){
43984             this.refresh.disable();
43985         }
43986     },
43987
43988     // private
43989     doLoad : function(start){
43990         var o = {}, pn = this.getParams();
43991         o[pn.start] = start;
43992         o[pn.limit] = this.pageSize;
43993         if(this.fireEvent('beforechange', this, o) !== false){
43994             this.store.load({params:o});
43995         }
43996     },
43997
43998     /**
43999      * Move to the first page, has the same effect as clicking the 'first' button.
44000      */
44001     moveFirst : function(){
44002         this.doLoad(0);
44003     },
44004
44005     /**
44006      * Move to the previous page, has the same effect as clicking the 'previous' button.
44007      */
44008     movePrevious : function(){
44009         this.doLoad(Math.max(0, this.cursor-this.pageSize));
44010     },
44011
44012     /**
44013      * Move to the next page, has the same effect as clicking the 'next' button.
44014      */
44015     moveNext : function(){
44016         this.doLoad(this.cursor+this.pageSize);
44017     },
44018
44019     /**
44020      * Move to the last page, has the same effect as clicking the 'last' button.
44021      */
44022     moveLast : function(){
44023         var total = this.store.getTotalCount(),
44024             extra = total % this.pageSize;
44025
44026         this.doLoad(extra ? (total - extra) : total - this.pageSize);
44027     },
44028
44029     /**
44030      * Refresh the current page, has the same effect as clicking the 'refresh' button.
44031      */
44032     doRefresh : function(){
44033         this.doLoad(this.cursor);
44034     },
44035
44036     /**
44037      * Binds the paging toolbar to the specified {@link Ext.data.Store}
44038      * @param {Store} store The store to bind to this toolbar
44039      * @param {Boolean} initial (Optional) true to not remove listeners
44040      */
44041     bindStore : function(store, initial){
44042         var doLoad;
44043         if(!initial && this.store){
44044             if(store !== this.store && this.store.autoDestroy){
44045                 this.store.destroy();
44046             }else{
44047                 this.store.un('beforeload', this.beforeLoad, this);
44048                 this.store.un('load', this.onLoad, this);
44049                 this.store.un('exception', this.onLoadError, this);
44050             }
44051             if(!store){
44052                 this.store = null;
44053             }
44054         }
44055         if(store){
44056             store = Ext.StoreMgr.lookup(store);
44057             store.on({
44058                 scope: this,
44059                 beforeload: this.beforeLoad,
44060                 load: this.onLoad,
44061                 exception: this.onLoadError
44062             });
44063             doLoad = true;
44064         }
44065         this.store = store;
44066         if(doLoad){
44067             this.onLoad(store, null, {});
44068         }
44069     },
44070
44071     /**
44072      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
44073      * @param {Ext.data.Store} store The data store to unbind
44074      */
44075     unbind : function(store){
44076         this.bindStore(null);
44077     },
44078
44079     /**
44080      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
44081      * @param {Ext.data.Store} store The data store to bind
44082      */
44083     bind : function(store){
44084         this.bindStore(store);
44085     },
44086
44087     // private
44088     onDestroy : function(){
44089         this.bindStore(null);
44090         Ext.PagingToolbar.superclass.onDestroy.call(this);
44091     }
44092 });
44093
44094 })();
44095 Ext.reg('paging', Ext.PagingToolbar);/**\r
44096  * @class Ext.History\r
44097  * @extends Ext.util.Observable\r
44098  * History management component that allows you to register arbitrary tokens that signify application\r
44099  * history state on navigation actions.  You can then handle the history {@link #change} event in order\r
44100  * to reset your application UI to the appropriate state when the user navigates forward or backward through\r
44101  * the browser history stack.\r
44102  * @singleton\r
44103  */\r
44104 Ext.History = (function () {\r
44105     var iframe, hiddenField;\r
44106     var ready = false;\r
44107     var currentToken;\r
44108 \r
44109     function getHash() {\r
44110         var href = top.location.href, i = href.indexOf("#");\r
44111         return i >= 0 ? href.substr(i + 1) : null;\r
44112     }\r
44113 \r
44114     function doSave() {\r
44115         hiddenField.value = currentToken;\r
44116     }\r
44117 \r
44118     function handleStateChange(token) {\r
44119         currentToken = token;\r
44120         Ext.History.fireEvent('change', token);\r
44121     }\r
44122 \r
44123     function updateIFrame (token) {\r
44124         var html = ['<html><body><div id="state">',token,'</div></body></html>'].join('');\r
44125         try {\r
44126             var doc = iframe.contentWindow.document;\r
44127             doc.open();\r
44128             doc.write(html);\r
44129             doc.close();\r
44130             return true;\r
44131         } catch (e) {\r
44132             return false;\r
44133         }\r
44134     }\r
44135 \r
44136     function checkIFrame() {\r
44137         if (!iframe.contentWindow || !iframe.contentWindow.document) {\r
44138             setTimeout(checkIFrame, 10);\r
44139             return;\r
44140         }\r
44141 \r
44142         var doc = iframe.contentWindow.document;\r
44143         var elem = doc.getElementById("state");\r
44144         var token = elem ? elem.innerText : null;\r
44145 \r
44146         var hash = getHash();\r
44147 \r
44148         setInterval(function () {\r
44149 \r
44150             doc = iframe.contentWindow.document;\r
44151             elem = doc.getElementById("state");\r
44152 \r
44153             var newtoken = elem ? elem.innerText : null;\r
44154 \r
44155             var newHash = getHash();\r
44156 \r
44157             if (newtoken !== token) {\r
44158                 token = newtoken;\r
44159                 handleStateChange(token);\r
44160                 top.location.hash = token;\r
44161                 hash = token;\r
44162                 doSave();\r
44163             } else if (newHash !== hash) {\r
44164                 hash = newHash;\r
44165                 updateIFrame(newHash);\r
44166             }\r
44167 \r
44168         }, 50);\r
44169 \r
44170         ready = true;\r
44171 \r
44172         Ext.History.fireEvent('ready', Ext.History);\r
44173     }\r
44174 \r
44175     function startUp() {\r
44176         currentToken = hiddenField.value ? hiddenField.value : getHash();\r
44177 \r
44178         if (Ext.isIE) {\r
44179             checkIFrame();\r
44180         } else {\r
44181             var hash = getHash();\r
44182             setInterval(function () {\r
44183                 var newHash = getHash();\r
44184                 if (newHash !== hash) {\r
44185                     hash = newHash;\r
44186                     handleStateChange(hash);\r
44187                     doSave();\r
44188                 }\r
44189             }, 50);\r
44190             ready = true;\r
44191             Ext.History.fireEvent('ready', Ext.History);\r
44192         }\r
44193     }\r
44194 \r
44195     return {\r
44196         /**\r
44197          * The id of the hidden field required for storing the current history token.\r
44198          * @type String\r
44199          * @property\r
44200          */\r
44201         fieldId: 'x-history-field',\r
44202         /**\r
44203          * The id of the iframe required by IE to manage the history stack.\r
44204          * @type String\r
44205          * @property\r
44206          */\r
44207         iframeId: 'x-history-frame',\r
44208         \r
44209         events:{},\r
44210 \r
44211         /**\r
44212          * Initialize the global History instance.\r
44213          * @param {Boolean} onReady (optional) A callback function that will be called once the history\r
44214          * component is fully initialized.\r
44215          * @param {Object} scope (optional) The callback scope\r
44216          */\r
44217         init: function (onReady, scope) {\r
44218             if(ready) {\r
44219                 Ext.callback(onReady, scope, [this]);\r
44220                 return;\r
44221             }\r
44222             if(!Ext.isReady){\r
44223                 Ext.onReady(function(){\r
44224                     Ext.History.init(onReady, scope);\r
44225                 });\r
44226                 return;\r
44227             }\r
44228             hiddenField = Ext.getDom(Ext.History.fieldId);\r
44229             if (Ext.isIE) {\r
44230                 iframe = Ext.getDom(Ext.History.iframeId);\r
44231             }\r
44232             this.addEvents('ready', 'change');\r
44233             if(onReady){\r
44234                 this.on('ready', onReady, scope, {single:true});\r
44235             }\r
44236             startUp();\r
44237         },\r
44238 \r
44239         /**\r
44240          * Add a new token to the history stack. This can be any arbitrary value, although it would\r
44241          * commonly be the concatenation of a component id and another id marking the specifc history\r
44242          * state of that component.  Example usage:\r
44243          * <pre><code>\r
44244 // Handle tab changes on a TabPanel\r
44245 tabPanel.on('tabchange', function(tabPanel, tab){\r
44246     Ext.History.add(tabPanel.id + ':' + tab.id);\r
44247 });\r
44248 </code></pre>\r
44249          * @param {String} token The value that defines a particular application-specific history state\r
44250          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token\r
44251          * it will not save a new history step. Set to false if the same state can be saved more than once\r
44252          * at the same history stack location (defaults to true).\r
44253          */\r
44254         add: function (token, preventDup) {\r
44255             if(preventDup !== false){\r
44256                 if(this.getToken() == token){\r
44257                     return true;\r
44258                 }\r
44259             }\r
44260             if (Ext.isIE) {\r
44261                 return updateIFrame(token);\r
44262             } else {\r
44263                 top.location.hash = token;\r
44264                 return true;\r
44265             }\r
44266         },\r
44267 \r
44268         /**\r
44269          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).\r
44270          */\r
44271         back: function(){\r
44272             history.go(-1);\r
44273         },\r
44274 \r
44275         /**\r
44276          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).\r
44277          */\r
44278         forward: function(){\r
44279             history.go(1);\r
44280         },\r
44281 \r
44282         /**\r
44283          * Retrieves the currently-active history token.\r
44284          * @return {String} The token\r
44285          */\r
44286         getToken: function() {\r
44287             return ready ? currentToken : getHash();\r
44288         }\r
44289     };\r
44290 })();\r
44291 Ext.apply(Ext.History, new Ext.util.Observable());/**\r
44292  * @class Ext.Tip\r
44293  * @extends Ext.Panel\r
44294  * @xtype tip\r
44295  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and\r
44296  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned\r
44297  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.\r
44298  * @constructor\r
44299  * Create a new Tip\r
44300  * @param {Object} config The configuration options\r
44301  */\r
44302 Ext.Tip = Ext.extend(Ext.Panel, {\r
44303     /**\r
44304      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).\r
44305      */\r
44306     /**\r
44307      * @cfg {Number} width\r
44308      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of\r
44309      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.\r
44310      */\r
44311     /**\r
44312      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).\r
44313      */\r
44314     minWidth : 40,\r
44315     /**\r
44316      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.\r
44317      */\r
44318     maxWidth : 300,\r
44319     /**\r
44320      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"\r
44321      * for bottom-right shadow (defaults to "sides").\r
44322      */\r
44323     shadow : "sides",\r
44324     /**\r
44325      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value\r
44326      * for this tip relative to its element of origin (defaults to "tl-bl?").\r
44327      */\r
44328     defaultAlign : "tl-bl?",\r
44329     autoRender: true,\r
44330     quickShowInterval : 250,\r
44331 \r
44332     // private panel overrides\r
44333     frame:true,\r
44334     hidden:true,\r
44335     baseCls: 'x-tip',\r
44336     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},\r
44337     autoHeight:true,\r
44338 \r
44339     closeAction: 'hide',\r
44340 \r
44341     // private\r
44342     initComponent : function(){\r
44343         Ext.Tip.superclass.initComponent.call(this);\r
44344         if(this.closable && !this.title){\r
44345             this.elements += ',header';\r
44346         }\r
44347     },\r
44348 \r
44349     // private\r
44350     afterRender : function(){\r
44351         Ext.Tip.superclass.afterRender.call(this);\r
44352         if(this.closable){\r
44353             this.addTool({\r
44354                 id: 'close',\r
44355                 handler: this[this.closeAction],\r
44356                 scope: this\r
44357             });\r
44358         }\r
44359     },\r
44360 \r
44361     /**\r
44362      * Shows this tip at the specified XY position.  Example usage:\r
44363      * <pre><code>\r
44364 // Show the tip at x:50 and y:100\r
44365 tip.showAt([50,100]);\r
44366 </code></pre>\r
44367      * @param {Array} xy An array containing the x and y coordinates\r
44368      */\r
44369     showAt : function(xy){\r
44370         Ext.Tip.superclass.show.call(this);\r
44371         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){\r
44372             this.doAutoWidth();\r
44373         }\r
44374         if(this.constrainPosition){\r
44375             xy = this.el.adjustForConstraints(xy);\r
44376         }\r
44377         this.setPagePosition(xy[0], xy[1]);\r
44378     },\r
44379 \r
44380     // protected\r
44381     doAutoWidth : function(){\r
44382         var bw = this.body.getTextWidth();\r
44383         if(this.title){\r
44384             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));\r
44385         }\r
44386         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr");\r
44387         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));\r
44388         \r
44389         // IE7 repaint bug on initial show\r
44390         if(Ext.isIE7 && !this.repainted){\r
44391             this.el.repaint();\r
44392             this.repainted = true;\r
44393         }\r
44394     },\r
44395 \r
44396     /**\r
44397      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}\r
44398      * anchor position value.  Example usage:\r
44399      * <pre><code>\r
44400 // Show the tip at the default position ('tl-br?')\r
44401 tip.showBy('my-el');\r
44402 \r
44403 // Show the tip's top-left corner anchored to the element's top-right corner\r
44404 tip.showBy('my-el', 'tl-tr');\r
44405 </code></pre>\r
44406      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to\r
44407      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or\r
44408      * {@link #defaultAlign} if specified).\r
44409      */\r
44410     showBy : function(el, pos){\r
44411         if(!this.rendered){\r
44412             this.render(Ext.getBody());\r
44413         }\r
44414         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));\r
44415     },\r
44416 \r
44417     initDraggable : function(){\r
44418         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);\r
44419         this.header.addClass('x-tip-draggable');\r
44420     }\r
44421 });\r
44422 \r
44423 Ext.reg('tip', Ext.Tip);\r
44424 \r
44425 // private - custom Tip DD implementation\r
44426 Ext.Tip.DD = function(tip, config){\r
44427     Ext.apply(this, config);\r
44428     this.tip = tip;\r
44429     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);\r
44430     this.setHandleElId(tip.header.id);\r
44431     this.scroll = false;\r
44432 };\r
44433 \r
44434 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {\r
44435     moveOnly:true,\r
44436     scroll:false,\r
44437     headerOffsets:[100, 25],\r
44438     startDrag : function(){\r
44439         this.tip.el.disableShadow();\r
44440     },\r
44441     endDrag : function(e){\r
44442         this.tip.el.enableShadow(true);\r
44443     }\r
44444 });/**\r
44445  * @class Ext.ToolTip\r
44446  * @extends Ext.Tip\r
44447  * A standard tooltip implementation for providing additional information when hovering over a target element.\r
44448  * @xtype tooltip\r
44449  * @constructor\r
44450  * Create a new Tooltip\r
44451  * @param {Object} config The configuration options\r
44452  */\r
44453 Ext.ToolTip = Ext.extend(Ext.Tip, {\r
44454     /**\r
44455      * When a Tooltip is configured with the <code>{@link #delegate}</code>\r
44456      * option to cause selected child elements of the <code>{@link #target}</code>\r
44457      * Element to each trigger a seperate show event, this property is set to\r
44458      * the DOM element which triggered the show.\r
44459      * @type DOMElement\r
44460      * @property triggerElement\r
44461      */\r
44462     /**\r
44463      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor\r
44464      * for mouseover events to trigger showing this ToolTip.\r
44465      */\r
44466     /**\r
44467      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the\r
44468      * mouse exits the target element or after the <code>{@link #dismissDelay}</code>\r
44469      * has expired if set (defaults to true).  If <code>{@link closable} = true</code>\r
44470      * a close tool button will be rendered into the tooltip header.\r
44471      */\r
44472     /**\r
44473      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays\r
44474      * after the mouse enters the target element (defaults to 500)\r
44475      */\r
44476     showDelay : 500,\r
44477     /**\r
44478      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the\r
44479      * target element but before the tooltip actually hides (defaults to 200).\r
44480      * Set to 0 for the tooltip to hide immediately.\r
44481      */\r
44482     hideDelay : 200,\r
44483     /**\r
44484      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip\r
44485      * automatically hides (defaults to 5000). To disable automatic hiding, set\r
44486      * dismissDelay = 0.\r
44487      */\r
44488     dismissDelay : 5000,\r
44489     /**\r
44490      * @cfg {Array} mouseOffset An XY offset from the mouse position where the\r
44491      * tooltip should be shown (defaults to [15,18]).\r
44492      */\r
44493     /**\r
44494      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it\r
44495      * moves over the target element (defaults to false).\r
44496      */\r
44497     trackMouse : false,\r
44498     /**\r
44499      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target\r
44500      * element, false to anchor it relative to the mouse coordinates (defaults\r
44501      * to true).  When <code>anchorToTarget</code> is true, use\r
44502      * <code>{@link #defaultAlign}</code> to control tooltip alignment to the\r
44503      * target element.  When <code>anchorToTarget</code> is false, use\r
44504      * <code>{@link #anchorPosition}</code> instead to control alignment.\r
44505      */\r
44506     anchorToTarget : true,\r
44507     /**\r
44508      * @cfg {Number} anchorOffset A numeric pixel value used to offset the\r
44509      * default position of the anchor arrow (defaults to 0).  When the anchor\r
44510      * position is on the top or bottom of the tooltip, <code>anchorOffset</code>\r
44511      * will be used as a horizontal offset.  Likewise, when the anchor position\r
44512      * is on the left or right side, <code>anchorOffset</code> will be used as\r
44513      * a vertical offset.\r
44514      */\r
44515     anchorOffset : 0,\r
44516     /**\r
44517      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}\r
44518      * selector which allows selection of individual elements within the\r
44519      * <code>{@link #target}</code> element to trigger showing and hiding the\r
44520      * ToolTip as the mouse moves within the target.</p>\r
44521      * <p>When specified, the child element of the target which caused a show\r
44522      * event is placed into the <code>{@link #triggerElement}</code> property\r
44523      * before the ToolTip is shown.</p>\r
44524      * <p>This may be useful when a Component has regular, repeating elements\r
44525      * in it, each of which need a Tooltip which contains information specific\r
44526      * to that element. For example:</p><pre><code>\r
44527 var myGrid = new Ext.grid.gridPanel(gridConfig);\r
44528 myGrid.on('render', function(grid) {\r
44529     var store = grid.getStore();  // Capture the Store.\r
44530     var view = grid.getView();    // Capture the GridView.\r
44531     myGrid.tip = new Ext.ToolTip({\r
44532         target: view.mainBody,    // The overall target element.\r
44533         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.\r
44534         trackMouse: true,         // Moving within the row should not hide the tip.\r
44535         renderTo: document.body,  // Render immediately so that tip.body can be\r
44536                                   //  referenced prior to the first show.\r
44537         listeners: {              // Change content dynamically depending on which element\r
44538                                   //  triggered the show.\r
44539             beforeshow: function updateTipBody(tip) {\r
44540                 var rowIndex = view.findRowIndex(tip.triggerElement);\r
44541                 tip.body.dom.innerHTML = 'Over Record ID ' + store.getAt(rowIndex).id;\r
44542             }\r
44543         }\r
44544     });\r
44545 });\r
44546      *</code></pre>\r
44547      */\r
44548 \r
44549     // private\r
44550     targetCounter : 0,\r
44551 \r
44552     constrainPosition : false,\r
44553 \r
44554     // private\r
44555     initComponent : function(){\r
44556         Ext.ToolTip.superclass.initComponent.call(this);\r
44557         this.lastActive = new Date();\r
44558         this.initTarget(this.target);\r
44559         this.origAnchor = this.anchor;\r
44560     },\r
44561 \r
44562     // private\r
44563     onRender : function(ct, position){\r
44564         Ext.ToolTip.superclass.onRender.call(this, ct, position);\r
44565         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();\r
44566         this.anchorEl = this.el.createChild({\r
44567             cls: 'x-tip-anchor ' + this.anchorCls\r
44568         });\r
44569     },\r
44570 \r
44571     // private\r
44572     afterRender : function(){\r
44573         Ext.ToolTip.superclass.afterRender.call(this);\r
44574         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);\r
44575     },\r
44576 \r
44577     /**\r
44578      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.\r
44579      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to\r
44580      */\r
44581     initTarget : function(target){\r
44582         var t;\r
44583         if((t = Ext.get(target))){\r
44584             if(this.target){\r
44585                 var tg = Ext.get(this.target);\r
44586                 this.mun(tg, 'mouseover', this.onTargetOver, this);\r
44587                 this.mun(tg, 'mouseout', this.onTargetOut, this);\r
44588                 this.mun(tg, 'mousemove', this.onMouseMove, this);\r
44589             }\r
44590             this.mon(t, {\r
44591                 mouseover: this.onTargetOver,\r
44592                 mouseout: this.onTargetOut,\r
44593                 mousemove: this.onMouseMove,\r
44594                 scope: this\r
44595             });\r
44596             this.target = t;\r
44597         }\r
44598         if(this.anchor){\r
44599             this.anchorTarget = this.target;\r
44600         }\r
44601     },\r
44602 \r
44603     // private\r
44604     onMouseMove : function(e){\r
44605         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;\r
44606         if (t) {\r
44607             this.targetXY = e.getXY();\r
44608             if (t === this.triggerElement) {\r
44609                 if(!this.hidden && this.trackMouse){\r
44610                     this.setPagePosition(this.getTargetXY());\r
44611                 }\r
44612             } else {\r
44613                 this.hide();\r
44614                 this.lastActive = new Date(0);\r
44615                 this.onTargetOver(e);\r
44616             }\r
44617         } else if (!this.closable && this.isVisible()) {\r
44618             this.hide();\r
44619         }\r
44620     },\r
44621 \r
44622     // private\r
44623     getTargetXY : function(){\r
44624         if(this.delegate){\r
44625             this.anchorTarget = this.triggerElement;\r
44626         }\r
44627         if(this.anchor){\r
44628             this.targetCounter++;\r
44629             var offsets = this.getOffsets();\r
44630             var xy = (this.anchorToTarget && !this.trackMouse) ?\r
44631                 this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) :\r
44632                 this.targetXY;\r
44633 \r
44634             var dw = Ext.lib.Dom.getViewWidth()-5;\r
44635             var dh = Ext.lib.Dom.getViewHeight()-5;\r
44636             var scrollX = (document.documentElement.scrollLeft || document.body.scrollLeft || 0)+5;\r
44637             var scrollY = (document.documentElement.scrollTop || document.body.scrollTop || 0)+5;\r
44638 \r
44639             var axy = [xy[0] + offsets[0], xy[1] + offsets[1]];\r
44640             var sz = this.getSize();\r
44641             this.anchorEl.removeClass(this.anchorCls);\r
44642 \r
44643             if(this.targetCounter < 2){\r
44644                 if(axy[0] < scrollX){\r
44645                     if(this.anchorToTarget){\r
44646                         this.defaultAlign = 'l-r';\r
44647                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
44648                     }\r
44649                     this.anchor = 'left';\r
44650                     return this.getTargetXY();\r
44651                 }\r
44652                 if(axy[0]+sz.width > dw){\r
44653                     if(this.anchorToTarget){\r
44654                         this.defaultAlign = 'r-l';\r
44655                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
44656                     }\r
44657                     this.anchor = 'right';\r
44658                     return this.getTargetXY();\r
44659                 }\r
44660                 if(axy[1] < scrollY){\r
44661                     if(this.anchorToTarget){\r
44662                         this.defaultAlign = 't-b';\r
44663                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
44664                     }\r
44665                     this.anchor = 'top';\r
44666                     return this.getTargetXY();\r
44667                 }\r
44668                 if(axy[1]+sz.height > dh){\r
44669                     if(this.anchorToTarget){\r
44670                         this.defaultAlign = 'b-t';\r
44671                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
44672                     }\r
44673                     this.anchor = 'bottom';\r
44674                     return this.getTargetXY();\r
44675                 }\r
44676             }\r
44677 \r
44678             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();\r
44679             this.anchorEl.addClass(this.anchorCls);\r
44680             this.targetCounter = 0;\r
44681             return axy;\r
44682         }else{\r
44683             var mouseOffset = this.getMouseOffset();\r
44684             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];\r
44685         }\r
44686     },\r
44687 \r
44688     getMouseOffset : function(){\r
44689         var offset = this.anchor ? [0,0] : [15,18];\r
44690         if(this.mouseOffset){\r
44691             offset[0] += this.mouseOffset[0];\r
44692             offset[1] += this.mouseOffset[1];\r
44693         }\r
44694         return offset;\r
44695     },\r
44696 \r
44697     // private\r
44698     getAnchorPosition : function(){\r
44699         if(this.anchor){\r
44700             this.tipAnchor = this.anchor.charAt(0);\r
44701         }else{\r
44702             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
44703             if(!m){\r
44704                throw 'AnchorTip.defaultAlign is invalid';\r
44705             }\r
44706             this.tipAnchor = m[1].charAt(0);\r
44707         }\r
44708 \r
44709         switch(this.tipAnchor){\r
44710             case 't': return 'top';\r
44711             case 'b': return 'bottom';\r
44712             case 'r': return 'right';\r
44713         }\r
44714         return 'left';\r
44715     },\r
44716 \r
44717     // private\r
44718     getAnchorAlign : function(){\r
44719         switch(this.anchor){\r
44720             case 'top'  : return 'tl-bl';\r
44721             case 'left' : return 'tl-tr';\r
44722             case 'right': return 'tr-tl';\r
44723             default     : return 'bl-tl';\r
44724         }\r
44725     },\r
44726 \r
44727     // private\r
44728     getOffsets : function(){\r
44729         var offsets, ap = this.getAnchorPosition().charAt(0);\r
44730         if(this.anchorToTarget && !this.trackMouse){\r
44731             switch(ap){\r
44732                 case 't':\r
44733                     offsets = [0, 9];\r
44734                     break;\r
44735                 case 'b':\r
44736                     offsets = [0, -13];\r
44737                     break;\r
44738                 case 'r':\r
44739                     offsets = [-13, 0];\r
44740                     break;\r
44741                 default:\r
44742                     offsets = [9, 0];\r
44743                     break;\r
44744             }\r
44745         }else{\r
44746             switch(ap){\r
44747                 case 't':\r
44748                     offsets = [-15-this.anchorOffset, 30];\r
44749                     break;\r
44750                 case 'b':\r
44751                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];\r
44752                     break;\r
44753                 case 'r':\r
44754                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];\r
44755                     break;\r
44756                 default:\r
44757                     offsets = [25, -13-this.anchorOffset];\r
44758                     break;\r
44759             }\r
44760         }\r
44761         var mouseOffset = this.getMouseOffset();\r
44762         offsets[0] += mouseOffset[0];\r
44763         offsets[1] += mouseOffset[1];\r
44764 \r
44765         return offsets;\r
44766     },\r
44767 \r
44768     // private\r
44769     onTargetOver : function(e){\r
44770         if(this.disabled || e.within(this.target.dom, true)){\r
44771             return;\r
44772         }\r
44773         var t = e.getTarget(this.delegate);\r
44774         if (t) {\r
44775             this.triggerElement = t;\r
44776             this.clearTimer('hide');\r
44777             this.targetXY = e.getXY();\r
44778             this.delayShow();\r
44779         }\r
44780     },\r
44781 \r
44782     // private\r
44783     delayShow : function(){\r
44784         if(this.hidden && !this.showTimer){\r
44785             if(this.lastActive.getElapsed() < this.quickShowInterval){\r
44786                 this.show();\r
44787             }else{\r
44788                 this.showTimer = this.show.defer(this.showDelay, this);\r
44789             }\r
44790         }else if(!this.hidden && this.autoHide !== false){\r
44791             this.show();\r
44792         }\r
44793     },\r
44794 \r
44795     // private\r
44796     onTargetOut : function(e){\r
44797         if(this.disabled || e.within(this.target.dom, true)){\r
44798             return;\r
44799         }\r
44800         this.clearTimer('show');\r
44801         if(this.autoHide !== false){\r
44802             this.delayHide();\r
44803         }\r
44804     },\r
44805 \r
44806     // private\r
44807     delayHide : function(){\r
44808         if(!this.hidden && !this.hideTimer){\r
44809             this.hideTimer = this.hide.defer(this.hideDelay, this);\r
44810         }\r
44811     },\r
44812 \r
44813     /**\r
44814      * Hides this tooltip if visible.\r
44815      */\r
44816     hide: function(){\r
44817         this.clearTimer('dismiss');\r
44818         this.lastActive = new Date();\r
44819         if(this.anchorEl){\r
44820             this.anchorEl.hide();\r
44821         }\r
44822         Ext.ToolTip.superclass.hide.call(this);\r
44823         delete this.triggerElement;\r
44824     },\r
44825 \r
44826     /**\r
44827      * Shows this tooltip at the current event target XY position.\r
44828      */\r
44829     show : function(){\r
44830         if(this.anchor){\r
44831             // pre-show it off screen so that the el will have dimensions\r
44832             // for positioning calcs when getting xy next\r
44833             this.showAt([-1000,-1000]);\r
44834             this.origConstrainPosition = this.constrainPosition;\r
44835             this.constrainPosition = false;\r
44836             this.anchor = this.origAnchor;\r
44837         }\r
44838         this.showAt(this.getTargetXY());\r
44839 \r
44840         if(this.anchor){\r
44841             this.syncAnchor();\r
44842             this.anchorEl.show();\r
44843             this.constrainPosition = this.origConstrainPosition;\r
44844         }else{\r
44845             this.anchorEl.hide();\r
44846         }\r
44847     },\r
44848 \r
44849     // inherit docs\r
44850     showAt : function(xy){\r
44851         this.lastActive = new Date();\r
44852         this.clearTimers();\r
44853         Ext.ToolTip.superclass.showAt.call(this, xy);\r
44854         if(this.dismissDelay && this.autoHide !== false){\r
44855             this.dismissTimer = this.hide.defer(this.dismissDelay, this);\r
44856         }\r
44857         if(this.anchor && !this.anchorEl.isVisible()){\r
44858             this.syncAnchor();\r
44859             this.anchorEl.show();\r
44860         }\r
44861     },\r
44862 \r
44863     // private\r
44864     syncAnchor : function(){\r
44865         var anchorPos, targetPos, offset;\r
44866         switch(this.tipAnchor.charAt(0)){\r
44867             case 't':\r
44868                 anchorPos = 'b';\r
44869                 targetPos = 'tl';\r
44870                 offset = [20+this.anchorOffset, 2];\r
44871                 break;\r
44872             case 'r':\r
44873                 anchorPos = 'l';\r
44874                 targetPos = 'tr';\r
44875                 offset = [-2, 11+this.anchorOffset];\r
44876                 break;\r
44877             case 'b':\r
44878                 anchorPos = 't';\r
44879                 targetPos = 'bl';\r
44880                 offset = [20+this.anchorOffset, -2];\r
44881                 break;\r
44882             default:\r
44883                 anchorPos = 'r';\r
44884                 targetPos = 'tl';\r
44885                 offset = [2, 11+this.anchorOffset];\r
44886                 break;\r
44887         }\r
44888         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);\r
44889     },\r
44890 \r
44891     // private\r
44892     setPagePosition : function(x, y){\r
44893         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);\r
44894         if(this.anchor){\r
44895             this.syncAnchor();\r
44896         }\r
44897     },\r
44898 \r
44899     // private\r
44900     clearTimer : function(name){\r
44901         name = name + 'Timer';\r
44902         clearTimeout(this[name]);\r
44903         delete this[name];\r
44904     },\r
44905 \r
44906     // private\r
44907     clearTimers : function(){\r
44908         this.clearTimer('show');\r
44909         this.clearTimer('dismiss');\r
44910         this.clearTimer('hide');\r
44911     },\r
44912 \r
44913     // private\r
44914     onShow : function(){\r
44915         Ext.ToolTip.superclass.onShow.call(this);\r
44916         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);\r
44917     },\r
44918 \r
44919     // private\r
44920     onHide : function(){\r
44921         Ext.ToolTip.superclass.onHide.call(this);\r
44922         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
44923     },\r
44924 \r
44925     // private\r
44926     onDocMouseDown : function(e){\r
44927         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){\r
44928             this.disable();\r
44929             this.enable.defer(100, this);\r
44930         }\r
44931     },\r
44932 \r
44933     // private\r
44934     onDisable : function(){\r
44935         this.clearTimers();\r
44936         this.hide();\r
44937     },\r
44938 \r
44939     // private\r
44940     adjustPosition : function(x, y){\r
44941         if(this.contstrainPosition){\r
44942             var ay = this.targetXY[1], h = this.getSize().height;\r
44943             if(y <= ay && (y+h) >= ay){\r
44944                 y = ay-h-5;\r
44945             }\r
44946         }\r
44947         return {x : x, y: y};\r
44948     },\r
44949 \r
44950     // private\r
44951     onDestroy : function(){\r
44952         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
44953         Ext.ToolTip.superclass.onDestroy.call(this);\r
44954     }\r
44955 });\r
44956 \r
44957 Ext.reg('tooltip', Ext.ToolTip);/**\r
44958  * @class Ext.QuickTip\r
44959  * @extends Ext.ToolTip\r
44960  * @xtype quicktip\r
44961  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global\r
44962  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.\r
44963  * @constructor\r
44964  * Create a new Tip\r
44965  * @param {Object} config The configuration options\r
44966  */\r
44967 Ext.QuickTip = Ext.extend(Ext.ToolTip, {\r
44968     /**\r
44969      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).\r
44970      */\r
44971     /**\r
44972      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).\r
44973      */\r
44974     interceptTitles : false,\r
44975 \r
44976     // private\r
44977     tagConfig : {\r
44978         namespace : "ext",\r
44979         attribute : "qtip",\r
44980         width : "qwidth",\r
44981         target : "target",\r
44982         title : "qtitle",\r
44983         hide : "hide",\r
44984         cls : "qclass",\r
44985         align : "qalign",\r
44986         anchor : "anchor"\r
44987     },\r
44988 \r
44989     // private\r
44990     initComponent : function(){\r
44991         this.target = this.target || Ext.getDoc();\r
44992         this.targets = this.targets || {};\r
44993         Ext.QuickTip.superclass.initComponent.call(this);\r
44994     },\r
44995 \r
44996     /**\r
44997      * Configures a new quick tip instance and assigns it to a target element.  The following config values are\r
44998      * supported (for example usage, see the {@link Ext.QuickTips} class header):\r
44999      * <div class="mdetail-params"><ul>\r
45000      * <li>autoHide</li>\r
45001      * <li>cls</li>\r
45002      * <li>dismissDelay (overrides the singleton value)</li>\r
45003      * <li>target (required)</li>\r
45004      * <li>text (required)</li>\r
45005      * <li>title</li>\r
45006      * <li>width</li></ul></div>\r
45007      * @param {Object} config The config object\r
45008      */\r
45009     register : function(config){\r
45010         var cs = Ext.isArray(config) ? config : arguments;\r
45011         for(var i = 0, len = cs.length; i < len; i++){\r
45012             var c = cs[i];\r
45013             var target = c.target;\r
45014             if(target){\r
45015                 if(Ext.isArray(target)){\r
45016                     for(var j = 0, jlen = target.length; j < jlen; j++){\r
45017                         this.targets[Ext.id(target[j])] = c;\r
45018                     }\r
45019                 } else{\r
45020                     this.targets[Ext.id(target)] = c;\r
45021                 }\r
45022             }\r
45023         }\r
45024     },\r
45025 \r
45026     /**\r
45027      * Removes this quick tip from its element and destroys it.\r
45028      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
45029      */\r
45030     unregister : function(el){\r
45031         delete this.targets[Ext.id(el)];\r
45032     },\r
45033     \r
45034     /**\r
45035      * Hides a visible tip or cancels an impending show for a particular element.\r
45036      * @param {String/HTMLElement/Element} el The element that is the target of the tip.\r
45037      */\r
45038     cancelShow: function(el){\r
45039         var at = this.activeTarget;\r
45040         el = Ext.get(el).dom;\r
45041         if(this.isVisible()){\r
45042             if(at && at.el == el){\r
45043                 this.hide();\r
45044             }\r
45045         }else if(at && at.el == el){\r
45046             this.clearTimer('show');\r
45047         }\r
45048     },\r
45049 \r
45050     // private\r
45051     getTipCfg: function(e) {\r
45052         var t = e.getTarget(), \r
45053             ttp, \r
45054             cfg;\r
45055         if(this.interceptTitles && t.title){\r
45056             ttp = t.title;\r
45057             t.qtip = ttp;\r
45058             t.removeAttribute("title");\r
45059             e.preventDefault();\r
45060         }else{\r
45061             cfg = this.tagConfig;\r
45062             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);\r
45063         }\r
45064         return ttp;\r
45065     },\r
45066 \r
45067     // private\r
45068     onTargetOver : function(e){\r
45069         if(this.disabled){\r
45070             return;\r
45071         }\r
45072         this.targetXY = e.getXY();\r
45073         var t = e.getTarget();\r
45074         if(!t || t.nodeType !== 1 || t == document || t == document.body){\r
45075             return;\r
45076         }\r
45077         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){\r
45078             this.clearTimer('hide');\r
45079             this.show();\r
45080             return;\r
45081         }\r
45082         if(t && this.targets[t.id]){\r
45083             this.activeTarget = this.targets[t.id];\r
45084             this.activeTarget.el = t;\r
45085             this.anchor = this.activeTarget.anchor;\r
45086             if(this.anchor){\r
45087                 this.anchorTarget = t;\r
45088             }\r
45089             this.delayShow();\r
45090             return;\r
45091         }\r
45092         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;\r
45093         if(ttp = this.getTipCfg(e)){\r
45094             var autoHide = et.getAttribute(cfg.hide, ns);\r
45095             this.activeTarget = {\r
45096                 el: t,\r
45097                 text: ttp,\r
45098                 width: et.getAttribute(cfg.width, ns),\r
45099                 autoHide: autoHide != "user" && autoHide !== 'false',\r
45100                 title: et.getAttribute(cfg.title, ns),\r
45101                 cls: et.getAttribute(cfg.cls, ns),\r
45102                 align: et.getAttribute(cfg.align, ns)\r
45103                 \r
45104             };\r
45105             this.anchor = et.getAttribute(cfg.anchor, ns);\r
45106             if(this.anchor){\r
45107                 this.anchorTarget = t;\r
45108             }\r
45109             this.delayShow();\r
45110         }\r
45111     },\r
45112 \r
45113     // private\r
45114     onTargetOut : function(e){\r
45115 \r
45116         // If moving within the current target, and it does not have a new tip, ignore the mouseout\r
45117         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {\r
45118             return;\r
45119         }\r
45120 \r
45121         this.clearTimer('show');\r
45122         if(this.autoHide !== false){\r
45123             this.delayHide();\r
45124         }\r
45125     },\r
45126 \r
45127     // inherit docs\r
45128     showAt : function(xy){\r
45129         var t = this.activeTarget;\r
45130         if(t){\r
45131             if(!this.rendered){\r
45132                 this.render(Ext.getBody());\r
45133                 this.activeTarget = t;\r
45134             }\r
45135             if(t.width){\r
45136                 this.setWidth(t.width);\r
45137                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));\r
45138                 this.measureWidth = false;\r
45139             } else{\r
45140                 this.measureWidth = true;\r
45141             }\r
45142             this.setTitle(t.title || '');\r
45143             this.body.update(t.text);\r
45144             this.autoHide = t.autoHide;\r
45145             this.dismissDelay = t.dismissDelay || this.dismissDelay;\r
45146             if(this.lastCls){\r
45147                 this.el.removeClass(this.lastCls);\r
45148                 delete this.lastCls;\r
45149             }\r
45150             if(t.cls){\r
45151                 this.el.addClass(t.cls);\r
45152                 this.lastCls = t.cls;\r
45153             }\r
45154             if(this.anchor){\r
45155                 this.constrainPosition = false;\r
45156             }else if(t.align){ // TODO: this doesn't seem to work consistently\r
45157                 xy = this.el.getAlignToXY(t.el, t.align);\r
45158                 this.constrainPosition = false;\r
45159             }else{\r
45160                 this.constrainPosition = true;\r
45161             }\r
45162         }\r
45163         Ext.QuickTip.superclass.showAt.call(this, xy);\r
45164     },\r
45165 \r
45166     // inherit docs\r
45167     hide: function(){\r
45168         delete this.activeTarget;\r
45169         Ext.QuickTip.superclass.hide.call(this);\r
45170     }\r
45171 });\r
45172 Ext.reg('quicktip', Ext.QuickTip);/**\r
45173  * @class Ext.QuickTips\r
45174  * <p>Provides attractive and customizable tooltips for any element. The QuickTips\r
45175  * singleton is used to configure and manage tooltips globally for multiple elements\r
45176  * in a generic manner.  To create individual tooltips with maximum customizability,\r
45177  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>\r
45178  * <p>Quicktips can be configured via tag attributes directly in markup, or by\r
45179  * registering quick tips programmatically via the {@link #register} method.</p>\r
45180  * <p>The singleton's instance of {@link Ext.QuickTip} is available via\r
45181  * {@link #getQuickTip}, and supports all the methods, and all the all the\r
45182  * configuration properties of Ext.QuickTip. These settings will apply to all\r
45183  * tooltips shown by the singleton.</p>\r
45184  * <p>Below is the summary of the configuration properties which can be used.\r
45185  * For detailed descriptions see {@link #getQuickTip}</p>\r
45186  * <p><b>QuickTips singleton configs (all are optional)</b></p>\r
45187  * <div class="mdetail-params"><ul><li>dismissDelay</li>\r
45188  * <li>hideDelay</li>\r
45189  * <li>maxWidth</li>\r
45190  * <li>minWidth</li>\r
45191  * <li>showDelay</li>\r
45192  * <li>trackMouse</li></ul></div>\r
45193  * <p><b>Target element configs (optional unless otherwise noted)</b></p>\r
45194  * <div class="mdetail-params"><ul><li>autoHide</li>\r
45195  * <li>cls</li>\r
45196  * <li>dismissDelay (overrides singleton value)</li>\r
45197  * <li>target (required)</li>\r
45198  * <li>text (required)</li>\r
45199  * <li>title</li>\r
45200  * <li>width</li></ul></div>\r
45201  * <p>Here is an example showing how some of these config options could be used:</p>\r
45202  * <pre><code>\r
45203 // Init the singleton.  Any tag-based quick tips will start working.\r
45204 Ext.QuickTips.init();\r
45205 \r
45206 // Apply a set of config properties to the singleton\r
45207 Ext.apply(Ext.QuickTips.getQuickTip(), {\r
45208     maxWidth: 200,\r
45209     minWidth: 100,\r
45210     showDelay: 50,\r
45211     trackMouse: true\r
45212 });\r
45213 \r
45214 // Manually register a quick tip for a specific element\r
45215 Ext.QuickTips.register({\r
45216     target: 'my-div',\r
45217     title: 'My Tooltip',\r
45218     text: 'This tooltip was added in code',\r
45219     width: 100,\r
45220     dismissDelay: 20\r
45221 });\r
45222 </code></pre>\r
45223  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with\r
45224  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary\r
45225  * of supported attributes (optional unless otherwise noted):</p>\r
45226  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the\r
45227  * same as autoHide = true.</li>\r
45228  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>\r
45229  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>\r
45230  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>\r
45231  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>\r
45232  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>\r
45233  * <pre><code>\r
45234 // Add a quick tip to an HTML button\r
45235 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"\r
45236      ext:qtip="This is a quick tip from markup!">&lt;/input>\r
45237 </code></pre>\r
45238  * @singleton\r
45239  */\r
45240 Ext.QuickTips = function(){\r
45241     var tip, locks = [];\r
45242     return {\r
45243         /**\r
45244          * Initialize the global QuickTips instance and prepare any quick tips.\r
45245          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) \r
45246          */\r
45247         init : function(autoRender){\r
45248             if(!tip){\r
45249                 if(!Ext.isReady){\r
45250                     Ext.onReady(function(){\r
45251                         Ext.QuickTips.init(autoRender);\r
45252                     });\r
45253                     return;\r
45254                 }\r
45255                 tip = new Ext.QuickTip({elements:'header,body'});\r
45256                 if(autoRender !== false){\r
45257                     tip.render(Ext.getBody());\r
45258                 }\r
45259             }\r
45260         },\r
45261 \r
45262         /**\r
45263          * Enable quick tips globally.\r
45264          */\r
45265         enable : function(){\r
45266             if(tip){\r
45267                 locks.pop();\r
45268                 if(locks.length < 1){\r
45269                     tip.enable();\r
45270                 }\r
45271             }\r
45272         },\r
45273 \r
45274         /**\r
45275          * Disable quick tips globally.\r
45276          */\r
45277         disable : function(){\r
45278             if(tip){\r
45279                 tip.disable();\r
45280             }\r
45281             locks.push(1);\r
45282         },\r
45283 \r
45284         /**\r
45285          * Returns true if quick tips are enabled, else false.\r
45286          * @return {Boolean}\r
45287          */\r
45288         isEnabled : function(){\r
45289             return tip !== undefined && !tip.disabled;\r
45290         },\r
45291 \r
45292         /**\r
45293          * Gets the global QuickTips instance.\r
45294          */\r
45295         getQuickTip : function(){\r
45296             return tip;\r
45297         },\r
45298 \r
45299         /**\r
45300          * Configures a new quick tip instance and assigns it to a target element.  See\r
45301          * {@link Ext.QuickTip#register} for details.\r
45302          * @param {Object} config The config object\r
45303          */\r
45304         register : function(){\r
45305             tip.register.apply(tip, arguments);\r
45306         },\r
45307 \r
45308         /**\r
45309          * Removes any registered quick tip from the target element and destroys it.\r
45310          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
45311          */\r
45312         unregister : function(){\r
45313             tip.unregister.apply(tip, arguments);\r
45314         },\r
45315 \r
45316         /**\r
45317          * Alias of {@link #register}.\r
45318          * @param {Object} config The config object\r
45319          */\r
45320         tips :function(){\r
45321             tip.register.apply(tip, arguments);\r
45322         }\r
45323     }\r
45324 }();/**\r
45325  * @class Ext.tree.TreePanel\r
45326  * @extends Ext.Panel\r
45327  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>\r
45328  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata\r
45329  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>\r
45330  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be\r
45331  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.\r
45332  * <p>An example of tree rendered to an existing div:</p><pre><code>\r
45333 var tree = new Ext.tree.TreePanel({\r
45334     renderTo: 'tree-div',\r
45335     useArrows: true,\r
45336     autoScroll: true,\r
45337     animate: true,\r
45338     enableDD: true,\r
45339     containerScroll: true,\r
45340     border: false,\r
45341     // auto create TreeLoader\r
45342     dataUrl: 'get-nodes.php',\r
45343 \r
45344     root: {\r
45345         nodeType: 'async',\r
45346         text: 'Ext JS',\r
45347         draggable: false,\r
45348         id: 'source'\r
45349     }\r
45350 });\r
45351 \r
45352 tree.getRootNode().expand();\r
45353  * </code></pre>\r
45354  * <p>The example above would work with a data packet similar to this:</p><pre><code>\r
45355 [{\r
45356     "text": "adapter",\r
45357     "id": "source\/adapter",\r
45358     "cls": "folder"\r
45359 }, {\r
45360     "text": "dd",\r
45361     "id": "source\/dd",\r
45362     "cls": "folder"\r
45363 }, {\r
45364     "text": "debug.js",\r
45365     "id": "source\/debug.js",\r
45366     "leaf": true,\r
45367     "cls": "file"\r
45368 }]\r
45369  * </code></pre>\r
45370  * <p>An example of tree within a Viewport:</p><pre><code>\r
45371 new Ext.Viewport({\r
45372     layout: 'border',\r
45373     items: [{\r
45374         region: 'west',\r
45375         collapsible: true,\r
45376         title: 'Navigation',\r
45377         xtype: 'treepanel',\r
45378         width: 200,\r
45379         autoScroll: true,\r
45380         split: true,\r
45381         loader: new Ext.tree.TreeLoader(),\r
45382         root: new Ext.tree.AsyncTreeNode({\r
45383             expanded: true,\r
45384             children: [{\r
45385                 text: 'Menu Option 1',\r
45386                 leaf: true\r
45387             }, {\r
45388                 text: 'Menu Option 2',\r
45389                 leaf: true\r
45390             }, {\r
45391                 text: 'Menu Option 3',\r
45392                 leaf: true\r
45393             }]\r
45394         }),\r
45395         rootVisible: false,\r
45396         listeners: {\r
45397             click: function(n) {\r
45398                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');\r
45399             }\r
45400         }\r
45401     }, {\r
45402         region: 'center',\r
45403         xtype: 'tabpanel',\r
45404         // remaining code not shown ...\r
45405     }]\r
45406 });\r
45407 </code></pre>\r
45408  *\r
45409  * @cfg {Ext.tree.TreeNode} root The root node for the tree.\r
45410  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)\r
45411  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)\r
45412  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop\r
45413  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag\r
45414  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop\r
45415  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance\r
45416  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance\r
45417  * @cfg {String} ddGroup The DD group this TreePanel belongs to\r
45418  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)\r
45419  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling\r
45420  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager\r
45421  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})\r
45422  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)\r
45423  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})\r
45424  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded\r
45425  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})\r
45426  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting\r
45427  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel\r
45428  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)\r
45429  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)\r
45430  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
45431  *\r
45432  * @constructor\r
45433  * @param {Object} config\r
45434  * @xtype treepanel\r
45435  */\r
45436 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {\r
45437     rootVisible : true,\r
45438     animate: Ext.enableFx,\r
45439     lines : true,\r
45440     enableDD : false,\r
45441     hlDrop : Ext.enableFx,\r
45442     pathSeparator: "/",\r
45443     \r
45444     /**\r
45445      * @cfg {Array} bubbleEvents\r
45446      * <p>An array of events that, when fired, should be bubbled to any parent container.\r
45447      * Defaults to <tt>['add', 'remove']</tt>.\r
45448      */\r
45449     bubbleEvents: [],\r
45450 \r
45451     initComponent : function(){\r
45452         Ext.tree.TreePanel.superclass.initComponent.call(this);\r
45453 \r
45454         if(!this.eventModel){\r
45455             this.eventModel = new Ext.tree.TreeEventModel(this);\r
45456         }\r
45457 \r
45458         // initialize the loader\r
45459         var l = this.loader;\r
45460         if(!l){\r
45461             l = new Ext.tree.TreeLoader({\r
45462                 dataUrl: this.dataUrl,\r
45463                 requestMethod: this.requestMethod\r
45464             });\r
45465         }else if(typeof l == 'object' && !l.load){\r
45466             l = new Ext.tree.TreeLoader(l);\r
45467         }\r
45468         this.loader = l;\r
45469 \r
45470         this.nodeHash = {};\r
45471 \r
45472         /**\r
45473         * The root node of this tree.\r
45474         * @type Ext.tree.TreeNode\r
45475         * @property root\r
45476         */\r
45477         if(this.root){\r
45478             var r = this.root;\r
45479             delete this.root;\r
45480             this.setRootNode(r);\r
45481         }\r
45482 \r
45483 \r
45484         this.addEvents(\r
45485 \r
45486             /**\r
45487             * @event append\r
45488             * Fires when a new child node is appended to a node in this tree.\r
45489             * @param {Tree} tree The owner tree\r
45490             * @param {Node} parent The parent node\r
45491             * @param {Node} node The newly appended node\r
45492             * @param {Number} index The index of the newly appended node\r
45493             */\r
45494            "append",\r
45495            /**\r
45496             * @event remove\r
45497             * Fires when a child node is removed from a node in this tree.\r
45498             * @param {Tree} tree The owner tree\r
45499             * @param {Node} parent The parent node\r
45500             * @param {Node} node The child node removed\r
45501             */\r
45502            "remove",\r
45503            /**\r
45504             * @event movenode\r
45505             * Fires when a node is moved to a new location in the tree\r
45506             * @param {Tree} tree The owner tree\r
45507             * @param {Node} node The node moved\r
45508             * @param {Node} oldParent The old parent of this node\r
45509             * @param {Node} newParent The new parent of this node\r
45510             * @param {Number} index The index it was moved to\r
45511             */\r
45512            "movenode",\r
45513            /**\r
45514             * @event insert\r
45515             * Fires when a new child node is inserted in a node in this tree.\r
45516             * @param {Tree} tree The owner tree\r
45517             * @param {Node} parent The parent node\r
45518             * @param {Node} node The child node inserted\r
45519             * @param {Node} refNode The child node the node was inserted before\r
45520             */\r
45521            "insert",\r
45522            /**\r
45523             * @event beforeappend\r
45524             * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
45525             * @param {Tree} tree The owner tree\r
45526             * @param {Node} parent The parent node\r
45527             * @param {Node} node The child node to be appended\r
45528             */\r
45529            "beforeappend",\r
45530            /**\r
45531             * @event beforeremove\r
45532             * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
45533             * @param {Tree} tree The owner tree\r
45534             * @param {Node} parent The parent node\r
45535             * @param {Node} node The child node to be removed\r
45536             */\r
45537            "beforeremove",\r
45538            /**\r
45539             * @event beforemovenode\r
45540             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
45541             * @param {Tree} tree The owner tree\r
45542             * @param {Node} node The node being moved\r
45543             * @param {Node} oldParent The parent of the node\r
45544             * @param {Node} newParent The new parent the node is moving to\r
45545             * @param {Number} index The index it is being moved to\r
45546             */\r
45547            "beforemovenode",\r
45548            /**\r
45549             * @event beforeinsert\r
45550             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
45551             * @param {Tree} tree The owner tree\r
45552             * @param {Node} parent The parent node\r
45553             * @param {Node} node The child node to be inserted\r
45554             * @param {Node} refNode The child node the node is being inserted before\r
45555             */\r
45556             "beforeinsert",\r
45557 \r
45558             /**\r
45559             * @event beforeload\r
45560             * Fires before a node is loaded, return false to cancel\r
45561             * @param {Node} node The node being loaded\r
45562             */\r
45563             "beforeload",\r
45564             /**\r
45565             * @event load\r
45566             * Fires when a node is loaded\r
45567             * @param {Node} node The node that was loaded\r
45568             */\r
45569             "load",\r
45570             /**\r
45571             * @event textchange\r
45572             * Fires when the text for a node is changed\r
45573             * @param {Node} node The node\r
45574             * @param {String} text The new text\r
45575             * @param {String} oldText The old text\r
45576             */\r
45577             "textchange",\r
45578             /**\r
45579             * @event beforeexpandnode\r
45580             * Fires before a node is expanded, return false to cancel.\r
45581             * @param {Node} node The node\r
45582             * @param {Boolean} deep\r
45583             * @param {Boolean} anim\r
45584             */\r
45585             "beforeexpandnode",\r
45586             /**\r
45587             * @event beforecollapsenode\r
45588             * Fires before a node is collapsed, return false to cancel.\r
45589             * @param {Node} node The node\r
45590             * @param {Boolean} deep\r
45591             * @param {Boolean} anim\r
45592             */\r
45593             "beforecollapsenode",\r
45594             /**\r
45595             * @event expandnode\r
45596             * Fires when a node is expanded\r
45597             * @param {Node} node The node\r
45598             */\r
45599             "expandnode",\r
45600             /**\r
45601             * @event disabledchange\r
45602             * Fires when the disabled status of a node changes\r
45603             * @param {Node} node The node\r
45604             * @param {Boolean} disabled\r
45605             */\r
45606             "disabledchange",\r
45607             /**\r
45608             * @event collapsenode\r
45609             * Fires when a node is collapsed\r
45610             * @param {Node} node The node\r
45611             */\r
45612             "collapsenode",\r
45613             /**\r
45614             * @event beforeclick\r
45615             * Fires before click processing on a node. Return false to cancel the default action.\r
45616             * @param {Node} node The node\r
45617             * @param {Ext.EventObject} e The event object\r
45618             */\r
45619             "beforeclick",\r
45620             /**\r
45621             * @event click\r
45622             * Fires when a node is clicked\r
45623             * @param {Node} node The node\r
45624             * @param {Ext.EventObject} e The event object\r
45625             */\r
45626             "click",\r
45627             /**\r
45628             * @event checkchange\r
45629             * Fires when a node with a checkbox's checked property changes\r
45630             * @param {Node} this This node\r
45631             * @param {Boolean} checked\r
45632             */\r
45633             "checkchange",\r
45634             /**\r
45635             * @event beforedblclick\r
45636             * Fires before double click processing on a node. Return false to cancel the default action.\r
45637             * @param {Node} node The node\r
45638             * @param {Ext.EventObject} e The event object\r
45639             */\r
45640             "beforedblclick",\r
45641             /**\r
45642             * @event dblclick\r
45643             * Fires when a node is double clicked\r
45644             * @param {Node} node The node\r
45645             * @param {Ext.EventObject} e The event object\r
45646             */\r
45647             "dblclick",\r
45648             /**\r
45649             * @event contextmenu\r
45650             * Fires when a node is right clicked. To display a context menu in response to this\r
45651             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add\r
45652             * a handler for this event:<pre><code>\r
45653 new Ext.tree.TreePanel({\r
45654     title: 'My TreePanel',\r
45655     root: new Ext.tree.AsyncTreeNode({\r
45656         text: 'The Root',\r
45657         children: [\r
45658             { text: 'Child node 1', leaf: true },\r
45659             { text: 'Child node 2', leaf: true }\r
45660         ]\r
45661     }),\r
45662     contextMenu: new Ext.menu.Menu({\r
45663         items: [{\r
45664             id: 'delete-node',\r
45665             text: 'Delete Node'\r
45666         }],\r
45667         listeners: {\r
45668             itemclick: function(item) {\r
45669                 switch (item.id) {\r
45670                     case 'delete-node':\r
45671                         var n = item.parentMenu.contextNode;\r
45672                         if (n.parentNode) {\r
45673                             n.remove();\r
45674                         }\r
45675                         break;\r
45676                 }\r
45677             }\r
45678         }\r
45679     }),\r
45680     listeners: {\r
45681         contextmenu: function(node, e) {\r
45682 //          Register the context node with the menu so that a Menu Item's handler function can access\r
45683 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.\r
45684             node.select();\r
45685             var c = node.getOwnerTree().contextMenu;\r
45686             c.contextNode = node;\r
45687             c.showAt(e.getXY());\r
45688         }\r
45689     }\r
45690 });\r
45691 </code></pre>\r
45692             * @param {Node} node The node\r
45693             * @param {Ext.EventObject} e The event object\r
45694             */\r
45695             "contextmenu",\r
45696             /**\r
45697             * @event beforechildrenrendered\r
45698             * Fires right before the child nodes for a node are rendered\r
45699             * @param {Node} node The node\r
45700             */\r
45701             "beforechildrenrendered",\r
45702            /**\r
45703              * @event startdrag\r
45704              * Fires when a node starts being dragged\r
45705              * @param {Ext.tree.TreePanel} this\r
45706              * @param {Ext.tree.TreeNode} node\r
45707              * @param {event} e The raw browser event\r
45708              */\r
45709             "startdrag",\r
45710             /**\r
45711              * @event enddrag\r
45712              * Fires when a drag operation is complete\r
45713              * @param {Ext.tree.TreePanel} this\r
45714              * @param {Ext.tree.TreeNode} node\r
45715              * @param {event} e The raw browser event\r
45716              */\r
45717             "enddrag",\r
45718             /**\r
45719              * @event dragdrop\r
45720              * Fires when a dragged node is dropped on a valid DD target\r
45721              * @param {Ext.tree.TreePanel} this\r
45722              * @param {Ext.tree.TreeNode} node\r
45723              * @param {DD} dd The dd it was dropped on\r
45724              * @param {event} e The raw browser event\r
45725              */\r
45726             "dragdrop",\r
45727             /**\r
45728              * @event beforenodedrop\r
45729              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent\r
45730              * passed to handlers has the following properties:<br />\r
45731              * <ul style="padding:5px;padding-left:16px;">\r
45732              * <li>tree - The TreePanel</li>\r
45733              * <li>target - The node being targeted for the drop</li>\r
45734              * <li>data - The drag data from the drag source</li>\r
45735              * <li>point - The point of the drop - append, above or below</li>\r
45736              * <li>source - The drag source</li>\r
45737              * <li>rawEvent - Raw mouse event</li>\r
45738              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)\r
45739              * to be inserted by setting them on this object.</li>\r
45740              * <li>cancel - Set this to true to cancel the drop.</li>\r
45741              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true\r
45742              * will prevent the animated "repair" from appearing.</li>\r
45743              * </ul>\r
45744              * @param {Object} dropEvent\r
45745              */\r
45746             "beforenodedrop",\r
45747             /**\r
45748              * @event nodedrop\r
45749              * Fires after a DD object is dropped on a node in this tree. The dropEvent\r
45750              * passed to handlers has the following properties:<br />\r
45751              * <ul style="padding:5px;padding-left:16px;">\r
45752              * <li>tree - The TreePanel</li>\r
45753              * <li>target - The node being targeted for the drop</li>\r
45754              * <li>data - The drag data from the drag source</li>\r
45755              * <li>point - The point of the drop - append, above or below</li>\r
45756              * <li>source - The drag source</li>\r
45757              * <li>rawEvent - Raw mouse event</li>\r
45758              * <li>dropNode - Dropped node(s).</li>\r
45759              * </ul>\r
45760              * @param {Object} dropEvent\r
45761              */\r
45762             "nodedrop",\r
45763              /**\r
45764              * @event nodedragover\r
45765              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent\r
45766              * passed to handlers has the following properties:<br />\r
45767              * <ul style="padding:5px;padding-left:16px;">\r
45768              * <li>tree - The TreePanel</li>\r
45769              * <li>target - The node being targeted for the drop</li>\r
45770              * <li>data - The drag data from the drag source</li>\r
45771              * <li>point - The point of the drop - append, above or below</li>\r
45772              * <li>source - The drag source</li>\r
45773              * <li>rawEvent - Raw mouse event</li>\r
45774              * <li>dropNode - Drop node(s) provided by the source.</li>\r
45775              * <li>cancel - Set this to true to signal drop not allowed.</li>\r
45776              * </ul>\r
45777              * @param {Object} dragOverEvent\r
45778              */\r
45779             "nodedragover"\r
45780         );\r
45781         if(this.singleExpand){\r
45782             this.on("beforeexpandnode", this.restrictExpand, this);\r
45783         }\r
45784     },\r
45785 \r
45786     // private\r
45787     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){\r
45788         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){\r
45789             ename = ename+'node';\r
45790         }\r
45791         // args inline for performance while bubbling events\r
45792         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);\r
45793     },\r
45794 \r
45795 \r
45796     /**\r
45797      * Returns this root node for this tree\r
45798      * @return {Node}\r
45799      */\r
45800     getRootNode : function(){\r
45801         return this.root;\r
45802     },\r
45803 \r
45804     /**\r
45805      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the\r
45806      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.\r
45807      * @param {Node} node\r
45808      * @return {Node}\r
45809      */\r
45810     setRootNode : function(node){\r
45811         Ext.destroy(this.root);\r
45812         if(!node.render){ // attributes passed\r
45813             node = this.loader.createNode(node);\r
45814         }\r
45815         this.root = node;\r
45816         node.ownerTree = this;\r
45817         node.isRoot = true;\r
45818         this.registerNode(node);\r
45819         if(!this.rootVisible){\r
45820             var uiP = node.attributes.uiProvider;\r
45821             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);\r
45822         }\r
45823         if (this.innerCt) {\r
45824             this.innerCt.update('');\r
45825             this.afterRender();\r
45826         }\r
45827         return node;\r
45828     },\r
45829 \r
45830     /**\r
45831      * Gets a node in this tree by its id\r
45832      * @param {String} id\r
45833      * @return {Node}\r
45834      */\r
45835     getNodeById : function(id){\r
45836         return this.nodeHash[id];\r
45837     },\r
45838 \r
45839     // private\r
45840     registerNode : function(node){\r
45841         this.nodeHash[node.id] = node;\r
45842     },\r
45843 \r
45844     // private\r
45845     unregisterNode : function(node){\r
45846         delete this.nodeHash[node.id];\r
45847     },\r
45848 \r
45849     // private\r
45850     toString : function(){\r
45851         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
45852     },\r
45853 \r
45854     // private\r
45855     restrictExpand : function(node){\r
45856         var p = node.parentNode;\r
45857         if(p){\r
45858             if(p.expandedChild && p.expandedChild.parentNode == p){\r
45859                 p.expandedChild.collapse();\r
45860             }\r
45861             p.expandedChild = node;\r
45862         }\r
45863     },\r
45864 \r
45865     /**\r
45866      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")\r
45867      * @param {String} attribute (optional) Defaults to null (return the actual nodes)\r
45868      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root\r
45869      * @return {Array}\r
45870      */\r
45871     getChecked : function(a, startNode){\r
45872         startNode = startNode || this.root;\r
45873         var r = [];\r
45874         var f = function(){\r
45875             if(this.attributes.checked){\r
45876                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));\r
45877             }\r
45878         };\r
45879         startNode.cascade(f);\r
45880         return r;\r
45881     },\r
45882 \r
45883     /**\r
45884      * Returns the container element for this TreePanel.\r
45885      * @return {Element} The container element for this TreePanel.\r
45886      */\r
45887     getEl : function(){\r
45888         return this.el;\r
45889     },\r
45890 \r
45891     /**\r
45892      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.\r
45893      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.\r
45894      */\r
45895     getLoader : function(){\r
45896         return this.loader;\r
45897     },\r
45898 \r
45899     /**\r
45900      * Expand all nodes\r
45901      */\r
45902     expandAll : function(){\r
45903         this.root.expand(true);\r
45904     },\r
45905 \r
45906     /**\r
45907      * Collapse all nodes\r
45908      */\r
45909     collapseAll : function(){\r
45910         this.root.collapse(true);\r
45911     },\r
45912 \r
45913     /**\r
45914      * Returns the selection model used by this TreePanel.\r
45915      * @return {TreeSelectionModel} The selection model used by this TreePanel\r
45916      */\r
45917     getSelectionModel : function(){\r
45918         if(!this.selModel){\r
45919             this.selModel = new Ext.tree.DefaultSelectionModel();\r
45920         }\r
45921         return this.selModel;\r
45922     },\r
45923 \r
45924     /**\r
45925      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
45926      * @param {String} path\r
45927      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
45928      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with\r
45929      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.\r
45930      */\r
45931     expandPath : function(path, attr, callback){\r
45932         attr = attr || "id";\r
45933         var keys = path.split(this.pathSeparator);\r
45934         var curNode = this.root;\r
45935         if(curNode.attributes[attr] != keys[1]){ // invalid root\r
45936             if(callback){\r
45937                 callback(false, null);\r
45938             }\r
45939             return;\r
45940         }\r
45941         var index = 1;\r
45942         var f = function(){\r
45943             if(++index == keys.length){\r
45944                 if(callback){\r
45945                     callback(true, curNode);\r
45946                 }\r
45947                 return;\r
45948             }\r
45949             var c = curNode.findChild(attr, keys[index]);\r
45950             if(!c){\r
45951                 if(callback){\r
45952                     callback(false, curNode);\r
45953                 }\r
45954                 return;\r
45955             }\r
45956             curNode = c;\r
45957             c.expand(false, false, f);\r
45958         };\r
45959         curNode.expand(false, false, f);\r
45960     },\r
45961 \r
45962     /**\r
45963      * 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
45964      * @param {String} path\r
45965      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
45966      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with\r
45967      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.\r
45968      */\r
45969     selectPath : function(path, attr, callback){\r
45970         attr = attr || "id";\r
45971         var keys = path.split(this.pathSeparator),\r
45972             v = keys.pop();\r
45973         if(keys.length > 1){\r
45974             var f = function(success, node){\r
45975                 if(success && node){\r
45976                     var n = node.findChild(attr, v);\r
45977                     if(n){\r
45978                         n.select();\r
45979                         if(callback){\r
45980                             callback(true, n);\r
45981                         }\r
45982                     }else if(callback){\r
45983                         callback(false, n);\r
45984                     }\r
45985                 }else{\r
45986                     if(callback){\r
45987                         callback(false, n);\r
45988                     }\r
45989                 }\r
45990             };\r
45991             this.expandPath(keys.join(this.pathSeparator), attr, f);\r
45992         }else{\r
45993             this.root.select();\r
45994             if(callback){\r
45995                 callback(true, this.root);\r
45996             }\r
45997         }\r
45998     },\r
45999 \r
46000     /**\r
46001      * Returns the underlying Element for this tree\r
46002      * @return {Ext.Element} The Element\r
46003      */\r
46004     getTreeEl : function(){\r
46005         return this.body;\r
46006     },\r
46007 \r
46008     // private\r
46009     onRender : function(ct, position){\r
46010         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);\r
46011         this.el.addClass('x-tree');\r
46012         this.innerCt = this.body.createChild({tag:"ul",\r
46013                cls:"x-tree-root-ct " +\r
46014                (this.useArrows ? 'x-tree-arrows' : this.lines ? "x-tree-lines" : "x-tree-no-lines")});\r
46015     },\r
46016 \r
46017     // private\r
46018     initEvents : function(){\r
46019         Ext.tree.TreePanel.superclass.initEvents.call(this);\r
46020 \r
46021         if(this.containerScroll){\r
46022             Ext.dd.ScrollManager.register(this.body);\r
46023         }\r
46024         if((this.enableDD || this.enableDrop) && !this.dropZone){\r
46025            /**\r
46026             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})\r
46027             * @property dropZone\r
46028             * @type Ext.tree.TreeDropZone\r
46029             */\r
46030              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {\r
46031                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true\r
46032            });\r
46033         }\r
46034         if((this.enableDD || this.enableDrag) && !this.dragZone){\r
46035            /**\r
46036             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})\r
46037             * @property dragZone\r
46038             * @type Ext.tree.TreeDragZone\r
46039             */\r
46040             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {\r
46041                ddGroup: this.ddGroup || "TreeDD",\r
46042                scroll: this.ddScroll\r
46043            });\r
46044         }\r
46045         this.getSelectionModel().init(this);\r
46046     },\r
46047 \r
46048     // private\r
46049     afterRender : function(){\r
46050         Ext.tree.TreePanel.superclass.afterRender.call(this);\r
46051         this.root.render();\r
46052         if(!this.rootVisible){\r
46053             this.root.renderChildren();\r
46054         }\r
46055     },\r
46056 \r
46057     onDestroy : function(){\r
46058         if(this.rendered){\r
46059             this.body.removeAllListeners();\r
46060             Ext.dd.ScrollManager.unregister(this.body);\r
46061             if(this.dropZone){\r
46062                 this.dropZone.unreg();\r
46063             }\r
46064             if(this.dragZone){\r
46065                this.dragZone.unreg();\r
46066             }\r
46067         }\r
46068         this.root.destroy();\r
46069         this.nodeHash = null;\r
46070         Ext.tree.TreePanel.superclass.onDestroy.call(this);\r
46071     }\r
46072 \r
46073     /**\r
46074      * @cfg {String/Number} activeItem\r
46075      * @hide\r
46076      */\r
46077     /**\r
46078      * @cfg {Boolean} autoDestroy\r
46079      * @hide\r
46080      */\r
46081     /**\r
46082      * @cfg {Object/String/Function} autoLoad\r
46083      * @hide\r
46084      */\r
46085     /**\r
46086      * @cfg {Boolean} autoWidth\r
46087      * @hide\r
46088      */\r
46089     /**\r
46090      * @cfg {Boolean/Number} bufferResize\r
46091      * @hide\r
46092      */\r
46093     /**\r
46094      * @cfg {String} defaultType\r
46095      * @hide\r
46096      */\r
46097     /**\r
46098      * @cfg {Object} defaults\r
46099      * @hide\r
46100      */\r
46101     /**\r
46102      * @cfg {Boolean} hideBorders\r
46103      * @hide\r
46104      */\r
46105     /**\r
46106      * @cfg {Mixed} items\r
46107      * @hide\r
46108      */\r
46109     /**\r
46110      * @cfg {String} layout\r
46111      * @hide\r
46112      */\r
46113     /**\r
46114      * @cfg {Object} layoutConfig\r
46115      * @hide\r
46116      */\r
46117     /**\r
46118      * @cfg {Boolean} monitorResize\r
46119      * @hide\r
46120      */\r
46121     /**\r
46122      * @property items\r
46123      * @hide\r
46124      */\r
46125     /**\r
46126      * @method cascade\r
46127      * @hide\r
46128      */\r
46129     /**\r
46130      * @method doLayout\r
46131      * @hide\r
46132      */\r
46133     /**\r
46134      * @method find\r
46135      * @hide\r
46136      */\r
46137     /**\r
46138      * @method findBy\r
46139      * @hide\r
46140      */\r
46141     /**\r
46142      * @method findById\r
46143      * @hide\r
46144      */\r
46145     /**\r
46146      * @method findByType\r
46147      * @hide\r
46148      */\r
46149     /**\r
46150      * @method getComponent\r
46151      * @hide\r
46152      */\r
46153     /**\r
46154      * @method getLayout\r
46155      * @hide\r
46156      */\r
46157     /**\r
46158      * @method getUpdater\r
46159      * @hide\r
46160      */\r
46161     /**\r
46162      * @method insert\r
46163      * @hide\r
46164      */\r
46165     /**\r
46166      * @method load\r
46167      * @hide\r
46168      */\r
46169     /**\r
46170      * @method remove\r
46171      * @hide\r
46172      */\r
46173     /**\r
46174      * @event add\r
46175      * @hide\r
46176      */\r
46177     /**\r
46178      * @method removeAll\r
46179      * @hide\r
46180      */\r
46181     /**\r
46182      * @event afterLayout\r
46183      * @hide\r
46184      */\r
46185     /**\r
46186      * @event beforeadd\r
46187      * @hide\r
46188      */\r
46189     /**\r
46190      * @event beforeremove\r
46191      * @hide\r
46192      */\r
46193     /**\r
46194      * @event remove\r
46195      * @hide\r
46196      */\r
46197 \r
46198 \r
46199 \r
46200     /**\r
46201      * @cfg {String} allowDomMove  @hide\r
46202      */\r
46203     /**\r
46204      * @cfg {String} autoEl @hide\r
46205      */\r
46206     /**\r
46207      * @cfg {String} applyTo  @hide\r
46208      */\r
46209     /**\r
46210      * @cfg {String} contentEl  @hide\r
46211      */\r
46212     /**\r
46213      * @cfg {String} disabledClass  @hide\r
46214      */\r
46215     /**\r
46216      * @cfg {String} elements  @hide\r
46217      */\r
46218     /**\r
46219      * @cfg {String} html  @hide\r
46220      */\r
46221     /**\r
46222      * @cfg {Boolean} preventBodyReset\r
46223      * @hide\r
46224      */\r
46225     /**\r
46226      * @property disabled\r
46227      * @hide\r
46228      */\r
46229     /**\r
46230      * @method applyToMarkup\r
46231      * @hide\r
46232      */\r
46233     /**\r
46234      * @method enable\r
46235      * @hide\r
46236      */\r
46237     /**\r
46238      * @method disable\r
46239      * @hide\r
46240      */\r
46241     /**\r
46242      * @method setDisabled\r
46243      * @hide\r
46244      */\r
46245 });\r
46246 \r
46247 Ext.tree.TreePanel.nodeTypes = {};\r
46248 \r
46249 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){\r
46250     this.tree = tree;\r
46251     this.tree.on('render', this.initEvents, this);\r
46252 }\r
46253 \r
46254 Ext.tree.TreeEventModel.prototype = {\r
46255     initEvents : function(){\r
46256         var t = this.tree;\r
46257             \r
46258         if(t.trackMouseOver !== false){\r
46259             t.mon(t.innerCt, {\r
46260                 scope: this,\r
46261                 mouseover: this.delegateOver,\r
46262                 mouseout: this.delegateOut\r
46263             });\r
46264         }\r
46265         t.mon(t.getTreeEl(), {\r
46266             scope: this,\r
46267             click: this.delegateClick,\r
46268             dblclick: this.delegateDblClick,\r
46269             contextmenu: this.delegateContextMenu\r
46270         });\r
46271     },\r
46272 \r
46273     getNode : function(e){\r
46274         var t;\r
46275         if(t = e.getTarget('.x-tree-node-el', 10)){\r
46276             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');\r
46277             if(id){\r
46278                 return this.tree.getNodeById(id);\r
46279             }\r
46280         }\r
46281         return null;\r
46282     },\r
46283 \r
46284     getNodeTarget : function(e){\r
46285         var t = e.getTarget('.x-tree-node-icon', 1);\r
46286         if(!t){\r
46287             t = e.getTarget('.x-tree-node-el', 6);\r
46288         }\r
46289         return t;\r
46290     },\r
46291 \r
46292     delegateOut : function(e, t){\r
46293         if(!this.beforeEvent(e)){\r
46294             return;\r
46295         }\r
46296         if(e.getTarget('.x-tree-ec-icon', 1)){\r
46297             var n = this.getNode(e);\r
46298             this.onIconOut(e, n);\r
46299             if(n == this.lastEcOver){\r
46300                 delete this.lastEcOver;\r
46301             }\r
46302         }\r
46303         if((t = this.getNodeTarget(e)) && !e.within(t, true)){\r
46304             this.onNodeOut(e, this.getNode(e));\r
46305         }\r
46306     },\r
46307 \r
46308     delegateOver : function(e, t){\r
46309         if(!this.beforeEvent(e)){\r
46310             return;\r
46311         }\r
46312         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF\r
46313             Ext.getBody().on('mouseover', this.trackExit, this);\r
46314             this.trackingDoc = true;\r
46315         }\r
46316         if(this.lastEcOver){ // prevent hung highlight\r
46317             this.onIconOut(e, this.lastEcOver);\r
46318             delete this.lastEcOver;\r
46319         }\r
46320         if(e.getTarget('.x-tree-ec-icon', 1)){\r
46321             this.lastEcOver = this.getNode(e);\r
46322             this.onIconOver(e, this.lastEcOver);\r
46323         }\r
46324         if(t = this.getNodeTarget(e)){\r
46325             this.onNodeOver(e, this.getNode(e));\r
46326         }\r
46327     },\r
46328 \r
46329     trackExit : function(e){\r
46330         if(this.lastOverNode && !e.within(this.lastOverNode.ui.getEl())){\r
46331             this.onNodeOut(e, this.lastOverNode);\r
46332             delete this.lastOverNode;\r
46333             Ext.getBody().un('mouseover', this.trackExit, this);\r
46334             this.trackingDoc = false;\r
46335         }\r
46336     },\r
46337 \r
46338     delegateClick : function(e, t){\r
46339         if(!this.beforeEvent(e)){\r
46340             return;\r
46341         }\r
46342 \r
46343         if(e.getTarget('input[type=checkbox]', 1)){\r
46344             this.onCheckboxClick(e, this.getNode(e));\r
46345         }\r
46346         else if(e.getTarget('.x-tree-ec-icon', 1)){\r
46347             this.onIconClick(e, this.getNode(e));\r
46348         }\r
46349         else if(this.getNodeTarget(e)){\r
46350             this.onNodeClick(e, this.getNode(e));\r
46351         }\r
46352     },\r
46353 \r
46354     delegateDblClick : function(e, t){\r
46355         if(this.beforeEvent(e) && this.getNodeTarget(e)){\r
46356             this.onNodeDblClick(e, this.getNode(e));\r
46357         }\r
46358     },\r
46359 \r
46360     delegateContextMenu : function(e, t){\r
46361         if(this.beforeEvent(e) && this.getNodeTarget(e)){\r
46362             this.onNodeContextMenu(e, this.getNode(e));\r
46363         }\r
46364     },\r
46365 \r
46366     onNodeClick : function(e, node){\r
46367         node.ui.onClick(e);\r
46368     },\r
46369 \r
46370     onNodeOver : function(e, node){\r
46371         this.lastOverNode = node;\r
46372         node.ui.onOver(e);\r
46373     },\r
46374 \r
46375     onNodeOut : function(e, node){\r
46376         node.ui.onOut(e);\r
46377     },\r
46378 \r
46379     onIconOver : function(e, node){\r
46380         node.ui.addClass('x-tree-ec-over');\r
46381     },\r
46382 \r
46383     onIconOut : function(e, node){\r
46384         node.ui.removeClass('x-tree-ec-over');\r
46385     },\r
46386 \r
46387     onIconClick : function(e, node){\r
46388         node.ui.ecClick(e);\r
46389     },\r
46390 \r
46391     onCheckboxClick : function(e, node){\r
46392         node.ui.onCheckChange(e);\r
46393     },\r
46394 \r
46395     onNodeDblClick : function(e, node){\r
46396         node.ui.onDblClick(e);\r
46397     },\r
46398 \r
46399     onNodeContextMenu : function(e, node){\r
46400         node.ui.onContextMenu(e);\r
46401     },\r
46402 \r
46403     beforeEvent : function(e){\r
46404         if(this.disabled){\r
46405             e.stopEvent();\r
46406             return false;\r
46407         }\r
46408         return true;\r
46409     },\r
46410 \r
46411     disable: function(){\r
46412         this.disabled = true;\r
46413     },\r
46414 \r
46415     enable: function(){\r
46416         this.disabled = false;\r
46417     }\r
46418 };/**\r
46419  * @class Ext.tree.DefaultSelectionModel\r
46420  * @extends Ext.util.Observable\r
46421  * The default single selection for a TreePanel.\r
46422  */\r
46423 Ext.tree.DefaultSelectionModel = function(config){\r
46424    this.selNode = null;\r
46425    \r
46426    this.addEvents(\r
46427        /**\r
46428         * @event selectionchange\r
46429         * Fires when the selected node changes\r
46430         * @param {DefaultSelectionModel} this\r
46431         * @param {TreeNode} node the new selection\r
46432         */\r
46433        "selectionchange",\r
46434 \r
46435        /**\r
46436         * @event beforeselect\r
46437         * Fires before the selected node changes, return false to cancel the change\r
46438         * @param {DefaultSelectionModel} this\r
46439         * @param {TreeNode} node the new selection\r
46440         * @param {TreeNode} node the old selection\r
46441         */\r
46442        "beforeselect"\r
46443    );\r
46444 \r
46445     Ext.apply(this, config);\r
46446     Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);\r
46447 };\r
46448 \r
46449 Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, {\r
46450     init : function(tree){\r
46451         this.tree = tree;\r
46452         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);\r
46453         tree.on("click", this.onNodeClick, this);\r
46454     },\r
46455     \r
46456     onNodeClick : function(node, e){\r
46457         this.select(node);\r
46458     },\r
46459     \r
46460     /**\r
46461      * Select a node.\r
46462      * @param {TreeNode} node The node to select\r
46463      * @return {TreeNode} The selected node\r
46464      */\r
46465     select : function(node, /* private*/ selectNextNode){\r
46466         // If node is hidden, select the next node in whatever direction was being moved in.\r
46467         if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {\r
46468             return selectNextNode.call(this, node);\r
46469         }\r
46470         var last = this.selNode;\r
46471         if(node == last){\r
46472             node.ui.onSelectedChange(true);\r
46473         }else if(this.fireEvent('beforeselect', this, node, last) !== false){\r
46474             if(last){\r
46475                 last.ui.onSelectedChange(false);\r
46476             }\r
46477             this.selNode = node;\r
46478             node.ui.onSelectedChange(true);\r
46479             this.fireEvent("selectionchange", this, node, last);\r
46480         }\r
46481         return node;\r
46482     },\r
46483     \r
46484     /**\r
46485      * Deselect a node.\r
46486      * @param {TreeNode} node The node to unselect\r
46487      */\r
46488     unselect : function(node){\r
46489         if(this.selNode == node){\r
46490             this.clearSelections();\r
46491         }    \r
46492     },\r
46493     \r
46494     /**\r
46495      * Clear all selections\r
46496      */\r
46497     clearSelections : function(){\r
46498         var n = this.selNode;\r
46499         if(n){\r
46500             n.ui.onSelectedChange(false);\r
46501             this.selNode = null;\r
46502             this.fireEvent("selectionchange", this, null);\r
46503         }\r
46504         return n;\r
46505     },\r
46506     \r
46507     /**\r
46508      * Get the selected node\r
46509      * @return {TreeNode} The selected node\r
46510      */\r
46511     getSelectedNode : function(){\r
46512         return this.selNode;    \r
46513     },\r
46514     \r
46515     /**\r
46516      * Returns true if the node is selected\r
46517      * @param {TreeNode} node The node to check\r
46518      * @return {Boolean}\r
46519      */\r
46520     isSelected : function(node){\r
46521         return this.selNode == node;  \r
46522     },\r
46523 \r
46524     /**\r
46525      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
46526      * @return TreeNode The new selection\r
46527      */\r
46528     selectPrevious : function(/* private */ s){\r
46529         if(!(s = s || this.selNode || this.lastSelNode)){\r
46530             return null;\r
46531         }\r
46532         // Here we pass in the current function to select to indicate the direction we're moving\r
46533         var ps = s.previousSibling;\r
46534         if(ps){\r
46535             if(!ps.isExpanded() || ps.childNodes.length < 1){\r
46536                 return this.select(ps, this.selectPrevious);\r
46537             } else{\r
46538                 var lc = ps.lastChild;\r
46539                 while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){\r
46540                     lc = lc.lastChild;\r
46541                 }\r
46542                 return this.select(lc, this.selectPrevious);\r
46543             }\r
46544         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
46545             return this.select(s.parentNode, this.selectPrevious);\r
46546         }\r
46547         return null;\r
46548     },\r
46549 \r
46550     /**\r
46551      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
46552      * @return TreeNode The new selection\r
46553      */\r
46554     selectNext : function(/* private */ s){\r
46555         if(!(s = s || this.selNode || this.lastSelNode)){\r
46556             return null;\r
46557         }\r
46558         // Here we pass in the current function to select to indicate the direction we're moving\r
46559         if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){\r
46560              return this.select(s.firstChild, this.selectNext);\r
46561          }else if(s.nextSibling){\r
46562              return this.select(s.nextSibling, this.selectNext);\r
46563          }else if(s.parentNode){\r
46564             var newS = null;\r
46565             s.parentNode.bubble(function(){\r
46566                 if(this.nextSibling){\r
46567                     newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);\r
46568                     return false;\r
46569                 }\r
46570             });\r
46571             return newS;\r
46572          }\r
46573         return null;\r
46574     },\r
46575 \r
46576     onKeyDown : function(e){\r
46577         var s = this.selNode || this.lastSelNode;\r
46578         // undesirable, but required\r
46579         var sm = this;\r
46580         if(!s){\r
46581             return;\r
46582         }\r
46583         var k = e.getKey();\r
46584         switch(k){\r
46585              case e.DOWN:\r
46586                  e.stopEvent();\r
46587                  this.selectNext();\r
46588              break;\r
46589              case e.UP:\r
46590                  e.stopEvent();\r
46591                  this.selectPrevious();\r
46592              break;\r
46593              case e.RIGHT:\r
46594                  e.preventDefault();\r
46595                  if(s.hasChildNodes()){\r
46596                      if(!s.isExpanded()){\r
46597                          s.expand();\r
46598                      }else if(s.firstChild){\r
46599                          this.select(s.firstChild, e);\r
46600                      }\r
46601                  }\r
46602              break;\r
46603              case e.LEFT:\r
46604                  e.preventDefault();\r
46605                  if(s.hasChildNodes() && s.isExpanded()){\r
46606                      s.collapse();\r
46607                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
46608                      this.select(s.parentNode, e);\r
46609                  }\r
46610              break;\r
46611         };\r
46612     }\r
46613 });\r
46614 \r
46615 /**\r
46616  * @class Ext.tree.MultiSelectionModel\r
46617  * @extends Ext.util.Observable\r
46618  * Multi selection for a TreePanel.\r
46619  */\r
46620 Ext.tree.MultiSelectionModel = function(config){\r
46621    this.selNodes = [];\r
46622    this.selMap = {};\r
46623    this.addEvents(\r
46624        /**\r
46625         * @event selectionchange\r
46626         * Fires when the selected nodes change\r
46627         * @param {MultiSelectionModel} this\r
46628         * @param {Array} nodes Array of the selected nodes\r
46629         */\r
46630        "selectionchange"\r
46631    );\r
46632     Ext.apply(this, config);\r
46633     Ext.tree.MultiSelectionModel.superclass.constructor.call(this);\r
46634 };\r
46635 \r
46636 Ext.extend(Ext.tree.MultiSelectionModel, Ext.util.Observable, {\r
46637     init : function(tree){\r
46638         this.tree = tree;\r
46639         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);\r
46640         tree.on("click", this.onNodeClick, this);\r
46641     },\r
46642     \r
46643     onNodeClick : function(node, e){\r
46644         if(e.ctrlKey && this.isSelected(node)){\r
46645             this.unselect(node);\r
46646         }else{\r
46647             this.select(node, e, e.ctrlKey);\r
46648         }\r
46649     },\r
46650     \r
46651     /**\r
46652      * Select a node.\r
46653      * @param {TreeNode} node The node to select\r
46654      * @param {EventObject} e (optional) An event associated with the selection\r
46655      * @param {Boolean} keepExisting True to retain existing selections\r
46656      * @return {TreeNode} The selected node\r
46657      */\r
46658     select : function(node, e, keepExisting){\r
46659         if(keepExisting !== true){\r
46660             this.clearSelections(true);\r
46661         }\r
46662         if(this.isSelected(node)){\r
46663             this.lastSelNode = node;\r
46664             return node;\r
46665         }\r
46666         this.selNodes.push(node);\r
46667         this.selMap[node.id] = node;\r
46668         this.lastSelNode = node;\r
46669         node.ui.onSelectedChange(true);\r
46670         this.fireEvent("selectionchange", this, this.selNodes);\r
46671         return node;\r
46672     },\r
46673     \r
46674     /**\r
46675      * Deselect a node.\r
46676      * @param {TreeNode} node The node to unselect\r
46677      */\r
46678     unselect : function(node){\r
46679         if(this.selMap[node.id]){\r
46680             node.ui.onSelectedChange(false);\r
46681             var sn = this.selNodes;\r
46682             var index = sn.indexOf(node);\r
46683             if(index != -1){\r
46684                 this.selNodes.splice(index, 1);\r
46685             }\r
46686             delete this.selMap[node.id];\r
46687             this.fireEvent("selectionchange", this, this.selNodes);\r
46688         }\r
46689     },\r
46690     \r
46691     /**\r
46692      * Clear all selections\r
46693      */\r
46694     clearSelections : function(suppressEvent){\r
46695         var sn = this.selNodes;\r
46696         if(sn.length > 0){\r
46697             for(var i = 0, len = sn.length; i < len; i++){\r
46698                 sn[i].ui.onSelectedChange(false);\r
46699             }\r
46700             this.selNodes = [];\r
46701             this.selMap = {};\r
46702             if(suppressEvent !== true){\r
46703                 this.fireEvent("selectionchange", this, this.selNodes);\r
46704             }\r
46705         }\r
46706     },\r
46707     \r
46708     /**\r
46709      * Returns true if the node is selected\r
46710      * @param {TreeNode} node The node to check\r
46711      * @return {Boolean}\r
46712      */\r
46713     isSelected : function(node){\r
46714         return this.selMap[node.id] ? true : false;  \r
46715     },\r
46716     \r
46717     /**\r
46718      * Returns an array of the selected nodes\r
46719      * @return {Array}\r
46720      */\r
46721     getSelectedNodes : function(){\r
46722         return this.selNodes;    \r
46723     },\r
46724 \r
46725     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,\r
46726 \r
46727     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,\r
46728 \r
46729     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious\r
46730 });/**\r
46731  * @class Ext.data.Tree\r
46732  * @extends Ext.util.Observable\r
46733  * Represents a tree data structure and bubbles all the events for its nodes. The nodes\r
46734  * in the tree have most standard DOM functionality.\r
46735  * @constructor\r
46736  * @param {Node} root (optional) The root node\r
46737  */\r
46738 Ext.data.Tree = function(root){\r
46739    this.nodeHash = {};\r
46740    /**\r
46741     * The root node for this tree\r
46742     * @type Node\r
46743     */\r
46744    this.root = null;\r
46745    if(root){\r
46746        this.setRootNode(root);\r
46747    }\r
46748    this.addEvents(\r
46749        /**\r
46750         * @event append\r
46751         * Fires when a new child node is appended to a node in this tree.\r
46752         * @param {Tree} tree The owner tree\r
46753         * @param {Node} parent The parent node\r
46754         * @param {Node} node The newly appended node\r
46755         * @param {Number} index The index of the newly appended node\r
46756         */\r
46757        "append",\r
46758        /**\r
46759         * @event remove\r
46760         * Fires when a child node is removed from a node in this tree.\r
46761         * @param {Tree} tree The owner tree\r
46762         * @param {Node} parent The parent node\r
46763         * @param {Node} node The child node removed\r
46764         */\r
46765        "remove",\r
46766        /**\r
46767         * @event move\r
46768         * Fires when a node is moved to a new location in the tree\r
46769         * @param {Tree} tree The owner tree\r
46770         * @param {Node} node The node moved\r
46771         * @param {Node} oldParent The old parent of this node\r
46772         * @param {Node} newParent The new parent of this node\r
46773         * @param {Number} index The index it was moved to\r
46774         */\r
46775        "move",\r
46776        /**\r
46777         * @event insert\r
46778         * Fires when a new child node is inserted in a node in this tree.\r
46779         * @param {Tree} tree The owner tree\r
46780         * @param {Node} parent The parent node\r
46781         * @param {Node} node The child node inserted\r
46782         * @param {Node} refNode The child node the node was inserted before\r
46783         */\r
46784        "insert",\r
46785        /**\r
46786         * @event beforeappend\r
46787         * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
46788         * @param {Tree} tree The owner tree\r
46789         * @param {Node} parent The parent node\r
46790         * @param {Node} node The child node to be appended\r
46791         */\r
46792        "beforeappend",\r
46793        /**\r
46794         * @event beforeremove\r
46795         * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
46796         * @param {Tree} tree The owner tree\r
46797         * @param {Node} parent The parent node\r
46798         * @param {Node} node The child node to be removed\r
46799         */\r
46800        "beforeremove",\r
46801        /**\r
46802         * @event beforemove\r
46803         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
46804         * @param {Tree} tree The owner tree\r
46805         * @param {Node} node The node being moved\r
46806         * @param {Node} oldParent The parent of the node\r
46807         * @param {Node} newParent The new parent the node is moving to\r
46808         * @param {Number} index The index it is being moved to\r
46809         */\r
46810        "beforemove",\r
46811        /**\r
46812         * @event beforeinsert\r
46813         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
46814         * @param {Tree} tree The owner tree\r
46815         * @param {Node} parent The parent node\r
46816         * @param {Node} node The child node to be inserted\r
46817         * @param {Node} refNode The child node the node is being inserted before\r
46818         */\r
46819        "beforeinsert"\r
46820    );\r
46821 \r
46822     Ext.data.Tree.superclass.constructor.call(this);\r
46823 };\r
46824 \r
46825 Ext.extend(Ext.data.Tree, Ext.util.Observable, {\r
46826     /**\r
46827      * @cfg {String} pathSeparator\r
46828      * The token used to separate paths in node ids (defaults to '/').\r
46829      */\r
46830     pathSeparator: "/",\r
46831 \r
46832     // private\r
46833     proxyNodeEvent : function(){\r
46834         return this.fireEvent.apply(this, arguments);\r
46835     },\r
46836 \r
46837     /**\r
46838      * Returns the root node for this tree.\r
46839      * @return {Node}\r
46840      */\r
46841     getRootNode : function(){\r
46842         return this.root;\r
46843     },\r
46844 \r
46845     /**\r
46846      * Sets the root node for this tree.\r
46847      * @param {Node} node\r
46848      * @return {Node}\r
46849      */\r
46850     setRootNode : function(node){\r
46851         this.root = node;\r
46852         node.ownerTree = this;\r
46853         node.isRoot = true;\r
46854         this.registerNode(node);\r
46855         return node;\r
46856     },\r
46857 \r
46858     /**\r
46859      * Gets a node in this tree by its id.\r
46860      * @param {String} id\r
46861      * @return {Node}\r
46862      */\r
46863     getNodeById : function(id){\r
46864         return this.nodeHash[id];\r
46865     },\r
46866 \r
46867     // private\r
46868     registerNode : function(node){\r
46869         this.nodeHash[node.id] = node;\r
46870     },\r
46871 \r
46872     // private\r
46873     unregisterNode : function(node){\r
46874         delete this.nodeHash[node.id];\r
46875     },\r
46876 \r
46877     toString : function(){\r
46878         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
46879     }\r
46880 });\r
46881 \r
46882 /**\r
46883  * @class Ext.data.Node\r
46884  * @extends Ext.util.Observable\r
46885  * @cfg {Boolean} leaf true if this node is a leaf and does not have children\r
46886  * @cfg {String} id The id for this node. If one is not specified, one is generated.\r
46887  * @constructor\r
46888  * @param {Object} attributes The attributes/config for the node\r
46889  */\r
46890 Ext.data.Node = function(attributes){\r
46891     /**\r
46892      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.\r
46893      * @type {Object}\r
46894      */\r
46895     this.attributes = attributes || {};\r
46896     this.leaf = this.attributes.leaf;\r
46897     /**\r
46898      * The node id. @type String\r
46899      */\r
46900     this.id = this.attributes.id;\r
46901     if(!this.id){\r
46902         this.id = Ext.id(null, "xnode-");\r
46903         this.attributes.id = this.id;\r
46904     }\r
46905     /**\r
46906      * All child nodes of this node. @type Array\r
46907      */\r
46908     this.childNodes = [];\r
46909     if(!this.childNodes.indexOf){ // indexOf is a must\r
46910         this.childNodes.indexOf = function(o){\r
46911             for(var i = 0, len = this.length; i < len; i++){\r
46912                 if(this[i] == o){\r
46913                     return i;\r
46914                 }\r
46915             }\r
46916             return -1;\r
46917         };\r
46918     }\r
46919     /**\r
46920      * The parent node for this node. @type Node\r
46921      */\r
46922     this.parentNode = null;\r
46923     /**\r
46924      * The first direct child node of this node, or null if this node has no child nodes. @type Node\r
46925      */\r
46926     this.firstChild = null;\r
46927     /**\r
46928      * The last direct child node of this node, or null if this node has no child nodes. @type Node\r
46929      */\r
46930     this.lastChild = null;\r
46931     /**\r
46932      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node\r
46933      */\r
46934     this.previousSibling = null;\r
46935     /**\r
46936      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node\r
46937      */\r
46938     this.nextSibling = null;\r
46939 \r
46940     this.addEvents({\r
46941        /**\r
46942         * @event append\r
46943         * Fires when a new child node is appended\r
46944         * @param {Tree} tree The owner tree\r
46945         * @param {Node} this This node\r
46946         * @param {Node} node The newly appended node\r
46947         * @param {Number} index The index of the newly appended node\r
46948         */\r
46949        "append" : true,\r
46950        /**\r
46951         * @event remove\r
46952         * Fires when a child node is removed\r
46953         * @param {Tree} tree The owner tree\r
46954         * @param {Node} this This node\r
46955         * @param {Node} node The removed node\r
46956         */\r
46957        "remove" : true,\r
46958        /**\r
46959         * @event move\r
46960         * Fires when this node is moved to a new location in the tree\r
46961         * @param {Tree} tree The owner tree\r
46962         * @param {Node} this This node\r
46963         * @param {Node} oldParent The old parent of this node\r
46964         * @param {Node} newParent The new parent of this node\r
46965         * @param {Number} index The index it was moved to\r
46966         */\r
46967        "move" : true,\r
46968        /**\r
46969         * @event insert\r
46970         * Fires when a new child node is inserted.\r
46971         * @param {Tree} tree The owner tree\r
46972         * @param {Node} this This node\r
46973         * @param {Node} node The child node inserted\r
46974         * @param {Node} refNode The child node the node was inserted before\r
46975         */\r
46976        "insert" : true,\r
46977        /**\r
46978         * @event beforeappend\r
46979         * Fires before a new child is appended, return false to cancel the append.\r
46980         * @param {Tree} tree The owner tree\r
46981         * @param {Node} this This node\r
46982         * @param {Node} node The child node to be appended\r
46983         */\r
46984        "beforeappend" : true,\r
46985        /**\r
46986         * @event beforeremove\r
46987         * Fires before a child is removed, return false to cancel the remove.\r
46988         * @param {Tree} tree The owner tree\r
46989         * @param {Node} this This node\r
46990         * @param {Node} node The child node to be removed\r
46991         */\r
46992        "beforeremove" : true,\r
46993        /**\r
46994         * @event beforemove\r
46995         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.\r
46996         * @param {Tree} tree The owner tree\r
46997         * @param {Node} this This node\r
46998         * @param {Node} oldParent The parent of this node\r
46999         * @param {Node} newParent The new parent this node is moving to\r
47000         * @param {Number} index The index it is being moved to\r
47001         */\r
47002        "beforemove" : true,\r
47003        /**\r
47004         * @event beforeinsert\r
47005         * Fires before a new child is inserted, return false to cancel the insert.\r
47006         * @param {Tree} tree The owner tree\r
47007         * @param {Node} this This node\r
47008         * @param {Node} node The child node to be inserted\r
47009         * @param {Node} refNode The child node the node is being inserted before\r
47010         */\r
47011        "beforeinsert" : true\r
47012    });\r
47013     this.listeners = this.attributes.listeners;\r
47014     Ext.data.Node.superclass.constructor.call(this);\r
47015 };\r
47016 \r
47017 Ext.extend(Ext.data.Node, Ext.util.Observable, {\r
47018     // private\r
47019     fireEvent : function(evtName){\r
47020         // first do standard event for this node\r
47021         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){\r
47022             return false;\r
47023         }\r
47024         // then bubble it up to the tree if the event wasn't cancelled\r
47025         var ot = this.getOwnerTree();\r
47026         if(ot){\r
47027             if(ot.proxyNodeEvent.apply(ot, arguments) === false){\r
47028                 return false;\r
47029             }\r
47030         }\r
47031         return true;\r
47032     },\r
47033 \r
47034     /**\r
47035      * Returns true if this node is a leaf\r
47036      * @return {Boolean}\r
47037      */\r
47038     isLeaf : function(){\r
47039         return this.leaf === true;\r
47040     },\r
47041 \r
47042     // private\r
47043     setFirstChild : function(node){\r
47044         this.firstChild = node;\r
47045     },\r
47046 \r
47047     //private\r
47048     setLastChild : function(node){\r
47049         this.lastChild = node;\r
47050     },\r
47051 \r
47052 \r
47053     /**\r
47054      * Returns true if this node is the last child of its parent\r
47055      * @return {Boolean}\r
47056      */\r
47057     isLast : function(){\r
47058        return (!this.parentNode ? true : this.parentNode.lastChild == this);\r
47059     },\r
47060 \r
47061     /**\r
47062      * Returns true if this node is the first child of its parent\r
47063      * @return {Boolean}\r
47064      */\r
47065     isFirst : function(){\r
47066        return (!this.parentNode ? true : this.parentNode.firstChild == this);\r
47067     },\r
47068 \r
47069     /**\r
47070      * Returns true if this node has one or more child nodes, else false.\r
47071      * @return {Boolean}\r
47072      */\r
47073     hasChildNodes : function(){\r
47074         return !this.isLeaf() && this.childNodes.length > 0;\r
47075     },\r
47076     \r
47077     /**\r
47078      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>\r
47079      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.\r
47080      * @return {Boolean}\r
47081      */\r
47082     isExpandable : function(){\r
47083         return this.attributes.expandable || this.hasChildNodes();\r
47084     },\r
47085 \r
47086     /**\r
47087      * Insert node(s) as the last child node of this node.\r
47088      * @param {Node/Array} node The node or Array of nodes to append\r
47089      * @return {Node} The appended node if single append, or null if an array was passed\r
47090      */\r
47091     appendChild : function(node){\r
47092         var multi = false;\r
47093         if(Ext.isArray(node)){\r
47094             multi = node;\r
47095         }else if(arguments.length > 1){\r
47096             multi = arguments;\r
47097         }\r
47098         // if passed an array or multiple args do them one by one\r
47099         if(multi){\r
47100             for(var i = 0, len = multi.length; i < len; i++) {\r
47101                 this.appendChild(multi[i]);\r
47102             }\r
47103         }else{\r
47104             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){\r
47105                 return false;\r
47106             }\r
47107             var index = this.childNodes.length;\r
47108             var oldParent = node.parentNode;\r
47109             // it's a move, make sure we move it cleanly\r
47110             if(oldParent){\r
47111                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){\r
47112                     return false;\r
47113                 }\r
47114                 oldParent.removeChild(node);\r
47115             }\r
47116             index = this.childNodes.length;\r
47117             if(index === 0){\r
47118                 this.setFirstChild(node);\r
47119             }\r
47120             this.childNodes.push(node);\r
47121             node.parentNode = this;\r
47122             var ps = this.childNodes[index-1];\r
47123             if(ps){\r
47124                 node.previousSibling = ps;\r
47125                 ps.nextSibling = node;\r
47126             }else{\r
47127                 node.previousSibling = null;\r
47128             }\r
47129             node.nextSibling = null;\r
47130             this.setLastChild(node);\r
47131             node.setOwnerTree(this.getOwnerTree());\r
47132             this.fireEvent("append", this.ownerTree, this, node, index);\r
47133             if(oldParent){\r
47134                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);\r
47135             }\r
47136             return node;\r
47137         }\r
47138     },\r
47139 \r
47140     /**\r
47141      * Removes a child node from this node.\r
47142      * @param {Node} node The node to remove\r
47143      * @return {Node} The removed node\r
47144      */\r
47145     removeChild : function(node){\r
47146         var index = this.childNodes.indexOf(node);\r
47147         if(index == -1){\r
47148             return false;\r
47149         }\r
47150         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){\r
47151             return false;\r
47152         }\r
47153 \r
47154         // remove it from childNodes collection\r
47155         this.childNodes.splice(index, 1);\r
47156 \r
47157         // update siblings\r
47158         if(node.previousSibling){\r
47159             node.previousSibling.nextSibling = node.nextSibling;\r
47160         }\r
47161         if(node.nextSibling){\r
47162             node.nextSibling.previousSibling = node.previousSibling;\r
47163         }\r
47164 \r
47165         // update child refs\r
47166         if(this.firstChild == node){\r
47167             this.setFirstChild(node.nextSibling);\r
47168         }\r
47169         if(this.lastChild == node){\r
47170             this.setLastChild(node.previousSibling);\r
47171         }\r
47172 \r
47173         node.setOwnerTree(null);\r
47174         // clear any references from the node\r
47175         node.parentNode = null;\r
47176         node.previousSibling = null;\r
47177         node.nextSibling = null;\r
47178         this.fireEvent("remove", this.ownerTree, this, node);\r
47179         return node;\r
47180     },\r
47181 \r
47182     /**\r
47183      * Inserts the first node before the second node in this nodes childNodes collection.\r
47184      * @param {Node} node The node to insert\r
47185      * @param {Node} refNode The node to insert before (if null the node is appended)\r
47186      * @return {Node} The inserted node\r
47187      */\r
47188     insertBefore : function(node, refNode){\r
47189         if(!refNode){ // like standard Dom, refNode can be null for append\r
47190             return this.appendChild(node);\r
47191         }\r
47192         // nothing to do\r
47193         if(node == refNode){\r
47194             return false;\r
47195         }\r
47196 \r
47197         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){\r
47198             return false;\r
47199         }\r
47200         var index = this.childNodes.indexOf(refNode);\r
47201         var oldParent = node.parentNode;\r
47202         var refIndex = index;\r
47203 \r
47204         // when moving internally, indexes will change after remove\r
47205         if(oldParent == this && this.childNodes.indexOf(node) < index){\r
47206             refIndex--;\r
47207         }\r
47208 \r
47209         // it's a move, make sure we move it cleanly\r
47210         if(oldParent){\r
47211             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){\r
47212                 return false;\r
47213             }\r
47214             oldParent.removeChild(node);\r
47215         }\r
47216         if(refIndex === 0){\r
47217             this.setFirstChild(node);\r
47218         }\r
47219         this.childNodes.splice(refIndex, 0, node);\r
47220         node.parentNode = this;\r
47221         var ps = this.childNodes[refIndex-1];\r
47222         if(ps){\r
47223             node.previousSibling = ps;\r
47224             ps.nextSibling = node;\r
47225         }else{\r
47226             node.previousSibling = null;\r
47227         }\r
47228         node.nextSibling = refNode;\r
47229         refNode.previousSibling = node;\r
47230         node.setOwnerTree(this.getOwnerTree());\r
47231         this.fireEvent("insert", this.ownerTree, this, node, refNode);\r
47232         if(oldParent){\r
47233             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);\r
47234         }\r
47235         return node;\r
47236     },\r
47237 \r
47238     /**\r
47239      * Removes this node from its parent\r
47240      * @return {Node} this\r
47241      */\r
47242     remove : function(){\r
47243         this.parentNode.removeChild(this);\r
47244         return this;\r
47245     },\r
47246 \r
47247     /**\r
47248      * Returns the child node at the specified index.\r
47249      * @param {Number} index\r
47250      * @return {Node}\r
47251      */\r
47252     item : function(index){\r
47253         return this.childNodes[index];\r
47254     },\r
47255 \r
47256     /**\r
47257      * Replaces one child node in this node with another.\r
47258      * @param {Node} newChild The replacement node\r
47259      * @param {Node} oldChild The node to replace\r
47260      * @return {Node} The replaced node\r
47261      */\r
47262     replaceChild : function(newChild, oldChild){\r
47263         var s = oldChild ? oldChild.nextSibling : null;\r
47264         this.removeChild(oldChild);\r
47265         this.insertBefore(newChild, s);\r
47266         return oldChild;\r
47267     },\r
47268 \r
47269     /**\r
47270      * Returns the index of a child node\r
47271      * @param {Node} node\r
47272      * @return {Number} The index of the node or -1 if it was not found\r
47273      */\r
47274     indexOf : function(child){\r
47275         return this.childNodes.indexOf(child);\r
47276     },\r
47277 \r
47278     /**\r
47279      * Returns the tree this node is in.\r
47280      * @return {Tree}\r
47281      */\r
47282     getOwnerTree : function(){\r
47283         // if it doesn't have one, look for one\r
47284         if(!this.ownerTree){\r
47285             var p = this;\r
47286             while(p){\r
47287                 if(p.ownerTree){\r
47288                     this.ownerTree = p.ownerTree;\r
47289                     break;\r
47290                 }\r
47291                 p = p.parentNode;\r
47292             }\r
47293         }\r
47294         return this.ownerTree;\r
47295     },\r
47296 \r
47297     /**\r
47298      * Returns depth of this node (the root node has a depth of 0)\r
47299      * @return {Number}\r
47300      */\r
47301     getDepth : function(){\r
47302         var depth = 0;\r
47303         var p = this;\r
47304         while(p.parentNode){\r
47305             ++depth;\r
47306             p = p.parentNode;\r
47307         }\r
47308         return depth;\r
47309     },\r
47310 \r
47311     // private\r
47312     setOwnerTree : function(tree){\r
47313         // if it is a move, we need to update everyone\r
47314         if(tree != this.ownerTree){\r
47315             if(this.ownerTree){\r
47316                 this.ownerTree.unregisterNode(this);\r
47317             }\r
47318             this.ownerTree = tree;\r
47319             var cs = this.childNodes;\r
47320             for(var i = 0, len = cs.length; i < len; i++) {\r
47321                 cs[i].setOwnerTree(tree);\r
47322             }\r
47323             if(tree){\r
47324                 tree.registerNode(this);\r
47325             }\r
47326         }\r
47327     },\r
47328     \r
47329     /**\r
47330      * Changes the id of this node.\r
47331      * @param {String} id The new id for the node.\r
47332      */\r
47333     setId: function(id){\r
47334         if(id !== this.id){\r
47335             var t = this.ownerTree;\r
47336             if(t){\r
47337                 t.unregisterNode(this);\r
47338             }\r
47339             this.id = this.attributes.id = id;\r
47340             if(t){\r
47341                 t.registerNode(this);\r
47342             }\r
47343             this.onIdChange(id);\r
47344         }\r
47345     },\r
47346     \r
47347     // private\r
47348     onIdChange: Ext.emptyFn,\r
47349 \r
47350     /**\r
47351      * Returns the path for this node. The path can be used to expand or select this node programmatically.\r
47352      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)\r
47353      * @return {String} The path\r
47354      */\r
47355     getPath : function(attr){\r
47356         attr = attr || "id";\r
47357         var p = this.parentNode;\r
47358         var b = [this.attributes[attr]];\r
47359         while(p){\r
47360             b.unshift(p.attributes[attr]);\r
47361             p = p.parentNode;\r
47362         }\r
47363         var sep = this.getOwnerTree().pathSeparator;\r
47364         return sep + b.join(sep);\r
47365     },\r
47366 \r
47367     /**\r
47368      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of\r
47369      * function call will be the scope provided or the current node. The arguments to the function\r
47370      * will be the args provided or the current node. If the function returns false at any point,\r
47371      * the bubble is stopped.\r
47372      * @param {Function} fn The function to call\r
47373      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
47374      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
47375      */\r
47376     bubble : function(fn, scope, args){\r
47377         var p = this;\r
47378         while(p){\r
47379             if(fn.apply(scope || p, args || [p]) === false){\r
47380                 break;\r
47381             }\r
47382             p = p.parentNode;\r
47383         }\r
47384     },\r
47385 \r
47386     /**\r
47387      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of\r
47388      * function call will be the scope provided or the current node. The arguments to the function\r
47389      * will be the args provided or the current node. If the function returns false at any point,\r
47390      * the cascade is stopped on that branch.\r
47391      * @param {Function} fn The function to call\r
47392      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
47393      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
47394      */\r
47395     cascade : function(fn, scope, args){\r
47396         if(fn.apply(scope || this, args || [this]) !== false){\r
47397             var cs = this.childNodes;\r
47398             for(var i = 0, len = cs.length; i < len; i++) {\r
47399                 cs[i].cascade(fn, scope, args);\r
47400             }\r
47401         }\r
47402     },\r
47403 \r
47404     /**\r
47405      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of\r
47406      * function call will be the scope provided or the current node. The arguments to the function\r
47407      * will be the args provided or the current node. If the function returns false at any point,\r
47408      * the iteration stops.\r
47409      * @param {Function} fn The function to call\r
47410      * @param {Object} scope (optional) The scope of the function (defaults to current node)\r
47411      * @param {Array} args (optional) The args to call the function with (default to passing the current node)\r
47412      */\r
47413     eachChild : function(fn, scope, args){\r
47414         var cs = this.childNodes;\r
47415         for(var i = 0, len = cs.length; i < len; i++) {\r
47416                 if(fn.apply(scope || this, args || [cs[i]]) === false){\r
47417                     break;\r
47418                 }\r
47419         }\r
47420     },\r
47421 \r
47422     /**\r
47423      * Finds the first child that has the attribute with the specified value.\r
47424      * @param {String} attribute The attribute name\r
47425      * @param {Mixed} value The value to search for\r
47426      * @return {Node} The found child or null if none was found\r
47427      */\r
47428     findChild : function(attribute, value){\r
47429         var cs = this.childNodes;\r
47430         for(var i = 0, len = cs.length; i < len; i++) {\r
47431                 if(cs[i].attributes[attribute] == value){\r
47432                     return cs[i];\r
47433                 }\r
47434         }\r
47435         return null;\r
47436     },\r
47437 \r
47438     /**\r
47439      * Finds the first child by a custom function. The child matches if the function passed\r
47440      * returns true.\r
47441      * @param {Function} fn\r
47442      * @param {Object} scope (optional)\r
47443      * @return {Node} The found child or null if none was found\r
47444      */\r
47445     findChildBy : function(fn, scope){\r
47446         var cs = this.childNodes;\r
47447         for(var i = 0, len = cs.length; i < len; i++) {\r
47448                 if(fn.call(scope||cs[i], cs[i]) === true){\r
47449                     return cs[i];\r
47450                 }\r
47451         }\r
47452         return null;\r
47453     },\r
47454 \r
47455     /**\r
47456      * Sorts this nodes children using the supplied sort function\r
47457      * @param {Function} fn\r
47458      * @param {Object} scope (optional)\r
47459      */\r
47460     sort : function(fn, scope){\r
47461         var cs = this.childNodes;\r
47462         var len = cs.length;\r
47463         if(len > 0){\r
47464             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;\r
47465             cs.sort(sortFn);\r
47466             for(var i = 0; i < len; i++){\r
47467                 var n = cs[i];\r
47468                 n.previousSibling = cs[i-1];\r
47469                 n.nextSibling = cs[i+1];\r
47470                 if(i === 0){\r
47471                     this.setFirstChild(n);\r
47472                 }\r
47473                 if(i == len-1){\r
47474                     this.setLastChild(n);\r
47475                 }\r
47476             }\r
47477         }\r
47478     },\r
47479 \r
47480     /**\r
47481      * Returns true if this node is an ancestor (at any point) of the passed node.\r
47482      * @param {Node} node\r
47483      * @return {Boolean}\r
47484      */\r
47485     contains : function(node){\r
47486         return node.isAncestor(this);\r
47487     },\r
47488 \r
47489     /**\r
47490      * Returns true if the passed node is an ancestor (at any point) of this node.\r
47491      * @param {Node} node\r
47492      * @return {Boolean}\r
47493      */\r
47494     isAncestor : function(node){\r
47495         var p = this.parentNode;\r
47496         while(p){\r
47497             if(p == node){\r
47498                 return true;\r
47499             }\r
47500             p = p.parentNode;\r
47501         }\r
47502         return false;\r
47503     },\r
47504 \r
47505     toString : function(){\r
47506         return "[Node"+(this.id?" "+this.id:"")+"]";\r
47507     }\r
47508 });/**\r
47509  * @class Ext.tree.TreeNode\r
47510  * @extends Ext.data.Node\r
47511  * @cfg {String} text The text for this node\r
47512  * @cfg {Boolean} expanded true to start the node expanded\r
47513  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)\r
47514  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)\r
47515  * @cfg {Boolean} disabled true to start the node disabled\r
47516  * @cfg {String} icon The path to an icon for the node. The preferred way to do this\r
47517  * is to use the cls or iconCls attributes and add the icon via a CSS background image.\r
47518  * @cfg {String} cls A css class to be added to the node\r
47519  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images\r
47520  * @cfg {String} href URL of the link used for the node (defaults to #)\r
47521  * @cfg {String} hrefTarget target frame for the link\r
47522  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).\r
47523  * @cfg {String} qtip An Ext QuickTip for the node\r
47524  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty\r
47525  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)\r
47526  * @cfg {Boolean} singleClickExpand True for single click expand on this node\r
47527  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)\r
47528  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox\r
47529  * (defaults to undefined with no checkbox rendered)\r
47530  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)\r
47531  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)\r
47532  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)\r
47533  * @cfg {Boolean} editable False to not allow this node to be edited by an (@link Ext.tree.TreeEditor} (defaults to true)\r
47534  * @constructor\r
47535  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node\r
47536  */\r
47537 Ext.tree.TreeNode = function(attributes){\r
47538     attributes = attributes || {};\r
47539     if(typeof attributes == 'string'){\r
47540         attributes = {text: attributes};\r
47541     }\r
47542     this.childrenRendered = false;\r
47543     this.rendered = false;\r
47544     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);\r
47545     this.expanded = attributes.expanded === true;\r
47546     this.isTarget = attributes.isTarget !== false;\r
47547     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;\r
47548     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;\r
47549 \r
47550     /**\r
47551      * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.\r
47552      * @type String\r
47553      */\r
47554     this.text = attributes.text;\r
47555     /**\r
47556      * True if this node is disabled.\r
47557      * @type Boolean\r
47558      */\r
47559     this.disabled = attributes.disabled === true;\r
47560     /**\r
47561      * True if this node is hidden.\r
47562      * @type Boolean\r
47563      */\r
47564     this.hidden = attributes.hidden === true;\r
47565 \r
47566     this.addEvents(\r
47567         /**\r
47568         * @event textchange\r
47569         * Fires when the text for this node is changed\r
47570         * @param {Node} this This node\r
47571         * @param {String} text The new text\r
47572         * @param {String} oldText The old text\r
47573         */\r
47574         'textchange',\r
47575         /**\r
47576         * @event beforeexpand\r
47577         * Fires before this node is expanded, return false to cancel.\r
47578         * @param {Node} this This node\r
47579         * @param {Boolean} deep\r
47580         * @param {Boolean} anim\r
47581         */\r
47582         'beforeexpand',\r
47583         /**\r
47584         * @event beforecollapse\r
47585         * Fires before this node is collapsed, return false to cancel.\r
47586         * @param {Node} this This node\r
47587         * @param {Boolean} deep\r
47588         * @param {Boolean} anim\r
47589         */\r
47590         'beforecollapse',\r
47591         /**\r
47592         * @event expand\r
47593         * Fires when this node is expanded\r
47594         * @param {Node} this This node\r
47595         */\r
47596         'expand',\r
47597         /**\r
47598         * @event disabledchange\r
47599         * Fires when the disabled status of this node changes\r
47600         * @param {Node} this This node\r
47601         * @param {Boolean} disabled\r
47602         */\r
47603         'disabledchange',\r
47604         /**\r
47605         * @event collapse\r
47606         * Fires when this node is collapsed\r
47607         * @param {Node} this This node\r
47608         */\r
47609         'collapse',\r
47610         /**\r
47611         * @event beforeclick\r
47612         * Fires before click processing. Return false to cancel the default action.\r
47613         * @param {Node} this This node\r
47614         * @param {Ext.EventObject} e The event object\r
47615         */\r
47616         'beforeclick',\r
47617         /**\r
47618         * @event click\r
47619         * Fires when this node is clicked\r
47620         * @param {Node} this This node\r
47621         * @param {Ext.EventObject} e The event object\r
47622         */\r
47623         'click',\r
47624         /**\r
47625         * @event checkchange\r
47626         * Fires when a node with a checkbox's checked property changes\r
47627         * @param {Node} this This node\r
47628         * @param {Boolean} checked\r
47629         */\r
47630         'checkchange',\r
47631         /**\r
47632         * @event beforedblclick\r
47633         * Fires before double click processing. Return false to cancel the default action.\r
47634         * @param {Node} this This node\r
47635         * @param {Ext.EventObject} e The event object\r
47636         */\r
47637         'beforedblclick',\r
47638         /**\r
47639         * @event dblclick\r
47640         * Fires when this node is double clicked\r
47641         * @param {Node} this This node\r
47642         * @param {Ext.EventObject} e The event object\r
47643         */\r
47644         'dblclick',\r
47645         /**\r
47646         * @event contextmenu\r
47647         * Fires when this node is right clicked\r
47648         * @param {Node} this This node\r
47649         * @param {Ext.EventObject} e The event object\r
47650         */\r
47651         'contextmenu',\r
47652         /**\r
47653         * @event beforechildrenrendered\r
47654         * Fires right before the child nodes for this node are rendered\r
47655         * @param {Node} this This node\r
47656         */\r
47657         'beforechildrenrendered'\r
47658     );\r
47659 \r
47660     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;\r
47661 \r
47662     /**\r
47663      * Read-only. The UI for this node\r
47664      * @type TreeNodeUI\r
47665      */\r
47666     this.ui = new uiClass(this);\r
47667 };\r
47668 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {\r
47669     preventHScroll : true,\r
47670     /**\r
47671      * Returns true if this node is expanded\r
47672      * @return {Boolean}\r
47673      */\r
47674     isExpanded : function(){\r
47675         return this.expanded;\r
47676     },\r
47677 \r
47678 /**\r
47679  * Returns the UI object for this node.\r
47680  * @return {TreeNodeUI} The object which is providing the user interface for this tree\r
47681  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance\r
47682  * of {@link Ext.tree.TreeNodeUI}\r
47683  */\r
47684     getUI : function(){\r
47685         return this.ui;\r
47686     },\r
47687 \r
47688     getLoader : function(){\r
47689         var owner;\r
47690         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : new Ext.tree.TreeLoader());\r
47691     },\r
47692 \r
47693     // private override\r
47694     setFirstChild : function(node){\r
47695         var of = this.firstChild;\r
47696         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);\r
47697         if(this.childrenRendered && of && node != of){\r
47698             of.renderIndent(true, true);\r
47699         }\r
47700         if(this.rendered){\r
47701             this.renderIndent(true, true);\r
47702         }\r
47703     },\r
47704 \r
47705     // private override\r
47706     setLastChild : function(node){\r
47707         var ol = this.lastChild;\r
47708         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);\r
47709         if(this.childrenRendered && ol && node != ol){\r
47710             ol.renderIndent(true, true);\r
47711         }\r
47712         if(this.rendered){\r
47713             this.renderIndent(true, true);\r
47714         }\r
47715     },\r
47716 \r
47717     // these methods are overridden to provide lazy rendering support\r
47718     // private override\r
47719     appendChild : function(n){\r
47720         if(!n.render && !Ext.isArray(n)){\r
47721             n = this.getLoader().createNode(n);\r
47722         }\r
47723         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);\r
47724         if(node && this.childrenRendered){\r
47725             node.render();\r
47726         }\r
47727         this.ui.updateExpandIcon();\r
47728         return node;\r
47729     },\r
47730 \r
47731     // private override\r
47732     removeChild : function(node){\r
47733         this.ownerTree.getSelectionModel().unselect(node);\r
47734         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);\r
47735         // if it's been rendered remove dom node\r
47736         if(this.childrenRendered){\r
47737             node.ui.remove();\r
47738         }\r
47739         if(this.childNodes.length < 1){\r
47740             this.collapse(false, false);\r
47741         }else{\r
47742             this.ui.updateExpandIcon();\r
47743         }\r
47744         if(!this.firstChild && !this.isHiddenRoot()) {\r
47745             this.childrenRendered = false;\r
47746         }\r
47747         return node;\r
47748     },\r
47749 \r
47750     // private override\r
47751     insertBefore : function(node, refNode){\r
47752         if(!node.render){\r
47753             node = this.getLoader().createNode(node);\r
47754         }\r
47755         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);\r
47756         if(newNode && refNode && this.childrenRendered){\r
47757             node.render();\r
47758         }\r
47759         this.ui.updateExpandIcon();\r
47760         return newNode;\r
47761     },\r
47762 \r
47763     /**\r
47764      * Sets the text for this node\r
47765      * @param {String} text\r
47766      */\r
47767     setText : function(text){\r
47768         var oldText = this.text;\r
47769         this.text = text;\r
47770         this.attributes.text = text;\r
47771         if(this.rendered){ // event without subscribing\r
47772             this.ui.onTextChange(this, text, oldText);\r
47773         }\r
47774         this.fireEvent('textchange', this, text, oldText);\r
47775     },\r
47776 \r
47777     /**\r
47778      * Triggers selection of this node\r
47779      */\r
47780     select : function(){\r
47781         this.getOwnerTree().getSelectionModel().select(this);\r
47782     },\r
47783 \r
47784     /**\r
47785      * Triggers deselection of this node\r
47786      */\r
47787     unselect : function(){\r
47788         this.getOwnerTree().getSelectionModel().unselect(this);\r
47789     },\r
47790 \r
47791     /**\r
47792      * Returns true if this node is selected\r
47793      * @return {Boolean}\r
47794      */\r
47795     isSelected : function(){\r
47796         return this.getOwnerTree().getSelectionModel().isSelected(this);\r
47797     },\r
47798 \r
47799     /**\r
47800      * Expand this node.\r
47801      * @param {Boolean} deep (optional) True to expand all children as well\r
47802      * @param {Boolean} anim (optional) false to cancel the default animation\r
47803      * @param {Function} callback (optional) A callback to be called when\r
47804      * expanding this node completes (does not wait for deep expand to complete).\r
47805      * Called with 1 parameter, this node.\r
47806      * @param {Object} scope (optional) The scope in which to execute the callback.\r
47807      */\r
47808     expand : function(deep, anim, callback, scope){\r
47809         if(!this.expanded){\r
47810             if(this.fireEvent('beforeexpand', this, deep, anim) === false){\r
47811                 return;\r
47812             }\r
47813             if(!this.childrenRendered){\r
47814                 this.renderChildren();\r
47815             }\r
47816             this.expanded = true;\r
47817             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){\r
47818                 this.ui.animExpand(function(){\r
47819                     this.fireEvent('expand', this);\r
47820                     this.runCallback(callback, scope || this, [this]);\r
47821                     if(deep === true){\r
47822                         this.expandChildNodes(true);\r
47823                     }\r
47824                 }.createDelegate(this));\r
47825                 return;\r
47826             }else{\r
47827                 this.ui.expand();\r
47828                 this.fireEvent('expand', this);\r
47829                 this.runCallback(callback, scope || this, [this]);\r
47830             }\r
47831         }else{\r
47832            this.runCallback(callback, scope || this, [this]);\r
47833         }\r
47834         if(deep === true){\r
47835             this.expandChildNodes(true);\r
47836         }\r
47837     },\r
47838 \r
47839     runCallback : function(cb, scope, args){\r
47840         if(Ext.isFunction(cb)){\r
47841             cb.apply(scope, args);\r
47842         }\r
47843     },\r
47844 \r
47845     isHiddenRoot : function(){\r
47846         return this.isRoot && !this.getOwnerTree().rootVisible;\r
47847     },\r
47848 \r
47849     /**\r
47850      * Collapse this node.\r
47851      * @param {Boolean} deep (optional) True to collapse all children as well\r
47852      * @param {Boolean} anim (optional) false to cancel the default animation\r
47853      * @param {Function} callback (optional) A callback to be called when\r
47854      * expanding this node completes (does not wait for deep expand to complete).\r
47855      * Called with 1 parameter, this node.\r
47856      * @param {Object} scope (optional) The scope in which to execute the callback.\r
47857      */\r
47858     collapse : function(deep, anim, callback, scope){\r
47859         if(this.expanded && !this.isHiddenRoot()){\r
47860             if(this.fireEvent('beforecollapse', this, deep, anim) === false){\r
47861                 return;\r
47862             }\r
47863             this.expanded = false;\r
47864             if((this.getOwnerTree().animate && anim !== false) || anim){\r
47865                 this.ui.animCollapse(function(){\r
47866                     this.fireEvent('collapse', this);\r
47867                     this.runCallback(callback, scope || this, [this]);\r
47868                     if(deep === true){\r
47869                         this.collapseChildNodes(true);\r
47870                     }\r
47871                 }.createDelegate(this));\r
47872                 return;\r
47873             }else{\r
47874                 this.ui.collapse();\r
47875                 this.fireEvent('collapse', this);\r
47876                 this.runCallback(callback, scope || this, [this]);\r
47877             }\r
47878         }else if(!this.expanded){\r
47879             this.runCallback(callback, scope || this, [this]);\r
47880         }\r
47881         if(deep === true){\r
47882             var cs = this.childNodes;\r
47883             for(var i = 0, len = cs.length; i < len; i++) {\r
47884                 cs[i].collapse(true, false);\r
47885             }\r
47886         }\r
47887     },\r
47888 \r
47889     // private\r
47890     delayedExpand : function(delay){\r
47891         if(!this.expandProcId){\r
47892             this.expandProcId = this.expand.defer(delay, this);\r
47893         }\r
47894     },\r
47895 \r
47896     // private\r
47897     cancelExpand : function(){\r
47898         if(this.expandProcId){\r
47899             clearTimeout(this.expandProcId);\r
47900         }\r
47901         this.expandProcId = false;\r
47902     },\r
47903 \r
47904     /**\r
47905      * Toggles expanded/collapsed state of the node\r
47906      */\r
47907     toggle : function(){\r
47908         if(this.expanded){\r
47909             this.collapse();\r
47910         }else{\r
47911             this.expand();\r
47912         }\r
47913     },\r
47914 \r
47915     /**\r
47916      * Ensures all parent nodes are expanded, and if necessary, scrolls\r
47917      * the node into view.\r
47918      * @param {Function} callback (optional) A function to call when the node has been made visible.\r
47919      * @param {Object} scope (optional) The scope in which to execute the callback.\r
47920      */\r
47921     ensureVisible : function(callback, scope){\r
47922         var tree = this.getOwnerTree();\r
47923         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){\r
47924             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime\r
47925             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);\r
47926             this.runCallback(callback, scope || this, [this]);\r
47927         }.createDelegate(this));\r
47928     },\r
47929 \r
47930     /**\r
47931      * Expand all child nodes\r
47932      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes\r
47933      */\r
47934     expandChildNodes : function(deep){\r
47935         var cs = this.childNodes;\r
47936         for(var i = 0, len = cs.length; i < len; i++) {\r
47937                 cs[i].expand(deep);\r
47938         }\r
47939     },\r
47940 \r
47941     /**\r
47942      * Collapse all child nodes\r
47943      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes\r
47944      */\r
47945     collapseChildNodes : function(deep){\r
47946         var cs = this.childNodes;\r
47947         for(var i = 0, len = cs.length; i < len; i++) {\r
47948                 cs[i].collapse(deep);\r
47949         }\r
47950     },\r
47951 \r
47952     /**\r
47953      * Disables this node\r
47954      */\r
47955     disable : function(){\r
47956         this.disabled = true;\r
47957         this.unselect();\r
47958         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
47959             this.ui.onDisableChange(this, true);\r
47960         }\r
47961         this.fireEvent('disabledchange', this, true);\r
47962     },\r
47963 \r
47964     /**\r
47965      * Enables this node\r
47966      */\r
47967     enable : function(){\r
47968         this.disabled = false;\r
47969         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
47970             this.ui.onDisableChange(this, false);\r
47971         }\r
47972         this.fireEvent('disabledchange', this, false);\r
47973     },\r
47974 \r
47975     // private\r
47976     renderChildren : function(suppressEvent){\r
47977         if(suppressEvent !== false){\r
47978             this.fireEvent('beforechildrenrendered', this);\r
47979         }\r
47980         var cs = this.childNodes;\r
47981         for(var i = 0, len = cs.length; i < len; i++){\r
47982             cs[i].render(true);\r
47983         }\r
47984         this.childrenRendered = true;\r
47985     },\r
47986 \r
47987     // private\r
47988     sort : function(fn, scope){\r
47989         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);\r
47990         if(this.childrenRendered){\r
47991             var cs = this.childNodes;\r
47992             for(var i = 0, len = cs.length; i < len; i++){\r
47993                 cs[i].render(true);\r
47994             }\r
47995         }\r
47996     },\r
47997 \r
47998     // private\r
47999     render : function(bulkRender){\r
48000         this.ui.render(bulkRender);\r
48001         if(!this.rendered){\r
48002             // make sure it is registered\r
48003             this.getOwnerTree().registerNode(this);\r
48004             this.rendered = true;\r
48005             if(this.expanded){\r
48006                 this.expanded = false;\r
48007                 this.expand(false, false);\r
48008             }\r
48009         }\r
48010     },\r
48011 \r
48012     // private\r
48013     renderIndent : function(deep, refresh){\r
48014         if(refresh){\r
48015             this.ui.childIndent = null;\r
48016         }\r
48017         this.ui.renderIndent();\r
48018         if(deep === true && this.childrenRendered){\r
48019             var cs = this.childNodes;\r
48020             for(var i = 0, len = cs.length; i < len; i++){\r
48021                 cs[i].renderIndent(true, refresh);\r
48022             }\r
48023         }\r
48024     },\r
48025 \r
48026     beginUpdate : function(){\r
48027         this.childrenRendered = false;\r
48028     },\r
48029 \r
48030     endUpdate : function(){\r
48031         if(this.expanded && this.rendered){\r
48032             this.renderChildren();\r
48033         }\r
48034     },\r
48035 \r
48036     destroy : function(){\r
48037         if(this.childNodes){\r
48038             for(var i = 0,l = this.childNodes.length; i < l; i++){\r
48039                 this.childNodes[i].destroy();\r
48040             }\r
48041             this.childNodes = null;\r
48042         }\r
48043         if(this.ui.destroy){\r
48044             this.ui.destroy();\r
48045         }\r
48046     },\r
48047 \r
48048     // private\r
48049     onIdChange : function(id){\r
48050         this.ui.onIdChange(id);\r
48051     }\r
48052 });\r
48053 \r
48054 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**\r
48055  * @class Ext.tree.AsyncTreeNode\r
48056  * @extends Ext.tree.TreeNode\r
48057  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)\r
48058  * @constructor\r
48059  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node \r
48060  */\r
48061  Ext.tree.AsyncTreeNode = function(config){\r
48062     this.loaded = config && config.loaded === true;\r
48063     this.loading = false;\r
48064     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);\r
48065     /**\r
48066     * @event beforeload\r
48067     * Fires before this node is loaded, return false to cancel\r
48068     * @param {Node} this This node\r
48069     */\r
48070     this.addEvents('beforeload', 'load');\r
48071     /**\r
48072     * @event load\r
48073     * Fires when this node is loaded\r
48074     * @param {Node} this This node\r
48075     */\r
48076     /**\r
48077      * The loader used by this node (defaults to using the tree's defined loader)\r
48078      * @type TreeLoader\r
48079      * @property loader\r
48080      */\r
48081 };\r
48082 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {\r
48083     expand : function(deep, anim, callback, scope){\r
48084         if(this.loading){ // if an async load is already running, waiting til it's done\r
48085             var timer;\r
48086             var f = function(){\r
48087                 if(!this.loading){ // done loading\r
48088                     clearInterval(timer);\r
48089                     this.expand(deep, anim, callback, scope);\r
48090                 }\r
48091             }.createDelegate(this);\r
48092             timer = setInterval(f, 200);\r
48093             return;\r
48094         }\r
48095         if(!this.loaded){\r
48096             if(this.fireEvent("beforeload", this) === false){\r
48097                 return;\r
48098             }\r
48099             this.loading = true;\r
48100             this.ui.beforeLoad(this);\r
48101             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();\r
48102             if(loader){\r
48103                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);\r
48104                 return;\r
48105             }\r
48106         }\r
48107         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);\r
48108     },\r
48109     \r
48110     /**\r
48111      * Returns true if this node is currently loading\r
48112      * @return {Boolean}\r
48113      */\r
48114     isLoading : function(){\r
48115         return this.loading;  \r
48116     },\r
48117     \r
48118     loadComplete : function(deep, anim, callback, scope){\r
48119         this.loading = false;\r
48120         this.loaded = true;\r
48121         this.ui.afterLoad(this);\r
48122         this.fireEvent("load", this);\r
48123         this.expand(deep, anim, callback, scope);\r
48124     },\r
48125     \r
48126     /**\r
48127      * Returns true if this node has been loaded\r
48128      * @return {Boolean}\r
48129      */\r
48130     isLoaded : function(){\r
48131         return this.loaded;\r
48132     },\r
48133     \r
48134     hasChildNodes : function(){\r
48135         if(!this.isLeaf() && !this.loaded){\r
48136             return true;\r
48137         }else{\r
48138             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);\r
48139         }\r
48140     },\r
48141 \r
48142     /**\r
48143      * Trigger a reload for this node\r
48144      * @param {Function} callback\r
48145      * @param {Object} scope (optional) The scope in which to execute the callback.\r
48146      */\r
48147     reload : function(callback, scope){\r
48148         this.collapse(false, false);\r
48149         while(this.firstChild){\r
48150             this.removeChild(this.firstChild).destroy();\r
48151         }\r
48152         this.childrenRendered = false;\r
48153         this.loaded = false;\r
48154         if(this.isHiddenRoot()){\r
48155             this.expanded = false;\r
48156         }\r
48157         this.expand(false, false, callback, scope);\r
48158     }\r
48159 });\r
48160 \r
48161 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**\r
48162  * @class Ext.tree.TreeNodeUI\r
48163  * This class provides the default UI implementation for Ext TreeNodes.\r
48164  * The TreeNode UI implementation is separate from the\r
48165  * tree implementation, and allows customizing of the appearance of\r
48166  * tree nodes.<br>\r
48167  * <p>\r
48168  * If you are customizing the Tree's user interface, you\r
48169  * may need to extend this class, but you should never need to instantiate this class.<br>\r
48170  * <p>\r
48171  * This class provides access to the user interface components of an Ext TreeNode, through\r
48172  * {@link Ext.tree.TreeNode#getUI}\r
48173  */\r
48174 Ext.tree.TreeNodeUI = function(node){\r
48175     this.node = node;\r
48176     this.rendered = false;\r
48177     this.animating = false;\r
48178     this.wasLeaf = true;\r
48179     this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
48180     this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
48181 };\r
48182 \r
48183 Ext.tree.TreeNodeUI.prototype = {\r
48184     // private\r
48185     removeChild : function(node){\r
48186         if(this.rendered){\r
48187             this.ctNode.removeChild(node.ui.getEl());\r
48188         } \r
48189     },\r
48190 \r
48191     // private\r
48192     beforeLoad : function(){\r
48193          this.addClass("x-tree-node-loading");\r
48194     },\r
48195 \r
48196     // private\r
48197     afterLoad : function(){\r
48198          this.removeClass("x-tree-node-loading");\r
48199     },\r
48200 \r
48201     // private\r
48202     onTextChange : function(node, text, oldText){\r
48203         if(this.rendered){\r
48204             this.textNode.innerHTML = text;\r
48205         }\r
48206     },\r
48207 \r
48208     // private\r
48209     onDisableChange : function(node, state){\r
48210         this.disabled = state;\r
48211                 if (this.checkbox) {\r
48212                         this.checkbox.disabled = state;\r
48213                 }        \r
48214         if(state){\r
48215             this.addClass("x-tree-node-disabled");\r
48216         }else{\r
48217             this.removeClass("x-tree-node-disabled");\r
48218         } \r
48219     },\r
48220 \r
48221     // private\r
48222     onSelectedChange : function(state){\r
48223         if(state){\r
48224             this.focus();\r
48225             this.addClass("x-tree-selected");\r
48226         }else{\r
48227             //this.blur();\r
48228             this.removeClass("x-tree-selected");\r
48229         }\r
48230     },\r
48231 \r
48232     // private\r
48233     onMove : function(tree, node, oldParent, newParent, index, refNode){\r
48234         this.childIndent = null;\r
48235         if(this.rendered){\r
48236             var targetNode = newParent.ui.getContainer();\r
48237             if(!targetNode){//target not rendered\r
48238                 this.holder = document.createElement("div");\r
48239                 this.holder.appendChild(this.wrap);\r
48240                 return;\r
48241             }\r
48242             var insertBefore = refNode ? refNode.ui.getEl() : null;\r
48243             if(insertBefore){\r
48244                 targetNode.insertBefore(this.wrap, insertBefore);\r
48245             }else{\r
48246                 targetNode.appendChild(this.wrap);\r
48247             }\r
48248             this.node.renderIndent(true, oldParent != newParent);\r
48249         }\r
48250     },\r
48251 \r
48252 /**\r
48253  * Adds one or more CSS classes to the node's UI element.\r
48254  * Duplicate classes are automatically filtered out.\r
48255  * @param {String/Array} className The CSS class to add, or an array of classes\r
48256  */\r
48257     addClass : function(cls){\r
48258         if(this.elNode){\r
48259             Ext.fly(this.elNode).addClass(cls);\r
48260         }\r
48261     },\r
48262 \r
48263 /**\r
48264  * Removes one or more CSS classes from the node's UI element.\r
48265  * @param {String/Array} className The CSS class to remove, or an array of classes\r
48266  */\r
48267     removeClass : function(cls){\r
48268         if(this.elNode){\r
48269             Ext.fly(this.elNode).removeClass(cls);  \r
48270         }\r
48271     },\r
48272 \r
48273     // private\r
48274     remove : function(){\r
48275         if(this.rendered){\r
48276             this.holder = document.createElement("div");\r
48277             this.holder.appendChild(this.wrap);\r
48278         }  \r
48279     },\r
48280 \r
48281     // private\r
48282     fireEvent : function(){\r
48283         return this.node.fireEvent.apply(this.node, arguments);  \r
48284     },\r
48285 \r
48286     // private\r
48287     initEvents : function(){\r
48288         this.node.on("move", this.onMove, this);\r
48289 \r
48290         if(this.node.disabled){\r
48291             this.onDisableChange(this.node, true);            \r
48292         }\r
48293         if(this.node.hidden){\r
48294             this.hide();\r
48295         }\r
48296         var ot = this.node.getOwnerTree();\r
48297         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
48298         if(dd && (!this.node.isRoot || ot.rootVisible)){\r
48299             Ext.dd.Registry.register(this.elNode, {\r
48300                 node: this.node,\r
48301                 handles: this.getDDHandles(),\r
48302                 isHandle: false\r
48303             });\r
48304         }\r
48305     },\r
48306 \r
48307     // private\r
48308     getDDHandles : function(){\r
48309         return [this.iconNode, this.textNode, this.elNode];\r
48310     },\r
48311 \r
48312 /**\r
48313  * Hides this node.\r
48314  */\r
48315     hide : function(){\r
48316         this.node.hidden = true;\r
48317         if(this.wrap){\r
48318             this.wrap.style.display = "none";\r
48319         }\r
48320     },\r
48321 \r
48322 /**\r
48323  * Shows this node.\r
48324  */\r
48325     show : function(){\r
48326         this.node.hidden = false;\r
48327         if(this.wrap){\r
48328             this.wrap.style.display = "";\r
48329         } \r
48330     },\r
48331 \r
48332     // private\r
48333     onContextMenu : function(e){\r
48334         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
48335             e.preventDefault();\r
48336             this.focus();\r
48337             this.fireEvent("contextmenu", this.node, e);\r
48338         }\r
48339     },\r
48340 \r
48341     // private\r
48342     onClick : function(e){\r
48343         if(this.dropping){\r
48344             e.stopEvent();\r
48345             return;\r
48346         }\r
48347         if(this.fireEvent("beforeclick", this.node, e) !== false){\r
48348             var a = e.getTarget('a');\r
48349             if(!this.disabled && this.node.attributes.href && a){\r
48350                 this.fireEvent("click", this.node, e);\r
48351                 return;\r
48352             }else if(a && e.ctrlKey){\r
48353                 e.stopEvent();\r
48354             }\r
48355             e.preventDefault();\r
48356             if(this.disabled){\r
48357                 return;\r
48358             }\r
48359 \r
48360             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
48361                 this.node.toggle();\r
48362             }\r
48363 \r
48364             this.fireEvent("click", this.node, e);\r
48365         }else{\r
48366             e.stopEvent();\r
48367         }\r
48368     },\r
48369 \r
48370     // private\r
48371     onDblClick : function(e){\r
48372         e.preventDefault();\r
48373         if(this.disabled){\r
48374             return;\r
48375         }\r
48376         if(this.fireEvent("beforedblclick", this.node, e) !== false){\r
48377             if(this.checkbox){\r
48378                 this.toggleCheck();\r
48379             }\r
48380             if(!this.animating && this.node.isExpandable()){\r
48381                 this.node.toggle();\r
48382             }\r
48383             this.fireEvent("dblclick", this.node, e);\r
48384         }\r
48385     },\r
48386 \r
48387     onOver : function(e){\r
48388         this.addClass('x-tree-node-over');\r
48389     },\r
48390 \r
48391     onOut : function(e){\r
48392         this.removeClass('x-tree-node-over');\r
48393     },\r
48394 \r
48395     // private\r
48396     onCheckChange : function(){\r
48397         var checked = this.checkbox.checked;\r
48398                 // fix for IE6\r
48399                 this.checkbox.defaultChecked = checked;         \r
48400         this.node.attributes.checked = checked;\r
48401         this.fireEvent('checkchange', this.node, checked);\r
48402     },\r
48403 \r
48404     // private\r
48405     ecClick : function(e){\r
48406         if(!this.animating && this.node.isExpandable()){\r
48407             this.node.toggle();\r
48408         }\r
48409     },\r
48410 \r
48411     // private\r
48412     startDrop : function(){\r
48413         this.dropping = true;\r
48414     },\r
48415     \r
48416     // delayed drop so the click event doesn't get fired on a drop\r
48417     endDrop : function(){ \r
48418        setTimeout(function(){\r
48419            this.dropping = false;\r
48420        }.createDelegate(this), 50); \r
48421     },\r
48422 \r
48423     // private\r
48424     expand : function(){\r
48425         this.updateExpandIcon();\r
48426         this.ctNode.style.display = "";\r
48427     },\r
48428 \r
48429     // private\r
48430     focus : function(){\r
48431         if(!this.node.preventHScroll){\r
48432             try{this.anchor.focus();\r
48433             }catch(e){}\r
48434         }else{\r
48435             try{\r
48436                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;\r
48437                 var l = noscroll.scrollLeft;\r
48438                 this.anchor.focus();\r
48439                 noscroll.scrollLeft = l;\r
48440             }catch(e){}\r
48441         }\r
48442     },\r
48443 \r
48444 /**\r
48445  * Sets the checked status of the tree node to the passed value, or, if no value was passed,\r
48446  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.\r
48447  * @param {Boolean} (optional) The new checked status.\r
48448  */\r
48449     toggleCheck : function(value){\r
48450         var cb = this.checkbox;\r
48451         if(cb){\r
48452             cb.checked = (value === undefined ? !cb.checked : value);\r
48453             this.onCheckChange();\r
48454         }\r
48455     },\r
48456 \r
48457     // private\r
48458     blur : function(){\r
48459         try{\r
48460             this.anchor.blur();\r
48461         }catch(e){} \r
48462     },\r
48463 \r
48464     // private\r
48465     animExpand : function(callback){\r
48466         var ct = Ext.get(this.ctNode);\r
48467         ct.stopFx();\r
48468         if(!this.node.isExpandable()){\r
48469             this.updateExpandIcon();\r
48470             this.ctNode.style.display = "";\r
48471             Ext.callback(callback);\r
48472             return;\r
48473         }\r
48474         this.animating = true;\r
48475         this.updateExpandIcon();\r
48476         \r
48477         ct.slideIn('t', {\r
48478            callback : function(){\r
48479                this.animating = false;\r
48480                Ext.callback(callback);\r
48481             },\r
48482             scope: this,\r
48483             duration: this.node.ownerTree.duration || .25\r
48484         });\r
48485     },\r
48486 \r
48487     // private\r
48488     highlight : function(){\r
48489         var tree = this.node.getOwnerTree();\r
48490         Ext.fly(this.wrap).highlight(\r
48491             tree.hlColor || "C3DAF9",\r
48492             {endColor: tree.hlBaseColor}\r
48493         );\r
48494     },\r
48495 \r
48496     // private\r
48497     collapse : function(){\r
48498         this.updateExpandIcon();\r
48499         this.ctNode.style.display = "none";\r
48500     },\r
48501 \r
48502     // private\r
48503     animCollapse : function(callback){\r
48504         var ct = Ext.get(this.ctNode);\r
48505         ct.enableDisplayMode('block');\r
48506         ct.stopFx();\r
48507 \r
48508         this.animating = true;\r
48509         this.updateExpandIcon();\r
48510 \r
48511         ct.slideOut('t', {\r
48512             callback : function(){\r
48513                this.animating = false;\r
48514                Ext.callback(callback);\r
48515             },\r
48516             scope: this,\r
48517             duration: this.node.ownerTree.duration || .25\r
48518         });\r
48519     },\r
48520 \r
48521     // private\r
48522     getContainer : function(){\r
48523         return this.ctNode;  \r
48524     },\r
48525 \r
48526     // private\r
48527     getEl : function(){\r
48528         return this.wrap;  \r
48529     },\r
48530 \r
48531     // private\r
48532     appendDDGhost : function(ghostNode){\r
48533         ghostNode.appendChild(this.elNode.cloneNode(true));\r
48534     },\r
48535 \r
48536     // private\r
48537     getDDRepairXY : function(){\r
48538         return Ext.lib.Dom.getXY(this.iconNode);\r
48539     },\r
48540 \r
48541     // private\r
48542     onRender : function(){\r
48543         this.render();    \r
48544     },\r
48545 \r
48546     // private\r
48547     render : function(bulkRender){\r
48548         var n = this.node, a = n.attributes;\r
48549         var targetNode = n.parentNode ? \r
48550               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
48551         \r
48552         if(!this.rendered){\r
48553             this.rendered = true;\r
48554 \r
48555             this.renderElements(n, a, targetNode, bulkRender);\r
48556 \r
48557             if(a.qtip){\r
48558                if(this.textNode.setAttributeNS){\r
48559                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
48560                    if(a.qtipTitle){\r
48561                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
48562                    }\r
48563                }else{\r
48564                    this.textNode.setAttribute("ext:qtip", a.qtip);\r
48565                    if(a.qtipTitle){\r
48566                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
48567                    }\r
48568                } \r
48569             }else if(a.qtipCfg){\r
48570                 a.qtipCfg.target = Ext.id(this.textNode);\r
48571                 Ext.QuickTips.register(a.qtipCfg);\r
48572             }\r
48573             this.initEvents();\r
48574             if(!this.node.expanded){\r
48575                 this.updateExpandIcon(true);\r
48576             }\r
48577         }else{\r
48578             if(bulkRender === true) {\r
48579                 targetNode.appendChild(this.wrap);\r
48580             }\r
48581         }\r
48582     },\r
48583 \r
48584     // private\r
48585     renderElements : function(n, a, targetNode, bulkRender){\r
48586         // add some indent caching, this helps performance when rendering a large tree\r
48587         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
48588 \r
48589         var cb = typeof a.checked == 'boolean';\r
48590 \r
48591         var href = a.href ? a.href : Ext.isGecko ? "" : "#";\r
48592         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
48593             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
48594             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
48595             '<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
48596             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
48597             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
48598              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
48599             '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
48600             "</li>"].join('');\r
48601 \r
48602         var nel;\r
48603         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
48604             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
48605         }else{\r
48606             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
48607         }\r
48608         \r
48609         this.elNode = this.wrap.childNodes[0];\r
48610         this.ctNode = this.wrap.childNodes[1];\r
48611         var cs = this.elNode.childNodes;\r
48612         this.indentNode = cs[0];\r
48613         this.ecNode = cs[1];\r
48614         this.iconNode = cs[2];\r
48615         var index = 3;\r
48616         if(cb){\r
48617             this.checkbox = cs[3];\r
48618                         // fix for IE6\r
48619                         this.checkbox.defaultChecked = this.checkbox.checked;                                           \r
48620             index++;\r
48621         }\r
48622         this.anchor = cs[index];\r
48623         this.textNode = cs[index].firstChild;\r
48624     },\r
48625 \r
48626 /**\r
48627  * Returns the &lt;a> element that provides focus for the node's UI.\r
48628  * @return {HtmlElement} The DOM anchor element.\r
48629  */\r
48630     getAnchor : function(){\r
48631         return this.anchor;\r
48632     },\r
48633     \r
48634 /**\r
48635  * Returns the text node.\r
48636  * @return {HtmlNode} The DOM text node.\r
48637  */\r
48638     getTextEl : function(){\r
48639         return this.textNode;\r
48640     },\r
48641     \r
48642 /**\r
48643  * Returns the icon &lt;img> element.\r
48644  * @return {HtmlElement} The DOM image element.\r
48645  */\r
48646     getIconEl : function(){\r
48647         return this.iconNode;\r
48648     },\r
48649 \r
48650 /**\r
48651  * Returns the checked status of the node. If the node was rendered with no\r
48652  * checkbox, it returns false.\r
48653  * @return {Boolean} The checked flag.\r
48654  */\r
48655     isChecked : function(){\r
48656         return this.checkbox ? this.checkbox.checked : false; \r
48657     },\r
48658 \r
48659     // private\r
48660     updateExpandIcon : function(){\r
48661         if(this.rendered){\r
48662             var n = this.node, c1, c2;\r
48663             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";\r
48664             var hasChild = n.hasChildNodes();\r
48665             if(hasChild || n.attributes.expandable){\r
48666                 if(n.expanded){\r
48667                     cls += "-minus";\r
48668                     c1 = "x-tree-node-collapsed";\r
48669                     c2 = "x-tree-node-expanded";\r
48670                 }else{\r
48671                     cls += "-plus";\r
48672                     c1 = "x-tree-node-expanded";\r
48673                     c2 = "x-tree-node-collapsed";\r
48674                 }\r
48675                 if(this.wasLeaf){\r
48676                     this.removeClass("x-tree-node-leaf");\r
48677                     this.wasLeaf = false;\r
48678                 }\r
48679                 if(this.c1 != c1 || this.c2 != c2){\r
48680                     Ext.fly(this.elNode).replaceClass(c1, c2);\r
48681                     this.c1 = c1; this.c2 = c2;\r
48682                 }\r
48683             }else{\r
48684                 if(!this.wasLeaf){\r
48685                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");\r
48686                     delete this.c1;\r
48687                     delete this.c2;\r
48688                     this.wasLeaf = true;\r
48689                 }\r
48690             }\r
48691             var ecc = "x-tree-ec-icon "+cls;\r
48692             if(this.ecc != ecc){\r
48693                 this.ecNode.className = ecc;\r
48694                 this.ecc = ecc;\r
48695             }\r
48696         }\r
48697     },\r
48698     \r
48699     // private\r
48700     onIdChange: function(id){\r
48701         if(this.rendered){\r
48702             this.elNode.setAttribute('ext:tree-node-id', id);\r
48703         }\r
48704     },\r
48705 \r
48706     // private\r
48707     getChildIndent : function(){\r
48708         if(!this.childIndent){\r
48709             var buf = [];\r
48710             var p = this.node;\r
48711             while(p){\r
48712                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
48713                     if(!p.isLast()) {\r
48714                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
48715                     } else {\r
48716                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
48717                     }\r
48718                 }\r
48719                 p = p.parentNode;\r
48720             }\r
48721             this.childIndent = buf.join("");\r
48722         }\r
48723         return this.childIndent;\r
48724     },\r
48725 \r
48726     // private\r
48727     renderIndent : function(){\r
48728         if(this.rendered){\r
48729             var indent = "";\r
48730             var p = this.node.parentNode;\r
48731             if(p){\r
48732                 indent = p.ui.getChildIndent();\r
48733             }\r
48734             if(this.indentMarkup != indent){ // don't rerender if not required\r
48735                 this.indentNode.innerHTML = indent;\r
48736                 this.indentMarkup = indent;\r
48737             }\r
48738             this.updateExpandIcon();\r
48739         }\r
48740     },\r
48741 \r
48742     destroy : function(){\r
48743         if(this.elNode){\r
48744             Ext.dd.Registry.unregister(this.elNode.id);\r
48745         }\r
48746         delete this.elNode;\r
48747         delete this.ctNode;\r
48748         delete this.indentNode;\r
48749         delete this.ecNode;\r
48750         delete this.iconNode;\r
48751         delete this.checkbox;\r
48752         delete this.anchor;\r
48753         delete this.textNode;\r
48754         \r
48755         if (this.holder){\r
48756              delete this.wrap;\r
48757              Ext.removeNode(this.holder);\r
48758              delete this.holder;\r
48759         }else{\r
48760             Ext.removeNode(this.wrap);\r
48761             delete this.wrap;\r
48762         }\r
48763     }\r
48764 };\r
48765 \r
48766 /**\r
48767  * @class Ext.tree.RootTreeNodeUI\r
48768  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
48769  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
48770  * <p>\r
48771  * If you are customizing the Tree's user interface, you\r
48772  * may need to extend this class, but you should never need to instantiate this class.<br>\r
48773  */\r
48774 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
48775     // private\r
48776     render : function(){\r
48777         if(!this.rendered){\r
48778             var targetNode = this.node.ownerTree.innerCt.dom;\r
48779             this.node.expanded = true;\r
48780             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
48781             this.wrap = this.ctNode = targetNode.firstChild;\r
48782         }\r
48783     },\r
48784     collapse : Ext.emptyFn,\r
48785     expand : Ext.emptyFn\r
48786 });/**\r
48787  * @class Ext.tree.TreeLoader\r
48788  * @extends Ext.util.Observable\r
48789  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child\r
48790  * nodes from a specified URL. The response must be a JavaScript Array definition\r
48791  * whose elements are node definition objects. e.g.:\r
48792  * <pre><code>\r
48793     [{\r
48794         id: 1,\r
48795         text: 'A leaf Node',\r
48796         leaf: true\r
48797     },{\r
48798         id: 2,\r
48799         text: 'A folder Node',\r
48800         children: [{\r
48801             id: 3,\r
48802             text: 'A child Node',\r
48803             leaf: true\r
48804         }]\r
48805    }]\r
48806 </code></pre>\r
48807  * <br><br>\r
48808  * A server request is sent, and child nodes are loaded only when a node is expanded.\r
48809  * The loading node's id is passed to the server under the parameter name "node" to\r
48810  * enable the server to produce the correct child nodes.\r
48811  * <br><br>\r
48812  * To pass extra parameters, an event handler may be attached to the "beforeload"\r
48813  * event, and the parameters specified in the TreeLoader's baseParams property:\r
48814  * <pre><code>\r
48815     myTreeLoader.on("beforeload", function(treeLoader, node) {\r
48816         this.baseParams.category = node.attributes.category;\r
48817     }, this);\r
48818 </code></pre>\r
48819  * This would pass an HTTP parameter called "category" to the server containing\r
48820  * the value of the Node's "category" attribute.\r
48821  * @constructor\r
48822  * Creates a new Treeloader.\r
48823  * @param {Object} config A config object containing config properties.\r
48824  */\r
48825 Ext.tree.TreeLoader = function(config){\r
48826     this.baseParams = {};\r
48827     Ext.apply(this, config);\r
48828 \r
48829     this.addEvents(\r
48830         /**\r
48831          * @event beforeload\r
48832          * Fires before a network request is made to retrieve the Json text which specifies a node's children.\r
48833          * @param {Object} This TreeLoader object.\r
48834          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
48835          * @param {Object} callback The callback function specified in the {@link #load} call.\r
48836          */\r
48837         "beforeload",\r
48838         /**\r
48839          * @event load\r
48840          * Fires when the node has been successfuly loaded.\r
48841          * @param {Object} This TreeLoader object.\r
48842          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
48843          * @param {Object} response The response object containing the data from the server.\r
48844          */\r
48845         "load",\r
48846         /**\r
48847          * @event loadexception\r
48848          * Fires if the network request failed.\r
48849          * @param {Object} This TreeLoader object.\r
48850          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
48851          * @param {Object} response The response object containing the data from the server.\r
48852          */\r
48853         "loadexception"\r
48854     );\r
48855     Ext.tree.TreeLoader.superclass.constructor.call(this);\r
48856     if(typeof this.paramOrder == 'string'){\r
48857         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
48858     }\r
48859 };\r
48860 \r
48861 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {\r
48862     /**\r
48863     * @cfg {String} dataUrl The URL from which to request a Json string which\r
48864     * specifies an array of node definition objects representing the child nodes\r
48865     * to be loaded.\r
48866     */\r
48867     /**\r
48868      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
48869      */\r
48870     /**\r
48871      * @cfg {String} url Equivalent to {@link #dataUrl}.\r
48872      */\r
48873     /**\r
48874      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.\r
48875      */\r
48876     /**\r
48877     * @cfg {Object} baseParams (optional) An object containing properties which\r
48878     * specify HTTP parameters to be passed to each request for child nodes.\r
48879     */\r
48880     /**\r
48881     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes\r
48882     * created by this loader. If the attributes sent by the server have an attribute in this object,\r
48883     * they take priority.\r
48884     */\r
48885     /**\r
48886     * @cfg {Object} uiProviders (optional) An object containing properties which\r
48887     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional\r
48888     * <i>uiProvider</i> attribute of a returned child node is a string rather\r
48889     * than a reference to a TreeNodeUI implementation, then that string value\r
48890     * is used as a property name in the uiProviders object.\r
48891     */\r
48892     uiProviders : {},\r
48893 \r
48894     /**\r
48895     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing\r
48896     * child nodes before loading.\r
48897     */\r
48898     clearOnLoad : true,\r
48899 \r
48900     /**\r
48901      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.\r
48902      * A list of params to be executed\r
48903      * server side.  Specify the params in the order in which they must be executed on the server-side\r
48904      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
48905      * comma, or pipe. For example,\r
48906      * any of the following would be acceptable:<pre><code>\r
48907 paramOrder: ['param1','param2','param3']\r
48908 paramOrder: 'param1 param2 param3'\r
48909 paramOrder: 'param1,param2,param3'\r
48910 paramOrder: 'param1|param2|param'\r
48911      </code></pre>\r
48912      */\r
48913     paramOrder: undefined,\r
48914 \r
48915     /**\r
48916      * @cfg {Boolean} paramsAsHash Only used when using directFn.\r
48917      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a\r
48918      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
48919      */\r
48920     paramsAsHash: false,\r
48921 \r
48922     /**\r
48923      * @cfg {Function} directFn\r
48924      * Function to call when executing a request.\r
48925      */\r
48926     directFn : undefined,\r
48927 \r
48928     /**\r
48929      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.\r
48930      * This is called automatically when a node is expanded, but may be used to reload\r
48931      * a node (or append new children if the {@link #clearOnLoad} option is false.)\r
48932      * @param {Ext.tree.TreeNode} node\r
48933      * @param {Function} callback\r
48934      * @param (Object) scope\r
48935      */\r
48936     load : function(node, callback, scope){\r
48937         if(this.clearOnLoad){\r
48938             while(node.firstChild){\r
48939                 node.removeChild(node.firstChild);\r
48940             }\r
48941         }\r
48942         if(this.doPreload(node)){ // preloaded json children\r
48943             this.runCallback(callback, scope || node, [node]);\r
48944         }else if(this.directFn || this.dataUrl || this.url){\r
48945             this.requestData(node, callback, scope || node);\r
48946         }\r
48947     },\r
48948 \r
48949     doPreload : function(node){\r
48950         if(node.attributes.children){\r
48951             if(node.childNodes.length < 1){ // preloaded?\r
48952                 var cs = node.attributes.children;\r
48953                 node.beginUpdate();\r
48954                 for(var i = 0, len = cs.length; i < len; i++){\r
48955                     var cn = node.appendChild(this.createNode(cs[i]));\r
48956                     if(this.preloadChildren){\r
48957                         this.doPreload(cn);\r
48958                     }\r
48959                 }\r
48960                 node.endUpdate();\r
48961             }\r
48962             return true;\r
48963         }\r
48964         return false;\r
48965     },\r
48966 \r
48967     getParams: function(node){\r
48968         var buf = [], bp = this.baseParams;\r
48969         if(this.directFn){\r
48970             buf.push(node.id);\r
48971             if(bp){\r
48972                 if(this.paramOrder){\r
48973                     for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
48974                         buf.push(bp[this.paramOrder[i]]);\r
48975                     }\r
48976                 }else if(this.paramsAsHash){\r
48977                     buf.push(bp);\r
48978                 }\r
48979             }\r
48980             return buf;\r
48981         }else{\r
48982             for(var key in bp){\r
48983                 if(!Ext.isFunction(bp[key])){\r
48984                     buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");\r
48985                 }\r
48986             }\r
48987             buf.push("node=", encodeURIComponent(node.id));\r
48988             return buf.join("");\r
48989         }\r
48990     },\r
48991 \r
48992     requestData : function(node, callback, scope){\r
48993         if(this.fireEvent("beforeload", this, node, callback) !== false){\r
48994             if(this.directFn){\r
48995                 var args = this.getParams(node);\r
48996                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));\r
48997                 this.directFn.apply(window, args);\r
48998             }else{\r
48999                 this.transId = Ext.Ajax.request({\r
49000                     method:this.requestMethod,\r
49001                     url: this.dataUrl||this.url,\r
49002                     success: this.handleResponse,\r
49003                     failure: this.handleFailure,\r
49004                     scope: this,\r
49005                     argument: {callback: callback, node: node, scope: scope},\r
49006                     params: this.getParams(node)\r
49007                 });\r
49008             }\r
49009         }else{\r
49010             // if the load is cancelled, make sure we notify\r
49011             // the node that we are done\r
49012             this.runCallback(callback, scope || node, []);\r
49013         }\r
49014     },\r
49015 \r
49016     processDirectResponse: function(result, response, args){\r
49017         if(response.status){\r
49018             this.handleResponse({\r
49019                 responseData: Ext.isArray(result) ? result : null,\r
49020                 responseText: result,\r
49021                 argument: args\r
49022             });\r
49023         }else{\r
49024             this.handleFailure({\r
49025                 argument: args\r
49026             });\r
49027         }\r
49028     },\r
49029 \r
49030     // private\r
49031     runCallback: function(cb, scope, args){\r
49032         if(Ext.isFunction(cb)){\r
49033             cb.apply(scope, args);\r
49034         }\r
49035     },\r
49036 \r
49037     isLoading : function(){\r
49038         return !!this.transId;\r
49039     },\r
49040 \r
49041     abort : function(){\r
49042         if(this.isLoading()){\r
49043             Ext.Ajax.abort(this.transId);\r
49044         }\r
49045     },\r
49046 \r
49047     /**\r
49048     * <p>Override this function for custom TreeNode node implementation, or to\r
49049     * modify the attributes at creation time.</p>\r
49050     * Example:<pre><code>\r
49051 new Ext.tree.TreePanel({\r
49052     ...\r
49053     loader: new Ext.tree.TreeLoader({\r
49054         url: 'dataUrl',\r
49055         createNode: function(attr) {\r
49056 //          Allow consolidation consignments to have\r
49057 //          consignments dropped into them.\r
49058             if (attr.isConsolidation) {\r
49059                 attr.iconCls = 'x-consol',\r
49060                 attr.allowDrop = true;\r
49061             }\r
49062             return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);\r
49063         }\r
49064     }),\r
49065     ...\r
49066 });\r
49067 </code></pre>\r
49068     * @param attr {Object} The attributes from which to create the new node.\r
49069     */\r
49070     createNode : function(attr){\r
49071         // apply baseAttrs, nice idea Corey!\r
49072         if(this.baseAttrs){\r
49073             Ext.applyIf(attr, this.baseAttrs);\r
49074         }\r
49075         if(this.applyLoader !== false && !attr.loader){\r
49076             attr.loader = this;\r
49077         }\r
49078         if(typeof attr.uiProvider == 'string'){\r
49079            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);\r
49080         }\r
49081         if(attr.nodeType){\r
49082             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);\r
49083         }else{\r
49084             return attr.leaf ?\r
49085                         new Ext.tree.TreeNode(attr) :\r
49086                         new Ext.tree.AsyncTreeNode(attr);\r
49087         }\r
49088     },\r
49089 \r
49090     processResponse : function(response, node, callback, scope){\r
49091         var json = response.responseText;\r
49092         try {\r
49093             var o = response.responseData || Ext.decode(json);\r
49094             node.beginUpdate();\r
49095             for(var i = 0, len = o.length; i < len; i++){\r
49096                 var n = this.createNode(o[i]);\r
49097                 if(n){\r
49098                     node.appendChild(n);\r
49099                 }\r
49100             }\r
49101             node.endUpdate();\r
49102             this.runCallback(callback, scope || node, [node]);\r
49103         }catch(e){\r
49104             this.handleFailure(response);\r
49105         }\r
49106     },\r
49107 \r
49108     handleResponse : function(response){\r
49109         this.transId = false;\r
49110         var a = response.argument;\r
49111         this.processResponse(response, a.node, a.callback, a.scope);\r
49112         this.fireEvent("load", this, a.node, response);\r
49113     },\r
49114 \r
49115     handleFailure : function(response){\r
49116         this.transId = false;\r
49117         var a = response.argument;\r
49118         this.fireEvent("loadexception", this, a.node, response);\r
49119         this.runCallback(a.callback, a.scope || a.node, [a.node]);\r
49120     }\r
49121 });/**
49122  * @class Ext.tree.TreeFilter
49123  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
49124  * @param {TreePanel} tree
49125  * @param {Object} config (optional)
49126  */
49127 Ext.tree.TreeFilter = function(tree, config){
49128     this.tree = tree;
49129     this.filtered = {};
49130     Ext.apply(this, config);
49131 };
49132
49133 Ext.tree.TreeFilter.prototype = {
49134     clearBlank:false,
49135     reverse:false,
49136     autoClear:false,
49137     remove:false,
49138
49139      /**
49140      * Filter the data by a specific attribute.
49141      * @param {String/RegExp} value Either string that the attribute value
49142      * should start with or a RegExp to test against the attribute
49143      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
49144      * @param {TreeNode} startNode (optional) The node to start the filter at.
49145      */
49146     filter : function(value, attr, startNode){
49147         attr = attr || "text";
49148         var f;
49149         if(typeof value == "string"){
49150             var vlen = value.length;
49151             // auto clear empty filter
49152             if(vlen == 0 && this.clearBlank){
49153                 this.clear();
49154                 return;
49155             }
49156             value = value.toLowerCase();
49157             f = function(n){
49158                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
49159             };
49160         }else if(value.exec){ // regex?
49161             f = function(n){
49162                 return value.test(n.attributes[attr]);
49163             };
49164         }else{
49165             throw 'Illegal filter type, must be string or regex';
49166         }
49167         this.filterBy(f, null, startNode);
49168         },
49169
49170     /**
49171      * Filter by a function. The passed function will be called with each
49172      * node in the tree (or from the startNode). If the function returns true, the node is kept
49173      * otherwise it is filtered. If a node is filtered, its children are also filtered.
49174      * @param {Function} fn The filter function
49175      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
49176      */
49177     filterBy : function(fn, scope, startNode){
49178         startNode = startNode || this.tree.root;
49179         if(this.autoClear){
49180             this.clear();
49181         }
49182         var af = this.filtered, rv = this.reverse;
49183         var f = function(n){
49184             if(n == startNode){
49185                 return true;
49186             }
49187             if(af[n.id]){
49188                 return false;
49189             }
49190             var m = fn.call(scope || n, n);
49191             if(!m || rv){
49192                 af[n.id] = n;
49193                 n.ui.hide();
49194                 return false;
49195             }
49196             return true;
49197         };
49198         startNode.cascade(f);
49199         if(this.remove){
49200            for(var id in af){
49201                if(typeof id != "function"){
49202                    var n = af[id];
49203                    if(n && n.parentNode){
49204                        n.parentNode.removeChild(n);
49205                    }
49206                }
49207            }
49208         }
49209     },
49210
49211     /**
49212      * Clears the current filter. Note: with the "remove" option
49213      * set a filter cannot be cleared.
49214      */
49215     clear : function(){
49216         var t = this.tree;
49217         var af = this.filtered;
49218         for(var id in af){
49219             if(typeof id != "function"){
49220                 var n = af[id];
49221                 if(n){
49222                     n.ui.show();
49223                 }
49224             }
49225         }
49226         this.filtered = {};
49227     }
49228 };
49229 /**\r
49230  * @class Ext.tree.TreeSorter\r
49231  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the \r
49232  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).\r
49233  * Example usage:<br />\r
49234  * <pre><code>\r
49235 new Ext.tree.TreeSorter(myTree, {\r
49236     folderSort: true,\r
49237     dir: "desc",\r
49238     sortType: function(node) {\r
49239         // sort by a custom, typed attribute:\r
49240         return parseInt(node.id, 10);\r
49241     }\r
49242 });\r
49243 </code></pre>\r
49244  * @constructor\r
49245  * @param {TreePanel} tree\r
49246  * @param {Object} config\r
49247  */\r
49248 Ext.tree.TreeSorter = function(tree, config){\r
49249     /**\r
49250      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)\r
49251      */\r
49252     /** \r
49253      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this \r
49254      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.\r
49255      */\r
49256     /** \r
49257      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")\r
49258      */\r
49259     /** \r
49260      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")\r
49261      */\r
49262     /** \r
49263      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)\r
49264      */\r
49265     /** \r
49266      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function\r
49267      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return\r
49268      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when\r
49269      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for \r
49270      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.\r
49271      */\r
49272     \r
49273     Ext.apply(this, config);\r
49274     tree.on("beforechildrenrendered", this.doSort, this);\r
49275     tree.on("append", this.updateSort, this);\r
49276     tree.on("insert", this.updateSort, this);\r
49277     tree.on("textchange", this.updateSortParent, this);\r
49278     \r
49279     var dsc = this.dir && this.dir.toLowerCase() == "desc";\r
49280     var p = this.property || "text";\r
49281     var sortType = this.sortType;\r
49282     var fs = this.folderSort;\r
49283     var cs = this.caseSensitive === true;\r
49284     var leafAttr = this.leafAttr || 'leaf';\r
49285 \r
49286     this.sortFn = function(n1, n2){\r
49287         if(fs){\r
49288             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){\r
49289                 return 1;\r
49290             }\r
49291             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){\r
49292                 return -1;\r
49293             }\r
49294         }\r
49295         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());\r
49296         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());\r
49297         if(v1 < v2){\r
49298                         return dsc ? +1 : -1;\r
49299                 }else if(v1 > v2){\r
49300                         return dsc ? -1 : +1;\r
49301         }else{\r
49302                 return 0;\r
49303         }\r
49304     };\r
49305 };\r
49306 \r
49307 Ext.tree.TreeSorter.prototype = {\r
49308     doSort : function(node){\r
49309         node.sort(this.sortFn);\r
49310     },\r
49311     \r
49312     compareNodes : function(n1, n2){\r
49313         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);\r
49314     },\r
49315     \r
49316     updateSort : function(tree, node){\r
49317         if(node.childrenRendered){\r
49318             this.doSort.defer(1, this, [node]);\r
49319         }\r
49320     },\r
49321     \r
49322     updateSortParent : function(node){\r
49323                 var p = node.parentNode;\r
49324                 if(p && p.childrenRendered){\r
49325             this.doSort.defer(1, this, [p]);\r
49326         }\r
49327     }\r
49328 };/**\r
49329  * @class Ext.tree.TreeDropZone\r
49330  * @extends Ext.dd.DropZone\r
49331  * @constructor\r
49332  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping\r
49333  * @param {Object} config\r
49334  */\r
49335 if(Ext.dd.DropZone){\r
49336     \r
49337 Ext.tree.TreeDropZone = function(tree, config){\r
49338     /**\r
49339      * @cfg {Boolean} allowParentInsert\r
49340      * Allow inserting a dragged node between an expanded parent node and its first child that will become a\r
49341      * sibling of the parent when dropped (defaults to false)\r
49342      */\r
49343     this.allowParentInsert = config.allowParentInsert || false;\r
49344     /**\r
49345      * @cfg {String} allowContainerDrop\r
49346      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)\r
49347      */\r
49348     this.allowContainerDrop = config.allowContainerDrop || false;\r
49349     /**\r
49350      * @cfg {String} appendOnly\r
49351      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)\r
49352      */\r
49353     this.appendOnly = config.appendOnly || false;\r
49354 \r
49355     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);\r
49356     /**\r
49357     * The TreePanel for this drop zone\r
49358     * @type Ext.tree.TreePanel\r
49359     * @property\r
49360     */\r
49361     this.tree = tree;\r
49362     /**\r
49363     * Arbitrary data that can be associated with this tree and will be included in the event object that gets\r
49364     * passed to any nodedragover event handler (defaults to {})\r
49365     * @type Ext.tree.TreePanel\r
49366     * @property\r
49367     */\r
49368     this.dragOverData = {};\r
49369     // private\r
49370     this.lastInsertClass = "x-tree-no-status";\r
49371 };\r
49372 \r
49373 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {\r
49374     /**\r
49375      * @cfg {String} ddGroup\r
49376      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
49377      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
49378      */\r
49379     ddGroup : "TreeDD",\r
49380 \r
49381     /**\r
49382      * @cfg {String} expandDelay\r
49383      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node\r
49384      * over the target (defaults to 1000)\r
49385      */\r
49386     expandDelay : 1000,\r
49387 \r
49388     // private\r
49389     expandNode : function(node){\r
49390         if(node.hasChildNodes() && !node.isExpanded()){\r
49391             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));\r
49392         }\r
49393     },\r
49394 \r
49395     // private\r
49396     queueExpand : function(node){\r
49397         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);\r
49398     },\r
49399 \r
49400     // private\r
49401     cancelExpand : function(){\r
49402         if(this.expandProcId){\r
49403             clearTimeout(this.expandProcId);\r
49404             this.expandProcId = false;\r
49405         }\r
49406     },\r
49407 \r
49408     // private\r
49409     isValidDropPoint : function(n, pt, dd, e, data){\r
49410         if(!n || !data){ return false; }\r
49411         var targetNode = n.node;\r
49412         var dropNode = data.node;\r
49413         // default drop rules\r
49414         if(!(targetNode && targetNode.isTarget && pt)){\r
49415             return false;\r
49416         }\r
49417         if(pt == "append" && targetNode.allowChildren === false){\r
49418             return false;\r
49419         }\r
49420         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){\r
49421             return false;\r
49422         }\r
49423         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){\r
49424             return false;\r
49425         }\r
49426         // reuse the object\r
49427         var overEvent = this.dragOverData;\r
49428         overEvent.tree = this.tree;\r
49429         overEvent.target = targetNode;\r
49430         overEvent.data = data;\r
49431         overEvent.point = pt;\r
49432         overEvent.source = dd;\r
49433         overEvent.rawEvent = e;\r
49434         overEvent.dropNode = dropNode;\r
49435         overEvent.cancel = false;  \r
49436         var result = this.tree.fireEvent("nodedragover", overEvent);\r
49437         return overEvent.cancel === false && result !== false;\r
49438     },\r
49439 \r
49440     // private\r
49441     getDropPoint : function(e, n, dd){\r
49442         var tn = n.node;\r
49443         if(tn.isRoot){\r
49444             return tn.allowChildren !== false ? "append" : false; // always append for root\r
49445         }\r
49446         var dragEl = n.ddel;\r
49447         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;\r
49448         var y = Ext.lib.Event.getPageY(e);\r
49449         var noAppend = tn.allowChildren === false || tn.isLeaf();\r
49450         if(this.appendOnly || tn.parentNode.allowChildren === false){\r
49451             return noAppend ? false : "append";\r
49452         }\r
49453         var noBelow = false;\r
49454         if(!this.allowParentInsert){\r
49455             noBelow = tn.hasChildNodes() && tn.isExpanded();\r
49456         }\r
49457         var q = (b - t) / (noAppend ? 2 : 3);\r
49458         if(y >= t && y < (t + q)){\r
49459             return "above";\r
49460         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){\r
49461             return "below";\r
49462         }else{\r
49463             return "append";\r
49464         }\r
49465     },\r
49466 \r
49467     // private\r
49468     onNodeEnter : function(n, dd, e, data){\r
49469         this.cancelExpand();\r
49470     },\r
49471     \r
49472     onContainerOver : function(dd, e, data) {\r
49473         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
49474             return this.dropAllowed;\r
49475         }\r
49476         return this.dropNotAllowed;\r
49477     },\r
49478 \r
49479     // private\r
49480     onNodeOver : function(n, dd, e, data){\r
49481         var pt = this.getDropPoint(e, n, dd);\r
49482         var node = n.node;\r
49483         \r
49484         // auto node expand check\r
49485         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){\r
49486             this.queueExpand(node);\r
49487         }else if(pt != "append"){\r
49488             this.cancelExpand();\r
49489         }\r
49490         \r
49491         // set the insert point style on the target node\r
49492         var returnCls = this.dropNotAllowed;\r
49493         if(this.isValidDropPoint(n, pt, dd, e, data)){\r
49494            if(pt){\r
49495                var el = n.ddel;\r
49496                var cls;\r
49497                if(pt == "above"){\r
49498                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";\r
49499                    cls = "x-tree-drag-insert-above";\r
49500                }else if(pt == "below"){\r
49501                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";\r
49502                    cls = "x-tree-drag-insert-below";\r
49503                }else{\r
49504                    returnCls = "x-tree-drop-ok-append";\r
49505                    cls = "x-tree-drag-append";\r
49506                }\r
49507                if(this.lastInsertClass != cls){\r
49508                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);\r
49509                    this.lastInsertClass = cls;\r
49510                }\r
49511            }\r
49512        }\r
49513        return returnCls;\r
49514     },\r
49515 \r
49516     // private\r
49517     onNodeOut : function(n, dd, e, data){\r
49518         this.cancelExpand();\r
49519         this.removeDropIndicators(n);\r
49520     },\r
49521 \r
49522     // private\r
49523     onNodeDrop : function(n, dd, e, data){\r
49524         var point = this.getDropPoint(e, n, dd);\r
49525         var targetNode = n.node;\r
49526         targetNode.ui.startDrop();\r
49527         if(!this.isValidDropPoint(n, point, dd, e, data)){\r
49528             targetNode.ui.endDrop();\r
49529             return false;\r
49530         }\r
49531         // first try to find the drop node\r
49532         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);\r
49533         return this.processDrop(targetNode, data, point, dd, e, dropNode);\r
49534     },\r
49535     \r
49536     onContainerDrop : function(dd, e, data){\r
49537         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
49538             var targetNode = this.tree.getRootNode();       \r
49539             targetNode.ui.startDrop();\r
49540             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);\r
49541             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);\r
49542         }\r
49543         return false;\r
49544     },\r
49545     \r
49546     // private\r
49547     processDrop: function(target, data, point, dd, e, dropNode){\r
49548         var dropEvent = {\r
49549             tree : this.tree,\r
49550             target: target,\r
49551             data: data,\r
49552             point: point,\r
49553             source: dd,\r
49554             rawEvent: e,\r
49555             dropNode: dropNode,\r
49556             cancel: !dropNode,\r
49557             dropStatus: false\r
49558         };\r
49559         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);\r
49560         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){\r
49561             target.ui.endDrop();\r
49562             return dropEvent.dropStatus;\r
49563         }\r
49564     \r
49565         target = dropEvent.target;\r
49566         if(point == 'append' && !target.isExpanded()){\r
49567             target.expand(false, null, function(){\r
49568                 this.completeDrop(dropEvent);\r
49569             }.createDelegate(this));\r
49570         }else{\r
49571             this.completeDrop(dropEvent);\r
49572         }\r
49573         return true;\r
49574     },\r
49575 \r
49576     // private\r
49577     completeDrop : function(de){\r
49578         var ns = de.dropNode, p = de.point, t = de.target;\r
49579         if(!Ext.isArray(ns)){\r
49580             ns = [ns];\r
49581         }\r
49582         var n;\r
49583         for(var i = 0, len = ns.length; i < len; i++){\r
49584             n = ns[i];\r
49585             if(p == "above"){\r
49586                 t.parentNode.insertBefore(n, t);\r
49587             }else if(p == "below"){\r
49588                 t.parentNode.insertBefore(n, t.nextSibling);\r
49589             }else{\r
49590                 t.appendChild(n);\r
49591             }\r
49592         }\r
49593         n.ui.focus();\r
49594         if(Ext.enableFx && this.tree.hlDrop){\r
49595             n.ui.highlight();\r
49596         }\r
49597         t.ui.endDrop();\r
49598         this.tree.fireEvent("nodedrop", de);\r
49599     },\r
49600 \r
49601     // private\r
49602     afterNodeMoved : function(dd, data, e, targetNode, dropNode){\r
49603         if(Ext.enableFx && this.tree.hlDrop){\r
49604             dropNode.ui.focus();\r
49605             dropNode.ui.highlight();\r
49606         }\r
49607         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);\r
49608     },\r
49609 \r
49610     // private\r
49611     getTree : function(){\r
49612         return this.tree;\r
49613     },\r
49614 \r
49615     // private\r
49616     removeDropIndicators : function(n){\r
49617         if(n && n.ddel){\r
49618             var el = n.ddel;\r
49619             Ext.fly(el).removeClass([\r
49620                     "x-tree-drag-insert-above",\r
49621                     "x-tree-drag-insert-below",\r
49622                     "x-tree-drag-append"]);\r
49623             this.lastInsertClass = "_noclass";\r
49624         }\r
49625     },\r
49626 \r
49627     // private\r
49628     beforeDragDrop : function(target, e, id){\r
49629         this.cancelExpand();\r
49630         return true;\r
49631     },\r
49632 \r
49633     // private\r
49634     afterRepair : function(data){\r
49635         if(data && Ext.enableFx){\r
49636             data.node.ui.highlight();\r
49637         }\r
49638         this.hideProxy();\r
49639     }    \r
49640 });\r
49641 \r
49642 }/**\r
49643  * @class Ext.tree.TreeDragZone\r
49644  * @extends Ext.dd.DragZone\r
49645  * @constructor\r
49646  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging\r
49647  * @param {Object} config\r
49648  */\r
49649 if(Ext.dd.DragZone){\r
49650 Ext.tree.TreeDragZone = function(tree, config){\r
49651     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);\r
49652     /**\r
49653     * The TreePanel for this drag zone\r
49654     * @type Ext.tree.TreePanel\r
49655     * @property\r
49656     */\r
49657     this.tree = tree;\r
49658 };\r
49659 \r
49660 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {\r
49661     /**\r
49662      * @cfg {String} ddGroup\r
49663      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
49664      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
49665      */\r
49666     ddGroup : "TreeDD",\r
49667 \r
49668     // private\r
49669     onBeforeDrag : function(data, e){\r
49670         var n = data.node;\r
49671         return n && n.draggable && !n.disabled;\r
49672     },\r
49673 \r
49674     // private\r
49675     onInitDrag : function(e){\r
49676         var data = this.dragData;\r
49677         this.tree.getSelectionModel().select(data.node);\r
49678         this.tree.eventModel.disable();\r
49679         this.proxy.update("");\r
49680         data.node.ui.appendDDGhost(this.proxy.ghost.dom);\r
49681         this.tree.fireEvent("startdrag", this.tree, data.node, e);\r
49682     },\r
49683 \r
49684     // private\r
49685     getRepairXY : function(e, data){\r
49686         return data.node.ui.getDDRepairXY();\r
49687     },\r
49688 \r
49689     // private\r
49690     onEndDrag : function(data, e){\r
49691         this.tree.eventModel.enable.defer(100, this.tree.eventModel);\r
49692         this.tree.fireEvent("enddrag", this.tree, data.node, e);\r
49693     },\r
49694 \r
49695     // private\r
49696     onValidDrop : function(dd, e, id){\r
49697         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);\r
49698         this.hideProxy();\r
49699     },\r
49700 \r
49701     // private\r
49702     beforeInvalidDrop : function(e, id){\r
49703         // this scrolls the original position back into view\r
49704         var sm = this.tree.getSelectionModel();\r
49705         sm.clearSelections();\r
49706         sm.select(this.dragData.node);\r
49707     },\r
49708     \r
49709     // private\r
49710     afterRepair : function(){\r
49711         if (Ext.enableFx && this.tree.hlDrop) {\r
49712             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
49713         }\r
49714         this.dragging = false;\r
49715     }\r
49716 });\r
49717 }/**
49718  * @class Ext.tree.TreeEditor
49719  * @extends Ext.Editor
49720  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
49721  * as the editor field.
49722  * @constructor
49723  * @param {TreePanel} tree
49724  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
49725  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
49726  * @param {Object} config (optional) A TreeEditor config object
49727  */
49728 Ext.tree.TreeEditor = function(tree, fc, config){
49729     fc = fc || {};
49730     var field = fc.events ? fc : new Ext.form.TextField(fc);
49731     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
49732
49733     this.tree = tree;
49734
49735     if(!tree.rendered){
49736         tree.on('render', this.initEditor, this);
49737     }else{
49738         this.initEditor(tree);
49739     }
49740 };
49741
49742 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
49743     /**
49744      * @cfg {String} alignment
49745      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
49746      */
49747     alignment: "l-l",
49748     // inherit
49749     autoSize: false,
49750     /**
49751      * @cfg {Boolean} hideEl
49752      * True to hide the bound element while the editor is displayed (defaults to false)
49753      */
49754     hideEl : false,
49755     /**
49756      * @cfg {String} cls
49757      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
49758      */
49759     cls: "x-small-editor x-tree-editor",
49760     /**
49761      * @cfg {Boolean} shim
49762      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
49763      */
49764     shim:false,
49765     // inherit
49766     shadow:"frame",
49767     /**
49768      * @cfg {Number} maxWidth
49769      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
49770      * the containing tree element's size, it will be automatically limited for you to the container width, taking
49771      * scroll and client offsets into account prior to each edit.
49772      */
49773     maxWidth: 250,
49774     /**
49775      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
49776      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
49777      * the editor for the node will display, otherwise it will be processed as a regular click.
49778      */
49779     editDelay : 350,
49780
49781     initEditor : function(tree){
49782         tree.on('beforeclick', this.beforeNodeClick, this);
49783         tree.on('dblclick', this.onNodeDblClick, this);
49784         this.on('complete', this.updateNode, this);
49785         this.on('beforestartedit', this.fitToTree, this);
49786         this.on('startedit', this.bindScroll, this, {delay:10});
49787         this.on('specialkey', this.onSpecialKey, this);
49788     },
49789
49790     // private
49791     fitToTree : function(ed, el){
49792         var td = this.tree.getTreeEl().dom, nd = el.dom;
49793         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
49794             td.scrollLeft = nd.offsetLeft;
49795         }
49796         var w = Math.min(
49797                 this.maxWidth,
49798                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
49799         this.setSize(w, '');
49800     },
49801
49802     /**
49803      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
49804      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
49805      */
49806     triggerEdit : function(node, defer){
49807         this.completeEdit();
49808                 if(node.attributes.editable !== false){
49809            /**
49810             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
49811             * @type Ext.tree.TreeNode
49812             * @property editNode
49813             */
49814                         this.editNode = node;
49815             if(this.tree.autoScroll){
49816                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
49817             }
49818             var value = node.text || '';
49819             if (!Ext.isGecko && Ext.isEmpty(node.text)){
49820                 node.setText('&#160;');
49821             }
49822             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
49823             return false;
49824         }
49825     },
49826
49827     // private
49828     bindScroll : function(){
49829         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
49830     },
49831
49832     // private
49833     beforeNodeClick : function(node, e){
49834         clearTimeout(this.autoEditTimer);
49835         if(this.tree.getSelectionModel().isSelected(node)){
49836             e.stopEvent();
49837             return this.triggerEdit(node);
49838         }
49839     },
49840
49841     onNodeDblClick : function(node, e){
49842         clearTimeout(this.autoEditTimer);
49843     },
49844
49845     // private
49846     updateNode : function(ed, value){
49847         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
49848         this.editNode.setText(value);
49849     },
49850
49851     // private
49852     onHide : function(){
49853         Ext.tree.TreeEditor.superclass.onHide.call(this);
49854         if(this.editNode){
49855             this.editNode.ui.focus.defer(50, this.editNode.ui);
49856         }
49857     },
49858
49859     // private
49860     onSpecialKey : function(field, e){
49861         var k = e.getKey();
49862         if(k == e.ESC){
49863             e.stopEvent();
49864             this.cancelEdit();
49865         }else if(k == e.ENTER && !e.hasModifier()){
49866             e.stopEvent();
49867             this.completeEdit();
49868         }
49869     }
49870 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> \r
49871     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> \r
49872 */\r
49873 \r
49874 var swfobject = function() {\r
49875     \r
49876     var UNDEF = "undefined",\r
49877         OBJECT = "object",\r
49878         SHOCKWAVE_FLASH = "Shockwave Flash",\r
49879         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",\r
49880         FLASH_MIME_TYPE = "application/x-shockwave-flash",\r
49881         EXPRESS_INSTALL_ID = "SWFObjectExprInst",\r
49882         ON_READY_STATE_CHANGE = "onreadystatechange",\r
49883         \r
49884         win = window,\r
49885         doc = document,\r
49886         nav = navigator,\r
49887         \r
49888         plugin = false,\r
49889         domLoadFnArr = [main],\r
49890         regObjArr = [],\r
49891         objIdArr = [],\r
49892         listenersArr = [],\r
49893         storedAltContent,\r
49894         storedAltContentId,\r
49895         storedCallbackFn,\r
49896         storedCallbackObj,\r
49897         isDomLoaded = false,\r
49898         isExpressInstallActive = false,\r
49899         dynamicStylesheet,\r
49900         dynamicStylesheetMedia,\r
49901         autoHideShow = true,\r
49902     \r
49903     /* Centralized function for browser feature detection\r
49904         - User agent string detection is only used when no good alternative is possible\r
49905         - Is executed directly for optimal performance\r
49906     */  \r
49907     ua = function() {\r
49908         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,\r
49909             u = nav.userAgent.toLowerCase(),\r
49910             p = nav.platform.toLowerCase(),\r
49911             windows = p ? /win/.test(p) : /win/.test(u),\r
49912             mac = p ? /mac/.test(p) : /mac/.test(u),\r
49913             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit\r
49914             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
49915             playerVersion = [0,0,0],\r
49916             d = null;\r
49917         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {\r
49918             d = nav.plugins[SHOCKWAVE_FLASH].description;\r
49919             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
49920                 plugin = true;\r
49921                 ie = false; // cascaded feature detection for Internet Explorer\r
49922                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");\r
49923                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);\r
49924                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);\r
49925                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;\r
49926             }\r
49927         }\r
49928         else if (typeof win.ActiveXObject != UNDEF) {\r
49929             try {\r
49930                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);\r
49931                 if (a) { // a will return null when ActiveX is disabled\r
49932                     d = a.GetVariable("$version");\r
49933                     if (d) {\r
49934                         ie = true; // cascaded feature detection for Internet Explorer\r
49935                         d = d.split(" ")[1].split(",");\r
49936                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
49937                     }\r
49938                 }\r
49939             }\r
49940             catch(e) {}\r
49941         }\r
49942         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };\r
49943     }(),\r
49944     \r
49945     /* Cross-browser onDomLoad\r
49946         - Will fire an event as soon as the DOM of a web page is loaded\r
49947         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/\r
49948         - Regular onload serves as fallback\r
49949     */ \r
49950     onDomLoad = function() {\r
49951         if (!ua.w3) { return; }\r
49952         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
49953             callDomLoadFunctions();\r
49954         }\r
49955         if (!isDomLoaded) {\r
49956             if (typeof doc.addEventListener != UNDEF) {\r
49957                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);\r
49958             }       \r
49959             if (ua.ie && ua.win) {\r
49960                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {\r
49961                     if (doc.readyState == "complete") {\r
49962                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);\r
49963                         callDomLoadFunctions();\r
49964                     }\r
49965                 });\r
49966                 if (win == top) { // if not inside an iframe\r
49967                     (function(){\r
49968                         if (isDomLoaded) { return; }\r
49969                         try {\r
49970                             doc.documentElement.doScroll("left");\r
49971                         }\r
49972                         catch(e) {\r
49973                             setTimeout(arguments.callee, 0);\r
49974                             return;\r
49975                         }\r
49976                         callDomLoadFunctions();\r
49977                     })();\r
49978                 }\r
49979             }\r
49980             if (ua.wk) {\r
49981                 (function(){\r
49982                     if (isDomLoaded) { return; }\r
49983                     if (!/loaded|complete/.test(doc.readyState)) {\r
49984                         setTimeout(arguments.callee, 0);\r
49985                         return;\r
49986                     }\r
49987                     callDomLoadFunctions();\r
49988                 })();\r
49989             }\r
49990             addLoadEvent(callDomLoadFunctions);\r
49991         }\r
49992     }();\r
49993     \r
49994     function callDomLoadFunctions() {\r
49995         if (isDomLoaded) { return; }\r
49996         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early\r
49997             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));\r
49998             t.parentNode.removeChild(t);\r
49999         }\r
50000         catch (e) { return; }\r
50001         isDomLoaded = true;\r
50002         var dl = domLoadFnArr.length;\r
50003         for (var i = 0; i < dl; i++) {\r
50004             domLoadFnArr[i]();\r
50005         }\r
50006     }\r
50007     \r
50008     function addDomLoadEvent(fn) {\r
50009         if (isDomLoaded) {\r
50010             fn();\r
50011         }\r
50012         else { \r
50013             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+\r
50014         }\r
50015     }\r
50016     \r
50017     /* Cross-browser onload\r
50018         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/\r
50019         - Will fire an event as soon as a web page including all of its assets are loaded \r
50020      */\r
50021     function addLoadEvent(fn) {\r
50022         if (typeof win.addEventListener != UNDEF) {\r
50023             win.addEventListener("load", fn, false);\r
50024         }\r
50025         else if (typeof doc.addEventListener != UNDEF) {\r
50026             doc.addEventListener("load", fn, false);\r
50027         }\r
50028         else if (typeof win.attachEvent != UNDEF) {\r
50029             addListener(win, "onload", fn);\r
50030         }\r
50031         else if (typeof win.onload == "function") {\r
50032             var fnOld = win.onload;\r
50033             win.onload = function() {\r
50034                 fnOld();\r
50035                 fn();\r
50036             };\r
50037         }\r
50038         else {\r
50039             win.onload = fn;\r
50040         }\r
50041     }\r
50042     \r
50043     /* Main function\r
50044         - Will preferably execute onDomLoad, otherwise onload (as a fallback)\r
50045     */\r
50046     function main() { \r
50047         if (plugin) {\r
50048             testPlayerVersion();\r
50049         }\r
50050         else {\r
50051             matchVersions();\r
50052         }\r
50053     }\r
50054     \r
50055     /* Detect the Flash Player version for non-Internet Explorer browsers\r
50056         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:\r
50057           a. Both release and build numbers can be detected\r
50058           b. Avoid wrong descriptions by corrupt installers provided by Adobe\r
50059           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports\r
50060         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available\r
50061     */\r
50062     function testPlayerVersion() {\r
50063         var b = doc.getElementsByTagName("body")[0];\r
50064         var o = createElement(OBJECT);\r
50065         o.setAttribute("type", FLASH_MIME_TYPE);\r
50066         var t = b.appendChild(o);\r
50067         if (t) {\r
50068             var counter = 0;\r
50069             (function(){\r
50070                 if (typeof t.GetVariable != UNDEF) {\r
50071                     var d = t.GetVariable("$version");\r
50072                     if (d) {\r
50073                         d = d.split(" ")[1].split(",");\r
50074                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
50075                     }\r
50076                 }\r
50077                 else if (counter < 10) {\r
50078                     counter++;\r
50079                     setTimeout(arguments.callee, 10);\r
50080                     return;\r
50081                 }\r
50082                 b.removeChild(o);\r
50083                 t = null;\r
50084                 matchVersions();\r
50085             })();\r
50086         }\r
50087         else {\r
50088             matchVersions();\r
50089         }\r
50090     }\r
50091     \r
50092     /* Perform Flash Player and SWF version matching; static publishing only\r
50093     */\r
50094     function matchVersions() {\r
50095         var rl = regObjArr.length;\r
50096         if (rl > 0) {\r
50097             for (var i = 0; i < rl; i++) { // for each registered object element\r
50098                 var id = regObjArr[i].id;\r
50099                 var cb = regObjArr[i].callbackFn;\r
50100                 var cbObj = {success:false, id:id};\r
50101                 if (ua.pv[0] > 0) {\r
50102                     var obj = getElementById(id);\r
50103                     if (obj) {\r
50104                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!\r
50105                             setVisibility(id, true);\r
50106                             if (cb) {\r
50107                                 cbObj.success = true;\r
50108                                 cbObj.ref = getObjectById(id);\r
50109                                 cb(cbObj);\r
50110                             }\r
50111                         }\r
50112                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported\r
50113                             var att = {};\r
50114                             att.data = regObjArr[i].expressInstall;\r
50115                             att.width = obj.getAttribute("width") || "0";\r
50116                             att.height = obj.getAttribute("height") || "0";\r
50117                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }\r
50118                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }\r
50119                             // parse HTML object param element's name-value pairs\r
50120                             var par = {};\r
50121                             var p = obj.getElementsByTagName("param");\r
50122                             var pl = p.length;\r
50123                             for (var j = 0; j < pl; j++) {\r
50124                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {\r
50125                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");\r
50126                                 }\r
50127                             }\r
50128                             showExpressInstall(att, par, id, cb);\r
50129                         }\r
50130                         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
50131                             displayAltContent(obj);\r
50132                             if (cb) { cb(cbObj); }\r
50133                         }\r
50134                     }\r
50135                 }\r
50136                 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
50137                     setVisibility(id, true);\r
50138                     if (cb) {\r
50139                         var o = getObjectById(id); // test whether there is an HTML object element or not\r
50140                         if (o && typeof o.SetVariable != UNDEF) { \r
50141                             cbObj.success = true;\r
50142                             cbObj.ref = o;\r
50143                         }\r
50144                         cb(cbObj);\r
50145                     }\r
50146                 }\r
50147             }\r
50148         }\r
50149     }\r
50150     \r
50151     function getObjectById(objectIdStr) {\r
50152         var r = null;\r
50153         var o = getElementById(objectIdStr);\r
50154         if (o && o.nodeName == "OBJECT") {\r
50155             if (typeof o.SetVariable != UNDEF) {\r
50156                 r = o;\r
50157             }\r
50158             else {\r
50159                 var n = o.getElementsByTagName(OBJECT)[0];\r
50160                 if (n) {\r
50161                     r = n;\r
50162                 }\r
50163             }\r
50164         }\r
50165         return r;\r
50166     }\r
50167     \r
50168     /* Requirements for Adobe Express Install\r
50169         - only one instance can be active at a time\r
50170         - fp 6.0.65 or higher\r
50171         - Win/Mac OS only\r
50172         - no Webkit engines older than version 312\r
50173     */\r
50174     function canExpressInstall() {\r
50175         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);\r
50176     }\r
50177     \r
50178     /* Show the Adobe Express Install dialog\r
50179         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75\r
50180     */\r
50181     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {\r
50182         isExpressInstallActive = true;\r
50183         storedCallbackFn = callbackFn || null;\r
50184         storedCallbackObj = {success:false, id:replaceElemIdStr};\r
50185         var obj = getElementById(replaceElemIdStr);\r
50186         if (obj) {\r
50187             if (obj.nodeName == "OBJECT") { // static publishing\r
50188                 storedAltContent = abstractAltContent(obj);\r
50189                 storedAltContentId = null;\r
50190             }\r
50191             else { // dynamic publishing\r
50192                 storedAltContent = obj;\r
50193                 storedAltContentId = replaceElemIdStr;\r
50194             }\r
50195             att.id = EXPRESS_INSTALL_ID;\r
50196             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }\r
50197             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }\r
50198             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";\r
50199             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",\r
50200                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;\r
50201             if (typeof par.flashvars != UNDEF) {\r
50202                 par.flashvars += "&" + fv;\r
50203             }\r
50204             else {\r
50205                 par.flashvars = fv;\r
50206             }\r
50207             // 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
50208             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
50209             if (ua.ie && ua.win && obj.readyState != 4) {\r
50210                 var newObj = createElement("div");\r
50211                 replaceElemIdStr += "SWFObjectNew";\r
50212                 newObj.setAttribute("id", replaceElemIdStr);\r
50213                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf\r
50214                 obj.style.display = "none";\r
50215                 (function(){\r
50216                     if (obj.readyState == 4) {\r
50217                         obj.parentNode.removeChild(obj);\r
50218                     }\r
50219                     else {\r
50220                         setTimeout(arguments.callee, 10);\r
50221                     }\r
50222                 })();\r
50223             }\r
50224             createSWF(att, par, replaceElemIdStr);\r
50225         }\r
50226     }\r
50227     \r
50228     /* Functions to abstract and display alternative content\r
50229     */\r
50230     function displayAltContent(obj) {\r
50231         if (ua.ie && ua.win && obj.readyState != 4) {\r
50232             // 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
50233             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
50234             var el = createElement("div");\r
50235             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content\r
50236             el.parentNode.replaceChild(abstractAltContent(obj), el);\r
50237             obj.style.display = "none";\r
50238             (function(){\r
50239                 if (obj.readyState == 4) {\r
50240                     obj.parentNode.removeChild(obj);\r
50241                 }\r
50242                 else {\r
50243                     setTimeout(arguments.callee, 10);\r
50244                 }\r
50245             })();\r
50246         }\r
50247         else {\r
50248             obj.parentNode.replaceChild(abstractAltContent(obj), obj);\r
50249         }\r
50250     } \r
50251 \r
50252     function abstractAltContent(obj) {\r
50253         var ac = createElement("div");\r
50254         if (ua.win && ua.ie) {\r
50255             ac.innerHTML = obj.innerHTML;\r
50256         }\r
50257         else {\r
50258             var nestedObj = obj.getElementsByTagName(OBJECT)[0];\r
50259             if (nestedObj) {\r
50260                 var c = nestedObj.childNodes;\r
50261                 if (c) {\r
50262                     var cl = c.length;\r
50263                     for (var i = 0; i < cl; i++) {\r
50264                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {\r
50265                             ac.appendChild(c[i].cloneNode(true));\r
50266                         }\r
50267                     }\r
50268                 }\r
50269             }\r
50270         }\r
50271         return ac;\r
50272     }\r
50273     \r
50274     /* Cross-browser dynamic SWF creation\r
50275     */\r
50276     function createSWF(attObj, parObj, id) {\r
50277         var r, el = getElementById(id);\r
50278         if (ua.wk && ua.wk < 312) { return r; }\r
50279         if (el) {\r
50280             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content\r
50281                 attObj.id = id;\r
50282             }\r
50283             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML\r
50284                 var att = "";\r
50285                 for (var i in attObj) {\r
50286                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries\r
50287                         if (i.toLowerCase() == "data") {\r
50288                             parObj.movie = attObj[i];\r
50289                         }\r
50290                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
50291                             att += ' class="' + attObj[i] + '"';\r
50292                         }\r
50293                         else if (i.toLowerCase() != "classid") {\r
50294                             att += ' ' + i + '="' + attObj[i] + '"';\r
50295                         }\r
50296                     }\r
50297                 }\r
50298                 var par = "";\r
50299                 for (var j in parObj) {\r
50300                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries\r
50301                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';\r
50302                     }\r
50303                 }\r
50304                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';\r
50305                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)\r
50306                 r = getElementById(attObj.id);  \r
50307             }\r
50308             else { // well-behaving browsers\r
50309                 var o = createElement(OBJECT);\r
50310                 o.setAttribute("type", FLASH_MIME_TYPE);\r
50311                 for (var m in attObj) {\r
50312                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries\r
50313                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
50314                             o.setAttribute("class", attObj[m]);\r
50315                         }\r
50316                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute\r
50317                             o.setAttribute(m, attObj[m]);\r
50318                         }\r
50319                     }\r
50320                 }\r
50321                 for (var n in parObj) {\r
50322                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element\r
50323                         createObjParam(o, n, parObj[n]);\r
50324                     }\r
50325                 }\r
50326                 el.parentNode.replaceChild(o, el);\r
50327                 r = o;\r
50328             }\r
50329         }\r
50330         return r;\r
50331     }\r
50332     \r
50333     function createObjParam(el, pName, pValue) {\r
50334         var p = createElement("param");\r
50335         p.setAttribute("name", pName);  \r
50336         p.setAttribute("value", pValue);\r
50337         el.appendChild(p);\r
50338     }\r
50339     \r
50340     /* Cross-browser SWF removal\r
50341         - Especially needed to safely and completely remove a SWF in Internet Explorer\r
50342     */\r
50343     function removeSWF(id) {\r
50344         var obj = getElementById(id);\r
50345         if (obj && obj.nodeName == "OBJECT") {\r
50346             if (ua.ie && ua.win) {\r
50347                 obj.style.display = "none";\r
50348                 (function(){\r
50349                     if (obj.readyState == 4) {\r
50350                         removeObjectInIE(id);\r
50351                     }\r
50352                     else {\r
50353                         setTimeout(arguments.callee, 10);\r
50354                     }\r
50355                 })();\r
50356             }\r
50357             else {\r
50358                 obj.parentNode.removeChild(obj);\r
50359             }\r
50360         }\r
50361     }\r
50362     \r
50363     function removeObjectInIE(id) {\r
50364         var obj = getElementById(id);\r
50365         if (obj) {\r
50366             for (var i in obj) {\r
50367                 if (typeof obj[i] == "function") {\r
50368                     obj[i] = null;\r
50369                 }\r
50370             }\r
50371             obj.parentNode.removeChild(obj);\r
50372         }\r
50373     }\r
50374     \r
50375     /* Functions to optimize JavaScript compression\r
50376     */\r
50377     function getElementById(id) {\r
50378         var el = null;\r
50379         try {\r
50380             el = doc.getElementById(id);\r
50381         }\r
50382         catch (e) {}\r
50383         return el;\r
50384     }\r
50385     \r
50386     function createElement(el) {\r
50387         return doc.createElement(el);\r
50388     }\r
50389     \r
50390     /* Updated attachEvent function for Internet Explorer\r
50391         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks\r
50392     */  \r
50393     function addListener(target, eventType, fn) {\r
50394         target.attachEvent(eventType, fn);\r
50395         listenersArr[listenersArr.length] = [target, eventType, fn];\r
50396     }\r
50397     \r
50398     /* Flash Player and SWF content version matching\r
50399     */\r
50400     function hasPlayerVersion(rv) {\r
50401         var pv = ua.pv, v = rv.split(".");\r
50402         v[0] = parseInt(v[0], 10);\r
50403         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"\r
50404         v[2] = parseInt(v[2], 10) || 0;\r
50405         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
50406     }\r
50407     \r
50408     /* Cross-browser dynamic CSS creation\r
50409         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php\r
50410     */  \r
50411     function createCSS(sel, decl, media, newStyle) {\r
50412         if (ua.ie && ua.mac) { return; }\r
50413         var h = doc.getElementsByTagName("head")[0];\r
50414         if (!h) { return; } // to also support badly authored HTML pages that lack a head element\r
50415         var m = (media && typeof media == "string") ? media : "screen";\r
50416         if (newStyle) {\r
50417             dynamicStylesheet = null;\r
50418             dynamicStylesheetMedia = null;\r
50419         }\r
50420         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { \r
50421             // create dynamic stylesheet + get a global reference to it\r
50422             var s = createElement("style");\r
50423             s.setAttribute("type", "text/css");\r
50424             s.setAttribute("media", m);\r
50425             dynamicStylesheet = h.appendChild(s);\r
50426             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {\r
50427                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];\r
50428             }\r
50429             dynamicStylesheetMedia = m;\r
50430         }\r
50431         // add style rule\r
50432         if (ua.ie && ua.win) {\r
50433             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {\r
50434                 dynamicStylesheet.addRule(sel, decl);\r
50435             }\r
50436         }\r
50437         else {\r
50438             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {\r
50439                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));\r
50440             }\r
50441         }\r
50442     }\r
50443     \r
50444     function setVisibility(id, isVisible) {\r
50445         if (!autoHideShow) { return; }\r
50446         var v = isVisible ? "visible" : "hidden";\r
50447         if (isDomLoaded && getElementById(id)) {\r
50448             getElementById(id).style.visibility = v;\r
50449         }\r
50450         else {\r
50451             createCSS("#" + id, "visibility:" + v);\r
50452         }\r
50453     }\r
50454 \r
50455     /* Filter to avoid XSS attacks\r
50456     */\r
50457     function urlEncodeIfNecessary(s) {\r
50458         var regex = /[\\\"<>\.;]/;\r
50459         var hasBadChars = regex.exec(s) != null;\r
50460         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;\r
50461     }\r
50462     \r
50463     /* 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
50464     */\r
50465     var cleanup = function() {\r
50466         if (ua.ie && ua.win) {\r
50467             window.attachEvent("onunload", function() {\r
50468                 // remove listeners to avoid memory leaks\r
50469                 var ll = listenersArr.length;\r
50470                 for (var i = 0; i < ll; i++) {\r
50471                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);\r
50472                 }\r
50473                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect\r
50474                 var il = objIdArr.length;\r
50475                 for (var j = 0; j < il; j++) {\r
50476                     removeSWF(objIdArr[j]);\r
50477                 }\r
50478                 // cleanup library's main closures to avoid memory leaks\r
50479                 for (var k in ua) {\r
50480                     ua[k] = null;\r
50481                 }\r
50482                 ua = null;\r
50483                 for (var l in swfobject) {\r
50484                     swfobject[l] = null;\r
50485                 }\r
50486                 swfobject = null;\r
50487             });\r
50488         }\r
50489     }();\r
50490     \r
50491     return {\r
50492         /* Public API\r
50493             - Reference: http://code.google.com/p/swfobject/wiki/documentation\r
50494         */ \r
50495         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {\r
50496             if (ua.w3 && objectIdStr && swfVersionStr) {\r
50497                 var regObj = {};\r
50498                 regObj.id = objectIdStr;\r
50499                 regObj.swfVersion = swfVersionStr;\r
50500                 regObj.expressInstall = xiSwfUrlStr;\r
50501                 regObj.callbackFn = callbackFn;\r
50502                 regObjArr[regObjArr.length] = regObj;\r
50503                 setVisibility(objectIdStr, false);\r
50504             }\r
50505             else if (callbackFn) {\r
50506                 callbackFn({success:false, id:objectIdStr});\r
50507             }\r
50508         },\r
50509         \r
50510         getObjectById: function(objectIdStr) {\r
50511             if (ua.w3) {\r
50512                 return getObjectById(objectIdStr);\r
50513             }\r
50514         },\r
50515         \r
50516         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {\r
50517             var callbackObj = {success:false, id:replaceElemIdStr};\r
50518             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {\r
50519                 setVisibility(replaceElemIdStr, false);\r
50520                 addDomLoadEvent(function() {\r
50521                     widthStr += ""; // auto-convert to string\r
50522                     heightStr += "";\r
50523                     var att = {};\r
50524                     if (attObj && typeof attObj === OBJECT) {\r
50525                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs\r
50526                             att[i] = attObj[i];\r
50527                         }\r
50528                     }\r
50529                     att.data = swfUrlStr;\r
50530                     att.width = widthStr;\r
50531                     att.height = heightStr;\r
50532                     var par = {}; \r
50533                     if (parObj && typeof parObj === OBJECT) {\r
50534                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs\r
50535                             par[j] = parObj[j];\r
50536                         }\r
50537                     }\r
50538                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {\r
50539                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs\r
50540                             if (typeof par.flashvars != UNDEF) {\r
50541                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];\r
50542                             }\r
50543                             else {\r
50544                                 par.flashvars = k + "=" + flashvarsObj[k];\r
50545                             }\r
50546                         }\r
50547                     }\r
50548                     if (hasPlayerVersion(swfVersionStr)) { // create SWF\r
50549                         var obj = createSWF(att, par, replaceElemIdStr);\r
50550                         if (att.id == replaceElemIdStr) {\r
50551                             setVisibility(replaceElemIdStr, true);\r
50552                         }\r
50553                         callbackObj.success = true;\r
50554                         callbackObj.ref = obj;\r
50555                     }\r
50556                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install\r
50557                         att.data = xiSwfUrlStr;\r
50558                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
50559                         return;\r
50560                     }\r
50561                     else { // show alternative content\r
50562                         setVisibility(replaceElemIdStr, true);\r
50563                     }\r
50564                     if (callbackFn) { callbackFn(callbackObj); }\r
50565                 });\r
50566             }\r
50567             else if (callbackFn) { callbackFn(callbackObj); }\r
50568         },\r
50569         \r
50570         switchOffAutoHideShow: function() {\r
50571             autoHideShow = false;\r
50572         },\r
50573         \r
50574         ua: ua,\r
50575         \r
50576         getFlashPlayerVersion: function() {\r
50577             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };\r
50578         },\r
50579         \r
50580         hasFlashPlayerVersion: hasPlayerVersion,\r
50581         \r
50582         createSWF: function(attObj, parObj, replaceElemIdStr) {\r
50583             if (ua.w3) {\r
50584                 return createSWF(attObj, parObj, replaceElemIdStr);\r
50585             }\r
50586             else {\r
50587                 return undefined;\r
50588             }\r
50589         },\r
50590         \r
50591         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {\r
50592             if (ua.w3 && canExpressInstall()) {\r
50593                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
50594             }\r
50595         },\r
50596         \r
50597         removeSWF: function(objElemIdStr) {\r
50598             if (ua.w3) {\r
50599                 removeSWF(objElemIdStr);\r
50600             }\r
50601         },\r
50602         \r
50603         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {\r
50604             if (ua.w3) {\r
50605                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);\r
50606             }\r
50607         },\r
50608         \r
50609         addDomLoadEvent: addDomLoadEvent,\r
50610         \r
50611         addLoadEvent: addLoadEvent,\r
50612         \r
50613         getQueryParamValue: function(param) {\r
50614             var q = doc.location.search || doc.location.hash;\r
50615             if (q) {\r
50616                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark\r
50617                 if (param == null) {\r
50618                     return urlEncodeIfNecessary(q);\r
50619                 }\r
50620                 var pairs = q.split("&");\r
50621                 for (var i = 0; i < pairs.length; i++) {\r
50622                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {\r
50623                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));\r
50624                     }\r
50625                 }\r
50626             }\r
50627             return "";\r
50628         },\r
50629         \r
50630         // For internal usage only\r
50631         expressInstallCallback: function() {\r
50632             if (isExpressInstallActive) {\r
50633                 var obj = getElementById(EXPRESS_INSTALL_ID);\r
50634                 if (obj && storedAltContent) {\r
50635                     obj.parentNode.replaceChild(storedAltContent, obj);\r
50636                     if (storedAltContentId) {\r
50637                         setVisibility(storedAltContentId, true);\r
50638                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }\r
50639                     }\r
50640                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }\r
50641                 }\r
50642                 isExpressInstallActive = false;\r
50643             } \r
50644         }\r
50645     };\r
50646 }();\r
50647 /**
50648  * @class Ext.FlashComponent
50649  * @extends Ext.BoxComponent
50650  * @constructor
50651  * @xtype flash
50652  */
50653 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
50654     /**
50655      * @cfg {String} flashVersion
50656      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.45'</tt>.
50657      */
50658     flashVersion : '9.0.45',
50659
50660     /**
50661      * @cfg {String} backgroundColor
50662      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
50663      */
50664     backgroundColor: '#ffffff',
50665
50666     /**
50667      * @cfg {String} wmode
50668      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
50669      */
50670     wmode: 'opaque',
50671     
50672     /**
50673      * @cfg {Object} flashVars
50674      * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
50675      */
50676     flashVars: undefined,
50677     
50678     /**
50679      * @cfg {Object} flashParams
50680      * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
50681      * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
50682      */
50683     flashParams: undefined,
50684
50685     /**
50686      * @cfg {String} url
50687      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
50688      */
50689     url: undefined,
50690     swfId : undefined,
50691     swfWidth: '100%',
50692     swfHeight: '100%',
50693
50694     /**
50695      * @cfg {Boolean} expressInstall
50696      * True to prompt the user to install flash if not installed. Note that this uses
50697      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
50698      */
50699     expressInstall: false,
50700
50701     initComponent : function(){
50702         Ext.FlashComponent.superclass.initComponent.call(this);
50703
50704         this.addEvents('initialize');
50705     },
50706
50707     onRender : function(){
50708         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
50709
50710         var params = Ext.apply({
50711             allowScriptAccess: 'always',
50712             bgcolor: this.backgroundColor,
50713             wmode: this.wmode
50714         }, this.flashParams), vars = Ext.apply({
50715             allowedDomain: document.location.hostname,
50716             elementID: this.getId(),
50717             eventHandler: 'Ext.FlashEventProxy.onEvent'
50718         }, this.flashVars);
50719
50720         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
50721             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
50722
50723         this.swf = Ext.getDom(this.id);
50724         this.el = Ext.get(this.swf);
50725     },
50726
50727     getSwfId : function(){
50728         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
50729     },
50730
50731     getId : function(){
50732         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
50733     },
50734
50735     onFlashEvent : function(e){
50736         switch(e.type){
50737             case "swfReady":
50738                 this.initSwf();
50739                 return;
50740             case "log":
50741                 return;
50742         }
50743         e.component = this;
50744         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
50745     },
50746
50747     initSwf : function(){
50748         this.onSwfReady(!!this.isInitialized);
50749         this.isInitialized = true;
50750         this.fireEvent('initialize', this);
50751     },
50752
50753     beforeDestroy: function(){
50754         if(this.rendered){
50755             swfobject.removeSWF(this.swf.id);
50756         }
50757         Ext.FlashComponent.superclass.beforeDestroy.call(this);
50758     },
50759
50760     onSwfReady : Ext.emptyFn
50761 });
50762
50763 /**
50764  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
50765  * @static
50766  * @type String
50767  */
50768 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
50769
50770 Ext.reg('flash', Ext.FlashComponent);/**\r
50771  * @class Ext.FlashProxy\r
50772  * @singleton\r
50773  */\r
50774 Ext.FlashEventProxy = {\r
50775     onEvent : function(id, e){\r
50776         var fp = Ext.getCmp(id);\r
50777         if(fp){\r
50778             fp.onFlashEvent(e);\r
50779         }else{\r
50780             arguments.callee.defer(10, this, [id, e]);\r
50781         }\r
50782     }\r
50783 }/**\r
50784  * @class Ext.chart.Chart\r
50785  * @extends Ext.FlashComponent\r
50786  * The Ext.chart package provides the capability to visualize data with flash based charting.\r
50787  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.\r
50788  * @constructor\r
50789  * @xtype chart\r
50790  */\r
50791  \r
50792  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {\r
50793     refreshBuffer: 100,\r
50794 \r
50795     /**\r
50796      * @cfg {Object} chartStyle\r
50797      * Sets styles for this chart. Contains a number of default values. Modifying this property will override\r
50798      * the base styles on the chart.\r
50799      */\r
50800     chartStyle: {\r
50801         padding: 10,\r
50802         animationEnabled: true,\r
50803         font: {\r
50804             name: 'Tahoma',\r
50805             color: 0x444444,\r
50806             size: 11\r
50807         },\r
50808         dataTip: {\r
50809             padding: 5,\r
50810             border: {\r
50811                 color: 0x99bbe8,\r
50812                 size:1\r
50813             },\r
50814             background: {\r
50815                 color: 0xDAE7F6,\r
50816                 alpha: .9\r
50817             },\r
50818             font: {\r
50819                 name: 'Tahoma',\r
50820                 color: 0x15428B,\r
50821                 size: 10,\r
50822                 bold: true\r
50823             }\r
50824         }\r
50825     },\r
50826     \r
50827     /**\r
50828      * @cfg {String} url\r
50829      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should\r
50830      * be modified to point to the local charts resource.\r
50831      */\r
50832     \r
50833     /**\r
50834      * @cfg {Object} extraStyle\r
50835      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.\r
50836      */\r
50837     extraStyle: null,\r
50838     \r
50839     /**\r
50840      * @cfg {Boolean} disableCaching\r
50841      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.\r
50842      */\r
50843     disableCaching: Ext.isIE || Ext.isOpera,\r
50844     disableCacheParam: '_dc',\r
50845 \r
50846     initComponent : function(){\r
50847         Ext.chart.Chart.superclass.initComponent.call(this);\r
50848         if(!this.url){\r
50849             this.url = Ext.chart.Chart.CHART_URL;\r
50850         }\r
50851         if(this.disableCaching){\r
50852             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));\r
50853         }\r
50854         this.addEvents(\r
50855             'itemmouseover',\r
50856             'itemmouseout',\r
50857             'itemclick',\r
50858             'itemdoubleclick',\r
50859             'itemdragstart',\r
50860             'itemdrag',\r
50861             'itemdragend'\r
50862         );\r
50863         this.store = Ext.StoreMgr.lookup(this.store);\r
50864     },\r
50865 \r
50866     /**\r
50867      * Sets a single style value on the Chart instance.\r
50868      *\r
50869      * @param name {String} Name of the Chart style value to change.\r
50870      * @param value {Object} New value to pass to the Chart style.\r
50871      */\r
50872      setStyle: function(name, value){\r
50873          this.swf.setStyle(name, Ext.encode(value));\r
50874      },\r
50875 \r
50876     /**\r
50877      * Resets all styles on the Chart instance.\r
50878      *\r
50879      * @param styles {Object} Initializer for all Chart styles.\r
50880      */\r
50881     setStyles: function(styles){\r
50882         this.swf.setStyles(Ext.encode(styles));\r
50883     },\r
50884 \r
50885     /**\r
50886      * Sets the styles on all series in the Chart.\r
50887      *\r
50888      * @param styles {Array} Initializer for all Chart series styles.\r
50889      */\r
50890     setSeriesStyles: function(styles){\r
50891         var s = [];\r
50892         Ext.each(styles, function(style){\r
50893             s.push(Ext.encode(style));\r
50894         });\r
50895         this.swf.setSeriesStyles(s);\r
50896     },\r
50897 \r
50898     setCategoryNames : function(names){\r
50899         this.swf.setCategoryNames(names);\r
50900     },\r
50901 \r
50902     setTipRenderer : function(fn){\r
50903         var chart = this;\r
50904         this.tipFnName = this.createFnProxy(function(item, index, series){\r
50905             var record = chart.store.getAt(index);\r
50906             return fn(chart, record, index, series);\r
50907         }, this.tipFnName);\r
50908         this.swf.setDataTipFunction(this.tipFnName);\r
50909     },\r
50910 \r
50911     setSeries : function(series){\r
50912         this.series = series;\r
50913         this.refresh();\r
50914     },\r
50915 \r
50916     /**\r
50917      * Changes the data store bound to this chart and refreshes it.\r
50918      * @param {Store} store The store to bind to this chart\r
50919      */\r
50920     bindStore : function(store, initial){\r
50921         if(!initial && this.store){\r
50922             if(store !== this.store && this.store.autoDestroy){\r
50923                 this.store.destroy();\r
50924             }else{\r
50925                 this.store.un("datachanged", this.refresh, this);\r
50926                 this.store.un("add", this.delayRefresh, this);\r
50927                 this.store.un("remove", this.delayRefresh, this);\r
50928                 this.store.un("update", this.delayRefresh, this);\r
50929                 this.store.un("clear", this.refresh, this);\r
50930             }\r
50931         }\r
50932         if(store){\r
50933             store = Ext.StoreMgr.lookup(store);\r
50934             store.on({\r
50935                 scope: this,\r
50936                 datachanged: this.refresh,\r
50937                 add: this.delayRefresh,\r
50938                 remove: this.delayRefresh,\r
50939                 update: this.delayRefresh,\r
50940                 clear: this.refresh\r
50941             });\r
50942         }\r
50943         this.store = store;\r
50944         if(store && !initial){\r
50945             this.refresh();\r
50946         }\r
50947     },\r
50948 \r
50949     onSwfReady : function(isReset){\r
50950         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);\r
50951         this.swf.setType(this.type);\r
50952 \r
50953         if(this.chartStyle){\r
50954             this.setStyles(Ext.apply({}, this.extraStyle, this.chartStyle));\r
50955         }\r
50956 \r
50957         if(this.categoryNames){\r
50958             this.setCategoryNames(this.categoryNames);\r
50959         }\r
50960 \r
50961         if(this.tipRenderer){\r
50962             this.setTipRenderer(this.tipRenderer);\r
50963         }\r
50964         if(!isReset){\r
50965             this.bindStore(this.store, true);\r
50966         }\r
50967         this.refresh.defer(10, this);\r
50968     },\r
50969 \r
50970     delayRefresh : function(){\r
50971         if(!this.refreshTask){\r
50972             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);\r
50973         }\r
50974         this.refreshTask.delay(this.refreshBuffer);\r
50975     },\r
50976 \r
50977     refresh : function(){\r
50978         var styleChanged = false;\r
50979         // convert the store data into something YUI charts can understand\r
50980         var data = [], rs = this.store.data.items;\r
50981         for(var j = 0, len = rs.length; j < len; j++){\r
50982             data[j] = rs[j].data;\r
50983         }\r
50984         //make a copy of the series definitions so that we aren't\r
50985         //editing them directly.\r
50986         var dataProvider = [];\r
50987         var seriesCount = 0;\r
50988         var currentSeries = null;\r
50989         var i = 0;\r
50990         if(this.series){\r
50991             seriesCount = this.series.length;\r
50992             for(i = 0; i < seriesCount; i++){\r
50993                 currentSeries = this.series[i];\r
50994                 var clonedSeries = {};\r
50995                 for(var prop in currentSeries){\r
50996                     if(prop == "style" && currentSeries.style !== null){\r
50997                         clonedSeries.style = Ext.encode(currentSeries.style);\r
50998                         styleChanged = true;\r
50999                         //we don't want to modify the styles again next time\r
51000                         //so null out the style property.\r
51001                         // this causes issues\r
51002                         // currentSeries.style = null;\r
51003                     } else{\r
51004                         clonedSeries[prop] = currentSeries[prop];\r
51005                     }\r
51006                 }\r
51007                 dataProvider.push(clonedSeries);\r
51008             }\r
51009         }\r
51010 \r
51011         if(seriesCount > 0){\r
51012             for(i = 0; i < seriesCount; i++){\r
51013                 currentSeries = dataProvider[i];\r
51014                 if(!currentSeries.type){\r
51015                     currentSeries.type = this.type;\r
51016                 }\r
51017                 currentSeries.dataProvider = data;\r
51018             }\r
51019         } else{\r
51020             dataProvider.push({type: this.type, dataProvider: data});\r
51021         }\r
51022         this.swf.setDataProvider(dataProvider);\r
51023     },\r
51024 \r
51025     createFnProxy : function(fn, old){\r
51026         if(old){\r
51027             delete window[old];\r
51028         }\r
51029         var fnName = "extFnProxy" + (++Ext.chart.Chart.PROXY_FN_ID);\r
51030         window[fnName] = fn;\r
51031         return fnName;\r
51032     },\r
51033     \r
51034     onDestroy: function(){\r
51035         Ext.chart.Chart.superclass.onDestroy.call(this);\r
51036         this.bindStore(null);\r
51037         var tip = this.tipFnName;\r
51038         if(!Ext.isEmpty(tip)){\r
51039             delete window[tip];\r
51040         }\r
51041     }\r
51042 });\r
51043 Ext.reg('chart', Ext.chart.Chart);\r
51044 Ext.chart.Chart.PROXY_FN_ID = 0;\r
51045 \r
51046 /**\r
51047  * Sets the url to load the chart from. This should be set to a local resource.\r
51048  * @static\r
51049  * @type String\r
51050  */\r
51051 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.7.0/build/charts/assets/charts.swf';\r
51052 \r
51053 /**\r
51054  * @class Ext.chart.PieChart\r
51055  * @extends Ext.chart.Chart\r
51056  * @constructor\r
51057  * @xtype piechart\r
51058  */\r
51059 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {\r
51060     type: 'pie',\r
51061 \r
51062     onSwfReady : function(isReset){\r
51063         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);\r
51064 \r
51065         this.setDataField(this.dataField);\r
51066         this.setCategoryField(this.categoryField);\r
51067     },\r
51068 \r
51069     setDataField : function(field){\r
51070         this.dataField = field;\r
51071         this.swf.setDataField(field);\r
51072     },\r
51073 \r
51074     setCategoryField : function(field){\r
51075         this.categoryField = field;\r
51076         this.swf.setCategoryField(field);\r
51077     }\r
51078 });\r
51079 Ext.reg('piechart', Ext.chart.PieChart);\r
51080 \r
51081 /**\r
51082  * @class Ext.chart.CartesianChart\r
51083  * @extends Ext.chart.Chart\r
51084  * @constructor\r
51085  * @xtype cartesianchart\r
51086  */\r
51087 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {\r
51088     onSwfReady : function(isReset){\r
51089         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);\r
51090 \r
51091         if(this.xField){\r
51092             this.setXField(this.xField);\r
51093         }\r
51094         if(this.yField){\r
51095             this.setYField(this.yField);\r
51096         }\r
51097         if(this.xAxis){\r
51098             this.setXAxis(this.xAxis);\r
51099         }\r
51100         if(this.yAxis){\r
51101             this.setYAxis(this.yAxis);\r
51102         }\r
51103     },\r
51104 \r
51105     setXField : function(value){\r
51106         this.xField = value;\r
51107         this.swf.setHorizontalField(value);\r
51108     },\r
51109 \r
51110     setYField : function(value){\r
51111         this.yField = value;\r
51112         this.swf.setVerticalField(value);\r
51113     },\r
51114 \r
51115     setXAxis : function(value){\r
51116         this.xAxis = this.createAxis('xAxis', value);\r
51117         this.swf.setHorizontalAxis(this.xAxis);\r
51118     },\r
51119 \r
51120     setYAxis : function(value){\r
51121         this.yAxis = this.createAxis('yAxis', value);\r
51122         this.swf.setVerticalAxis(this.yAxis);\r
51123     },\r
51124 \r
51125     createAxis : function(axis, value){\r
51126         var o = Ext.apply({}, value), oldFn = null;\r
51127         if(this[axis]){\r
51128             oldFn = this[axis].labelFunction;\r
51129         }\r
51130         if(o.labelRenderer){\r
51131             var fn = o.labelRenderer;\r
51132             o.labelFunction = this.createFnProxy(function(v){\r
51133                 return fn(v);\r
51134             }, oldFn);\r
51135             delete o.labelRenderer;\r
51136         }\r
51137         return o;\r
51138     }\r
51139 });\r
51140 Ext.reg('cartesianchart', Ext.chart.CartesianChart);\r
51141 \r
51142 /**\r
51143  * @class Ext.chart.LineChart\r
51144  * @extends Ext.chart.CartesianChart\r
51145  * @constructor\r
51146  * @xtype linechart\r
51147  */\r
51148 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {\r
51149     type: 'line'\r
51150 });\r
51151 Ext.reg('linechart', Ext.chart.LineChart);\r
51152 \r
51153 /**\r
51154  * @class Ext.chart.ColumnChart\r
51155  * @extends Ext.chart.CartesianChart\r
51156  * @constructor\r
51157  * @xtype columnchart\r
51158  */\r
51159 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
51160     type: 'column'\r
51161 });\r
51162 Ext.reg('columnchart', Ext.chart.ColumnChart);\r
51163 \r
51164 /**\r
51165  * @class Ext.chart.StackedColumnChart\r
51166  * @extends Ext.chart.CartesianChart\r
51167  * @constructor\r
51168  * @xtype stackedcolumnchart\r
51169  */\r
51170 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
51171     type: 'stackcolumn'\r
51172 });\r
51173 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);\r
51174 \r
51175 /**\r
51176  * @class Ext.chart.BarChart\r
51177  * @extends Ext.chart.CartesianChart\r
51178  * @constructor\r
51179  * @xtype barchart\r
51180  */\r
51181 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {\r
51182     type: 'bar'\r
51183 });\r
51184 Ext.reg('barchart', Ext.chart.BarChart);\r
51185 \r
51186 /**\r
51187  * @class Ext.chart.StackedBarChart\r
51188  * @extends Ext.chart.CartesianChart\r
51189  * @constructor\r
51190  * @xtype stackedbarchart\r
51191  */\r
51192 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {\r
51193     type: 'stackbar'\r
51194 });\r
51195 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);\r
51196 \r
51197 \r
51198 \r
51199 /**\r
51200  * @class Ext.chart.Axis\r
51201  * Defines a CartesianChart's vertical or horizontal axis.\r
51202  * @constructor\r
51203  */\r
51204 Ext.chart.Axis = function(config){\r
51205     Ext.apply(this, config);\r
51206 };\r
51207 \r
51208 Ext.chart.Axis.prototype =\r
51209 {\r
51210     /**\r
51211      * The type of axis.\r
51212      *\r
51213      * @property type\r
51214      * @type String\r
51215      */\r
51216     type: null,\r
51217 \r
51218     /**\r
51219      * The direction in which the axis is drawn. May be "horizontal" or "vertical".\r
51220      *\r
51221      * @property orientation\r
51222      * @type String\r
51223      */\r
51224     orientation: "horizontal",\r
51225 \r
51226     /**\r
51227      * If true, the items on the axis will be drawn in opposite direction.\r
51228      *\r
51229      * @property reverse\r
51230      * @type Boolean\r
51231      */\r
51232     reverse: false,\r
51233 \r
51234     /**\r
51235      * A string reference to the globally-accessible function that may be called to\r
51236      * determine each of the label values for this axis.\r
51237      *\r
51238      * @property labelFunction\r
51239      * @type String\r
51240      */\r
51241     labelFunction: null,\r
51242 \r
51243     /**\r
51244      * If true, labels that overlap previously drawn labels on the axis will be hidden.\r
51245      *\r
51246      * @property hideOverlappingLabels\r
51247      * @type Boolean\r
51248      */\r
51249     hideOverlappingLabels: true\r
51250 };\r
51251 \r
51252 /**\r
51253  * @class Ext.chart.NumericAxis\r
51254  * @extends Ext.chart.Axis\r
51255  * A type of axis whose units are measured in numeric values.\r
51256  * @constructor\r
51257  */\r
51258 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {\r
51259     type: "numeric",\r
51260 \r
51261     /**\r
51262      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
51263      * will be calculated automatically.\r
51264      *\r
51265      * @property minimum\r
51266      * @type Number\r
51267      */\r
51268     minimum: NaN,\r
51269 \r
51270     /**\r
51271      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
51272      * will be calculated automatically.\r
51273      *\r
51274      * @property maximum\r
51275      * @type Number\r
51276      */\r
51277     maximum: NaN,\r
51278 \r
51279     /**\r
51280      * The spacing between major intervals on this axis.\r
51281      *\r
51282      * @property majorUnit\r
51283      * @type Number\r
51284      */\r
51285     majorUnit: NaN,\r
51286 \r
51287     /**\r
51288      * The spacing between minor intervals on this axis.\r
51289      *\r
51290      * @property minorUnit\r
51291      * @type Number\r
51292      */\r
51293     minorUnit: NaN,\r
51294 \r
51295     /**\r
51296      * If true, the labels, ticks, gridlines, and other objects will snap to\r
51297      * the nearest major or minor unit. If false, their position will be based\r
51298      * on the minimum value.\r
51299      *\r
51300      * @property snapToUnits\r
51301      * @type Boolean\r
51302      */\r
51303     snapToUnits: true,\r
51304 \r
51305     /**\r
51306      * If true, and the bounds are calculated automatically, either the minimum or\r
51307      * maximum will be set to zero.\r
51308      *\r
51309      * @property alwaysShowZero\r
51310      * @type Boolean\r
51311      */\r
51312     alwaysShowZero: true,\r
51313 \r
51314     /**\r
51315      * The scaling algorithm to use on this axis. May be "linear" or "logarithmic".\r
51316      *\r
51317      * @property scale\r
51318      * @type String\r
51319      */\r
51320     scale: "linear"\r
51321 });\r
51322 \r
51323 /**\r
51324  * @class Ext.chart.TimeAxis\r
51325  * @extends Ext.chart.Axis\r
51326  * A type of axis whose units are measured in time-based values.\r
51327  * @constructor\r
51328  */\r
51329 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {\r
51330     type: "time",\r
51331 \r
51332     /**\r
51333      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
51334      * will be calculated automatically.\r
51335      *\r
51336      * @property minimum\r
51337      * @type Date\r
51338      */\r
51339     minimum: null,\r
51340 \r
51341     /**\r
51342      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
51343      * will be calculated automatically.\r
51344      *\r
51345      * @property maximum\r
51346      * @type Number\r
51347      */\r
51348     maximum: null,\r
51349 \r
51350     /**\r
51351      * The spacing between major intervals on this axis.\r
51352      *\r
51353      * @property majorUnit\r
51354      * @type Number\r
51355      */\r
51356     majorUnit: NaN,\r
51357 \r
51358     /**\r
51359      * The time unit used by the majorUnit.\r
51360      *\r
51361      * @property majorTimeUnit\r
51362      * @type String\r
51363      */\r
51364     majorTimeUnit: null,\r
51365 \r
51366     /**\r
51367      * The spacing between minor intervals on this axis.\r
51368      *\r
51369      * @property majorUnit\r
51370      * @type Number\r
51371      */\r
51372     minorUnit: NaN,\r
51373 \r
51374     /**\r
51375      * The time unit used by the minorUnit.\r
51376      *\r
51377      * @property majorTimeUnit\r
51378      * @type String\r
51379      */\r
51380     minorTimeUnit: null,\r
51381 \r
51382     /**\r
51383      * If true, the labels, ticks, gridlines, and other objects will snap to\r
51384      * the nearest major or minor unit. If false, their position will be based\r
51385      * on the minimum value.\r
51386      *\r
51387      * @property snapToUnits\r
51388      * @type Boolean\r
51389      */\r
51390     snapToUnits: true\r
51391 });\r
51392 \r
51393 /**\r
51394  * @class Ext.chart.CategoryAxis\r
51395  * @extends Ext.chart.Axis\r
51396  * A type of axis that displays items in categories.\r
51397  * @constructor\r
51398  */\r
51399 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {\r
51400     type: "category",\r
51401 \r
51402     /**\r
51403      * A list of category names to display along this axis.\r
51404      *\r
51405      * @property categoryNames\r
51406      * @type Array\r
51407      */\r
51408     categoryNames: null\r
51409 });\r
51410 \r
51411 /**\r
51412  * @class Ext.chart.Series\r
51413  * Series class for the charts widget.\r
51414  * @constructor\r
51415  */\r
51416 Ext.chart.Series = function(config) { Ext.apply(this, config); };\r
51417 \r
51418 Ext.chart.Series.prototype =\r
51419 {\r
51420     /**\r
51421      * The type of series.\r
51422      *\r
51423      * @property type\r
51424      * @type String\r
51425      */\r
51426     type: null,\r
51427 \r
51428     /**\r
51429      * The human-readable name of the series.\r
51430      *\r
51431      * @property displayName\r
51432      * @type String\r
51433      */\r
51434     displayName: null\r
51435 };\r
51436 \r
51437 /**\r
51438  * @class Ext.chart.CartesianSeries\r
51439  * @extends Ext.chart.Series\r
51440  * CartesianSeries class for the charts widget.\r
51441  * @constructor\r
51442  */\r
51443 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {\r
51444     /**\r
51445      * The field used to access the x-axis value from the items from the data source.\r
51446      *\r
51447      * @property xField\r
51448      * @type String\r
51449      */\r
51450     xField: null,\r
51451 \r
51452     /**\r
51453      * The field used to access the y-axis value from the items from the data source.\r
51454      *\r
51455      * @property yField\r
51456      * @type String\r
51457      */\r
51458     yField: null\r
51459 });\r
51460 \r
51461 /**\r
51462  * @class Ext.chart.ColumnSeries\r
51463  * @extends Ext.chart.CartesianSeries\r
51464  * ColumnSeries class for the charts widget.\r
51465  * @constructor\r
51466  */\r
51467 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
51468     type: "column"\r
51469 });\r
51470 \r
51471 /**\r
51472  * @class Ext.chart.LineSeries\r
51473  * @extends Ext.chart.CartesianSeries\r
51474  * LineSeries class for the charts widget.\r
51475  * @constructor\r
51476  */\r
51477 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
51478     type: "line"\r
51479 });\r
51480 \r
51481 /**\r
51482  * @class Ext.chart.BarSeries\r
51483  * @extends Ext.chart.CartesianSeries\r
51484  * BarSeries class for the charts widget.\r
51485  * @constructor\r
51486  */\r
51487 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
51488     type: "bar"\r
51489 });\r
51490 \r
51491 \r
51492 /**\r
51493  * @class Ext.chart.PieSeries\r
51494  * @extends Ext.chart.Series\r
51495  * PieSeries class for the charts widget.\r
51496  * @constructor\r
51497  */\r
51498 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {\r
51499     type: "pie",\r
51500     dataField: null,\r
51501     categoryField: null\r
51502 });/**\r
51503  * @class Ext.layout.MenuLayout\r
51504  * @extends Ext.layout.ContainerLayout\r
51505  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>\r
51506  */\r
51507  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
51508     monitorResize : true,\r
51509 \r
51510     setContainer : function(ct){\r
51511         this.monitorResize = !ct.floating;\r
51512         // This event is only fired by the menu in IE, used so we don't couple\r
51513         // the menu with the layout.\r
51514         ct.on('autosize', this.doAutoSize, this);\r
51515         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);\r
51516     },\r
51517 \r
51518     renderItem : function(c, position, target){\r
51519         if (!this.itemTpl) {\r
51520             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(\r
51521                 '<li id="{itemId}" class="{itemCls}">',\r
51522                     '<tpl if="needsIcon">',\r
51523                         '<img src="{icon}" class="{iconCls}"/>',\r
51524                     '</tpl>',\r
51525                 '</li>'\r
51526             );\r
51527         }\r
51528 \r
51529         if(c && !c.rendered){\r
51530             if(Ext.isNumber(position)){\r
51531                 position = target.dom.childNodes[position];\r
51532             }\r
51533             var a = this.getItemArgs(c);\r
51534 \r
51535 //          The Component's positionEl is the <li> it is rendered into\r
51536             c.render(c.positionEl = position ?\r
51537                 this.itemTpl.insertBefore(position, a, true) :\r
51538                 this.itemTpl.append(target, a, true));\r
51539 \r
51540 //          Link the containing <li> to the item.\r
51541             c.positionEl.menuItemId = c.getItemId();\r
51542 \r
51543 //          If rendering a regular Component, and it needs an icon,\r
51544 //          move the Component rightwards.\r
51545             if (!a.isMenuItem && a.needsIcon) {\r
51546                 c.positionEl.addClass('x-menu-list-item-indent');\r
51547             }\r
51548             this.configureItem(c, position);\r
51549         }else if(c && !this.isValidParent(c, target)){\r
51550             if(Ext.isNumber(position)){\r
51551                 position = target.dom.childNodes[position];\r
51552             }\r
51553             target.dom.insertBefore(c.getActionEl().dom, position || null);\r
51554         }\r
51555     },\r
51556 \r
51557     getItemArgs : function(c) {\r
51558         var isMenuItem = c instanceof Ext.menu.Item;\r
51559         return {\r
51560             isMenuItem: isMenuItem,\r
51561             needsIcon: !isMenuItem && (c.icon || c.iconCls),\r
51562             icon: c.icon || Ext.BLANK_IMAGE_URL,\r
51563             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),\r
51564             itemId: 'x-menu-el-' + c.id,\r
51565             itemCls: 'x-menu-list-item '\r
51566         };\r
51567     },\r
51568 \r
51569 //  Valid if the Component is in a <li> which is part of our target <ul>\r
51570     isValidParent : function(c, target) {\r
51571         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);\r
51572     },\r
51573 \r
51574     onLayout : function(ct, target){\r
51575         this.renderAll(ct, target);\r
51576         this.doAutoSize();\r
51577     },\r
51578 \r
51579     doAutoSize : function(){\r
51580         var ct = this.container, w = ct.width;\r
51581         if(ct.floating){\r
51582             if(w){\r
51583                 ct.setWidth(w);\r
51584             }else if(Ext.isIE){\r
51585                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);\r
51586                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc\r
51587                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));\r
51588             }\r
51589         }\r
51590     }\r
51591 });\r
51592 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;\r
51593 \r
51594 /**\r
51595  * @class Ext.menu.Menu\r
51596  * @extends Ext.Container\r
51597  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class\r
51598  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>\r
51599  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>\r
51600  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}\r
51601  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line\r
51602  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}\r
51603  * for an example.</p>\r
51604  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with\r
51605  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>\r
51606  *\r
51607  * @xtype menu\r
51608  */\r
51609 Ext.menu.Menu = Ext.extend(Ext.Container, {\r
51610     /**\r
51611      * @cfg {Object} defaults\r
51612      * A config object that will be applied to all items added to this container either via the {@link #items}\r
51613      * config or via the {@link #add} method.  The defaults config can contain any number of\r
51614      * name/value property pairs to be added to each item, and should be valid for the types of items\r
51615      * being added to the menu.\r
51616      */\r
51617     /**\r
51618      * @cfg {Mixed} items\r
51619      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},\r
51620      * or general {@link Ext.Component Component}s.\r
51621      */\r
51622     /**\r
51623      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)\r
51624      */\r
51625     minWidth : 120,\r
51626     /**\r
51627      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'\r
51628      * for bottom-right shadow (defaults to 'sides')\r
51629      */\r
51630     shadow : 'sides',\r
51631     /**\r
51632      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of\r
51633      * this menu (defaults to 'tl-tr?')\r
51634      */\r
51635     subMenuAlign : 'tl-tr?',\r
51636     /**\r
51637      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu\r
51638      * relative to its element of origin (defaults to 'tl-bl?')\r
51639      */\r
51640     defaultAlign : 'tl-bl?',\r
51641     /**\r
51642      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)\r
51643      */\r
51644     allowOtherMenus : false,\r
51645     /**\r
51646      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays\r
51647      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).\r
51648      */\r
51649     ignoreParentClicks : false,\r
51650     /**\r
51651      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).\r
51652      */\r
51653     enableScrolling : true,\r
51654     /**\r
51655      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).\r
51656      */\r
51657     maxHeight : null,\r
51658     /**\r
51659      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).\r
51660      */\r
51661     scrollIncrement : 24,\r
51662     /**\r
51663      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).\r
51664      */\r
51665     showSeparator : true,\r
51666     /**\r
51667      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to\r
51668      * change the default Menu popup position after aligning according to the {@link #defaultAlign}\r
51669      * configuration. Defaults to <tt>[0, 0]</tt>.\r
51670      */\r
51671     defaultOffsets : [0, 0],\r
51672     \r
51673     /**\r
51674      * @cfg {Boolean} plain\r
51675      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.\r
51676      */\r
51677     plain : false,\r
51678 \r
51679     /**\r
51680      * @cfg {Boolean} floating\r
51681      * <p>By default, a Menu configured as <b><code>floating:true</code></b>\r
51682      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,\r
51683      * floating Component with zindex=15000).\r
51684      * If configured as <b><code>floating:false</code></b>, the Menu may be\r
51685      * used as child item of another Container instead of a free-floating\r
51686      * {@link Ext.Layer Layer}.\r
51687      */\r
51688     floating : true,\r
51689 \r
51690     // private\r
51691     hidden : true,\r
51692 \r
51693     /**\r
51694      * @cfg {String/Object} layout\r
51695      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).\r
51696      * Developers <i>may</i> override this configuration option if another layout is required.\r
51697      * See {@link Ext.Container#layout} for additional information.\r
51698      */\r
51699     layout : 'menu',\r
51700     hideMode : 'offsets',    // Important for laying out Components\r
51701     scrollerHeight : 8,\r
51702     autoLayout : true,       // Provided for backwards compat\r
51703     defaultType : 'menuitem',\r
51704 \r
51705     initComponent : function(){\r
51706         if(Ext.isArray(this.initialConfig)){\r
51707             Ext.apply(this, {items:this.initialConfig});\r
51708         }\r
51709         this.addEvents(\r
51710             /**\r
51711              * @event click\r
51712              * Fires when this menu is clicked (or when the enter key is pressed while it is active)\r
51713              * @param {Ext.menu.Menu} this\r
51714             * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
51715              * @param {Ext.EventObject} e\r
51716              */\r
51717             'click',\r
51718             /**\r
51719              * @event mouseover\r
51720              * Fires when the mouse is hovering over this menu\r
51721              * @param {Ext.menu.Menu} this\r
51722              * @param {Ext.EventObject} e\r
51723              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
51724              */\r
51725             'mouseover',\r
51726             /**\r
51727              * @event mouseout\r
51728              * Fires when the mouse exits this menu\r
51729              * @param {Ext.menu.Menu} this\r
51730              * @param {Ext.EventObject} e\r
51731              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
51732              */\r
51733             'mouseout',\r
51734             /**\r
51735              * @event itemclick\r
51736              * Fires when a menu item contained in this menu is clicked\r
51737              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked\r
51738              * @param {Ext.EventObject} e\r
51739              */\r
51740             'itemclick'\r
51741         );\r
51742         Ext.menu.MenuMgr.register(this);\r
51743         if(this.floating){\r
51744             Ext.EventManager.onWindowResize(this.hide, this);\r
51745         }else{\r
51746             if(this.initialConfig.hidden !== false){\r
51747                 this.hidden = false;\r
51748             }\r
51749             this.internalDefaults = {hideOnClick: false};\r
51750         }\r
51751         Ext.menu.Menu.superclass.initComponent.call(this);\r
51752         if(this.autoLayout){\r
51753             this.on({\r
51754                 add: this.doLayout,\r
51755                 remove: this.doLayout,\r
51756                 scope: this\r
51757             });\r
51758         }\r
51759     },\r
51760 \r
51761     //private\r
51762     getLayoutTarget : function() {\r
51763         return this.ul;\r
51764     },\r
51765 \r
51766     // private\r
51767     onRender : function(ct, position){\r
51768         if(!ct){\r
51769             ct = Ext.getBody();\r
51770         }\r
51771 \r
51772         var dh = {\r
51773             id: this.getId(),\r
51774             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),\r
51775             style: this.style,\r
51776             cn: [\r
51777                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},\r
51778                 {tag: 'ul', cls: 'x-menu-list'}\r
51779             ]\r
51780         };\r
51781         if(this.floating){\r
51782             this.el = new Ext.Layer({\r
51783                 shadow: this.shadow,\r
51784                 dh: dh,\r
51785                 constrain: false,\r
51786                 parentEl: ct,\r
51787                 zindex:15000\r
51788             });\r
51789         }else{\r
51790             this.el = ct.createChild(dh);\r
51791         }\r
51792         Ext.menu.Menu.superclass.onRender.call(this, ct, position);\r
51793 \r
51794         if(!this.keyNav){\r
51795             this.keyNav = new Ext.menu.MenuNav(this);\r
51796         }\r
51797         // generic focus element\r
51798         this.focusEl = this.el.child('a.x-menu-focus');\r
51799         this.ul = this.el.child('ul.x-menu-list');\r
51800         this.mon(this.ul, {\r
51801             scope: this,\r
51802             click: this.onClick,\r
51803             mouseover: this.onMouseOver,\r
51804             mouseout: this.onMouseOut\r
51805         });\r
51806         if(this.enableScrolling){\r
51807             this.mon(this.el, {\r
51808                 scope: this,\r
51809                 delegate: '.x-menu-scroller',\r
51810                 click: this.onScroll,\r
51811                 mouseover: this.deactivateActive\r
51812             });\r
51813         }\r
51814     },\r
51815 \r
51816     // private\r
51817     findTargetItem : function(e){\r
51818         var t = e.getTarget('.x-menu-list-item', this.ul, true);\r
51819         if(t && t.menuItemId){\r
51820             return this.items.get(t.menuItemId);\r
51821         }\r
51822     },\r
51823 \r
51824     // private\r
51825     onClick : function(e){\r
51826         var t = this.findTargetItem(e);\r
51827         if(t){\r
51828             if(t.isFormField){\r
51829                 this.setActiveItem(t);\r
51830             }else if(t instanceof Ext.menu.BaseItem){\r
51831                 if(t.menu && this.ignoreParentClicks){\r
51832                     t.expandMenu();\r
51833                     e.preventDefault();\r
51834                 }else if(t.onClick){\r
51835                     t.onClick(e);\r
51836                     this.fireEvent('click', this, t, e);\r
51837                 }\r
51838             }\r
51839         }\r
51840     },\r
51841 \r
51842     // private\r
51843     setActiveItem : function(item, autoExpand){\r
51844         if(item != this.activeItem){\r
51845             this.deactivateActive();\r
51846             if((this.activeItem = item).isFormField){\r
51847                 item.focus();\r
51848             }else{\r
51849                 item.activate(autoExpand);\r
51850             }\r
51851         }else if(autoExpand){\r
51852             item.expandMenu();\r
51853         }\r
51854     },\r
51855 \r
51856     deactivateActive : function(){\r
51857         var a = this.activeItem;\r
51858         if(a){\r
51859             if(a.isFormField){\r
51860                 //Fields cannot deactivate, but Combos must collapse\r
51861                 if(a.collapse){\r
51862                     a.collapse();\r
51863                 }\r
51864             }else{\r
51865                 a.deactivate();\r
51866             }\r
51867             delete this.activeItem;\r
51868         }\r
51869     },\r
51870 \r
51871     // private\r
51872     tryActivate : function(start, step){\r
51873         var items = this.items;\r
51874         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){\r
51875             var item = items.get(i);\r
51876             if(!item.disabled && (item.canActivate || item.isFormField)){\r
51877                 this.setActiveItem(item, false);\r
51878                 return item;\r
51879             }\r
51880         }\r
51881         return false;\r
51882     },\r
51883 \r
51884     // private\r
51885     onMouseOver : function(e){\r
51886         var t = this.findTargetItem(e);\r
51887         if(t){\r
51888             if(t.canActivate && !t.disabled){\r
51889                 this.setActiveItem(t, true);\r
51890             }\r
51891         }\r
51892         this.over = true;\r
51893         this.fireEvent('mouseover', this, e, t);\r
51894     },\r
51895 \r
51896     // private\r
51897     onMouseOut : function(e){\r
51898         var t = this.findTargetItem(e);\r
51899         if(t){\r
51900             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){\r
51901                 this.activeItem.deactivate();\r
51902                 delete this.activeItem;\r
51903             }\r
51904         }\r
51905         this.over = false;\r
51906         this.fireEvent('mouseout', this, e, t);\r
51907     },\r
51908 \r
51909     // private\r
51910     onScroll : function(e, t){\r
51911         if(e){\r
51912             e.stopEvent();\r
51913         }\r
51914         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
51915         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);\r
51916         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){\r
51917            this.onScrollerOut(null, t);\r
51918         }\r
51919     },\r
51920 \r
51921     // private\r
51922     onScrollerIn : function(e, t){\r
51923         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
51924         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){\r
51925             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
51926         }\r
51927     },\r
51928 \r
51929     // private\r
51930     onScrollerOut : function(e, t){\r
51931         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
51932     },\r
51933 \r
51934     /**\r
51935      * If <code>{@link #floating}=true</code>, shows this menu relative to\r
51936      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.\r
51937      * @param {Mixed} element The element to align to\r
51938      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to\r
51939      * the element (defaults to this.defaultAlign)\r
51940      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
51941      */\r
51942     show : function(el, pos, parentMenu){\r
51943         if(this.floating){\r
51944             this.parentMenu = parentMenu;\r
51945             if(!this.el){\r
51946                 this.render();\r
51947                 this.doLayout(false, true);\r
51948             }\r
51949             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);\r
51950         }else{\r
51951             Ext.menu.Menu.superclass.show.call(this);\r
51952         }\r
51953     },\r
51954 \r
51955     /**\r
51956      * Displays this menu at a specific xy position and fires the 'show' event if a\r
51957      * handler for the 'beforeshow' event does not return false cancelling the operation.\r
51958      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)\r
51959      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
51960      */\r
51961     showAt : function(xy, parentMenu){\r
51962         if(this.fireEvent('beforeshow', this) !== false){\r
51963             this.parentMenu = parentMenu;\r
51964             if(!this.el){\r
51965                 this.render();\r
51966             }\r
51967             if(this.enableScrolling){\r
51968                 // set the position so we can figure out the constrain value.\r
51969                 this.el.setXY(xy);\r
51970                 //constrain the value, keep the y coordinate the same\r
51971                 this.constrainScroll(xy[1]);\r
51972                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];\r
51973             }else{\r
51974                 //constrain to the viewport.\r
51975                 xy = this.el.adjustForConstraints(xy);\r
51976             }\r
51977             this.el.setXY(xy);\r
51978             this.el.show();\r
51979             Ext.menu.Menu.superclass.onShow.call(this);\r
51980             if(Ext.isIE){\r
51981                 // internal event, used so we don't couple the layout to the menu\r
51982                 this.fireEvent('autosize', this);\r
51983                 if(!Ext.isIE8){\r
51984                     this.el.repaint();\r
51985                 }\r
51986             }\r
51987             this.hidden = false;\r
51988             this.focus();\r
51989             this.fireEvent('show', this);\r
51990         }\r
51991     },\r
51992 \r
51993     constrainScroll : function(y){\r
51994         var max, full = this.ul.setHeight('auto').getHeight();\r
51995         if(this.floating){\r
51996             max = this.maxHeight ? this.maxHeight : Ext.fly(this.el.dom.parentNode).getViewSize().height - y;\r
51997         }else{\r
51998             max = this.getHeight();\r
51999         }\r
52000         if(full > max && max > 0){\r
52001             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);\r
52002             this.ul.setHeight(this.activeMax);\r
52003             this.createScrollers();\r
52004             this.el.select('.x-menu-scroller').setDisplayed('');\r
52005         }else{\r
52006             this.ul.setHeight(full);\r
52007             this.el.select('.x-menu-scroller').setDisplayed('none');\r
52008         }\r
52009         this.ul.dom.scrollTop = 0;\r
52010     },\r
52011 \r
52012     createScrollers : function(){\r
52013         if(!this.scroller){\r
52014             this.scroller = {\r
52015                 pos: 0,\r
52016                 top: this.el.insertFirst({\r
52017                     tag: 'div',\r
52018                     cls: 'x-menu-scroller x-menu-scroller-top',\r
52019                     html: '&#160;'\r
52020                 }),\r
52021                 bottom: this.el.createChild({\r
52022                     tag: 'div',\r
52023                     cls: 'x-menu-scroller x-menu-scroller-bottom',\r
52024                     html: '&#160;'\r
52025                 })\r
52026             };\r
52027             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);\r
52028             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {\r
52029                 listeners: {\r
52030                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)\r
52031                 }\r
52032             });\r
52033             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);\r
52034             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {\r
52035                 listeners: {\r
52036                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)\r
52037                 }\r
52038             });\r
52039         }\r
52040     },\r
52041 \r
52042     onLayout : function(){\r
52043         if(this.isVisible()){\r
52044             if(this.enableScrolling){\r
52045                 this.constrainScroll(this.el.getTop());\r
52046             }\r
52047             if(this.floating){\r
52048                 this.el.sync();\r
52049             }\r
52050         }\r
52051     },\r
52052 \r
52053     focus : function(){\r
52054         if(!this.hidden){\r
52055             this.doFocus.defer(50, this);\r
52056         }\r
52057     },\r
52058 \r
52059     doFocus : function(){\r
52060         if(!this.hidden){\r
52061             this.focusEl.focus();\r
52062         }\r
52063     },\r
52064 \r
52065     /**\r
52066      * Hides this menu and optionally all parent menus\r
52067      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)\r
52068      */\r
52069     hide : function(deep){\r
52070         this.deepHide = deep;\r
52071         Ext.menu.Menu.superclass.hide.call(this);\r
52072         delete this.deepHide;\r
52073     },\r
52074 \r
52075     // private\r
52076     onHide : function(){\r
52077         Ext.menu.Menu.superclass.onHide.call(this);\r
52078         this.deactivateActive();\r
52079         if(this.el && this.floating){\r
52080             this.el.hide();\r
52081         }\r
52082         var pm = this.parentMenu;\r
52083         if(this.deepHide === true && pm){\r
52084             if(pm.floating){\r
52085                 pm.hide(true);\r
52086             }else{\r
52087                 pm.deactivateActive();\r
52088             }\r
52089         }\r
52090     },\r
52091 \r
52092     // private\r
52093     lookupComponent : function(c){\r
52094          if(Ext.isString(c)){\r
52095             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);\r
52096              this.applyDefaults(c);\r
52097          }else{\r
52098             if(Ext.isObject(c)){\r
52099                 c = this.getMenuItem(c);\r
52100             }else if(c.tagName || c.el){ // element. Wrap it.\r
52101                 c = new Ext.BoxComponent({\r
52102                     el: c\r
52103                 });\r
52104             }\r
52105          }\r
52106          return c;\r
52107     },\r
52108 \r
52109     applyDefaults : function(c){\r
52110         if(!Ext.isString(c)){\r
52111             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);\r
52112             var d = this.internalDefaults;\r
52113             if(d){\r
52114                 if(c.events){\r
52115                     Ext.applyIf(c.initialConfig, d);\r
52116                     Ext.apply(c, d);\r
52117                 }else{\r
52118                     Ext.applyIf(c, d);\r
52119                 }\r
52120             }\r
52121         }\r
52122         return c;\r
52123     },\r
52124 \r
52125     // private\r
52126     getMenuItem : function(config){\r
52127        if(!config.isXType){\r
52128             if(!config.xtype && Ext.isBoolean(config.checked)){\r
52129                 return new Ext.menu.CheckItem(config)\r
52130             }\r
52131             return Ext.create(config, this.defaultType);\r
52132         }\r
52133         return config;\r
52134     },\r
52135 \r
52136     /**\r
52137      * Adds a separator bar to the menu\r
52138      * @return {Ext.menu.Item} The menu item that was added\r
52139      */\r
52140     addSeparator : function(){\r
52141         return this.add(new Ext.menu.Separator());\r
52142     },\r
52143 \r
52144     /**\r
52145      * Adds an {@link Ext.Element} object to the menu\r
52146      * @param {Mixed} el The element or DOM node to add, or its id\r
52147      * @return {Ext.menu.Item} The menu item that was added\r
52148      */\r
52149     addElement : function(el){\r
52150         return this.add(new Ext.menu.BaseItem(el));\r
52151     },\r
52152 \r
52153     /**\r
52154      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu\r
52155      * @param {Ext.menu.Item} item The menu item to add\r
52156      * @return {Ext.menu.Item} The menu item that was added\r
52157      */\r
52158     addItem : function(item){\r
52159         return this.add(item);\r
52160     },\r
52161 \r
52162     /**\r
52163      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu\r
52164      * @param {Object} config A MenuItem config object\r
52165      * @return {Ext.menu.Item} The menu item that was added\r
52166      */\r
52167     addMenuItem : function(config){\r
52168         return this.add(this.getMenuItem(config));\r
52169     },\r
52170 \r
52171     /**\r
52172      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu\r
52173      * @param {String} text The text to display in the menu item\r
52174      * @return {Ext.menu.Item} The menu item that was added\r
52175      */\r
52176     addText : function(text){\r
52177         return this.add(new Ext.menu.TextItem(text));\r
52178     },\r
52179 \r
52180     //private\r
52181     onDestroy : function(){\r
52182         Ext.menu.Menu.superclass.onDestroy.call(this);\r
52183         Ext.menu.MenuMgr.unregister(this);\r
52184         Ext.EventManager.removeResizeListener(this.hide, this);\r
52185         if(this.keyNav) {\r
52186             this.keyNav.disable();\r
52187         }\r
52188         var s = this.scroller;\r
52189         if(s){\r
52190             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);\r
52191         }\r
52192         Ext.destroy(\r
52193             this.el,\r
52194             this.focusEl,\r
52195             this.ul\r
52196         );\r
52197     }\r
52198 });\r
52199 \r
52200 Ext.reg('menu', Ext.menu.Menu);\r
52201 \r
52202 // MenuNav is a private utility class used internally by the Menu\r
52203 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){\r
52204     function up(e, m){\r
52205         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){\r
52206             m.tryActivate(m.items.length-1, -1);\r
52207         }\r
52208     }\r
52209     function down(e, m){\r
52210         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){\r
52211             m.tryActivate(0, 1);\r
52212         }\r
52213     }\r
52214     return {\r
52215         constructor : function(menu){\r
52216             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);\r
52217             this.scope = this.menu = menu;\r
52218         },\r
52219 \r
52220         doRelay : function(e, h){\r
52221             var k = e.getKey();\r
52222 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB\r
52223             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {\r
52224                 return false;\r
52225             }\r
52226             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){\r
52227                 this.menu.tryActivate(0, 1);\r
52228                 return false;\r
52229             }\r
52230             return h.call(this.scope || this, e, this.menu);\r
52231         },\r
52232 \r
52233         tab: function(e, m) {\r
52234             e.stopEvent();\r
52235             if (e.shiftKey) {\r
52236                 up(e, m);\r
52237             } else {\r
52238                 down(e, m);\r
52239             }\r
52240         },\r
52241 \r
52242         up : up,\r
52243 \r
52244         down : down,\r
52245 \r
52246         right : function(e, m){\r
52247             if(m.activeItem){\r
52248                 m.activeItem.expandMenu(true);\r
52249             }\r
52250         },\r
52251 \r
52252         left : function(e, m){\r
52253             m.hide();\r
52254             if(m.parentMenu && m.parentMenu.activeItem){\r
52255                 m.parentMenu.activeItem.activate();\r
52256             }\r
52257         },\r
52258 \r
52259         enter : function(e, m){\r
52260             if(m.activeItem){\r
52261                 e.stopPropagation();\r
52262                 m.activeItem.onClick(e);\r
52263                 m.fireEvent('click', this, m.activeItem);\r
52264                 return true;\r
52265             }\r
52266         }\r
52267     };\r
52268 }());\r
52269 /**
52270  * @class Ext.menu.MenuMgr
52271  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
52272  * @singleton
52273  */
52274 Ext.menu.MenuMgr = function(){
52275    var menus, active, groups = {}, attached = false, lastShow = new Date();
52276
52277    // private - called when first menu is created
52278    function init(){
52279        menus = {};
52280        active = new Ext.util.MixedCollection();
52281        Ext.getDoc().addKeyListener(27, function(){
52282            if(active.length > 0){
52283                hideAll();
52284            }
52285        });
52286    }
52287
52288    // private
52289    function hideAll(){
52290        if(active && active.length > 0){
52291            var c = active.clone();
52292            c.each(function(m){
52293                m.hide();
52294            });
52295        }
52296    }
52297
52298    // private
52299    function onHide(m){
52300        active.remove(m);
52301        if(active.length < 1){
52302            Ext.getDoc().un("mousedown", onMouseDown);
52303            attached = false;
52304        }
52305    }
52306
52307    // private
52308    function onShow(m){
52309        var last = active.last();
52310        lastShow = new Date();
52311        active.add(m);
52312        if(!attached){
52313            Ext.getDoc().on("mousedown", onMouseDown);
52314            attached = true;
52315        }
52316        if(m.parentMenu){
52317           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
52318           m.parentMenu.activeChild = m;
52319        }else if(last && last.isVisible()){
52320           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
52321        }
52322    }
52323
52324    // private
52325    function onBeforeHide(m){
52326        if(m.activeChild){
52327            m.activeChild.hide();
52328        }
52329        if(m.autoHideTimer){
52330            clearTimeout(m.autoHideTimer);
52331            delete m.autoHideTimer;
52332        }
52333    }
52334
52335    // private
52336    function onBeforeShow(m){
52337        var pm = m.parentMenu;
52338        if(!pm && !m.allowOtherMenus){
52339            hideAll();
52340        }else if(pm && pm.activeChild){
52341            pm.activeChild.hide();
52342        }
52343    }
52344
52345    // private
52346    function onMouseDown(e){
52347        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
52348            hideAll();
52349        }
52350    }
52351
52352    // private
52353    function onBeforeCheck(mi, state){
52354        if(state){
52355            var g = groups[mi.group];
52356            for(var i = 0, l = g.length; i < l; i++){
52357                if(g[i] != mi){
52358                    g[i].setChecked(false);
52359                }
52360            }
52361        }
52362    }
52363
52364    return {
52365
52366        /**
52367         * Hides all menus that are currently visible
52368         */
52369        hideAll : function(){
52370             hideAll();  
52371        },
52372
52373        // private
52374        register : function(menu){
52375            if(!menus){
52376                init();
52377            }
52378            menus[menu.id] = menu;
52379            menu.on("beforehide", onBeforeHide);
52380            menu.on("hide", onHide);
52381            menu.on("beforeshow", onBeforeShow);
52382            menu.on("show", onShow);
52383            var g = menu.group;
52384            if(g && menu.events["checkchange"]){
52385                if(!groups[g]){
52386                    groups[g] = [];
52387                }
52388                groups[g].push(menu);
52389                menu.on("checkchange", onCheck);
52390            }
52391        },
52392
52393         /**
52394          * Returns a {@link Ext.menu.Menu} object
52395          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
52396          * be used to generate and return a new Menu instance.
52397          * @return {Ext.menu.Menu} The specified menu, or null if none are found
52398          */
52399        get : function(menu){
52400            if(typeof menu == "string"){ // menu id
52401                if(!menus){  // not initialized, no menus to return
52402                    return null;
52403                }
52404                return menus[menu];
52405            }else if(menu.events){  // menu instance
52406                return menu;
52407            }else if(typeof menu.length == 'number'){ // array of menu items?
52408                return new Ext.menu.Menu({items:menu});
52409            }else{ // otherwise, must be a config
52410                return Ext.create(menu, 'menu');
52411            }
52412        },
52413
52414        // private
52415        unregister : function(menu){
52416            delete menus[menu.id];
52417            menu.un("beforehide", onBeforeHide);
52418            menu.un("hide", onHide);
52419            menu.un("beforeshow", onBeforeShow);
52420            menu.un("show", onShow);
52421            var g = menu.group;
52422            if(g && menu.events["checkchange"]){
52423                groups[g].remove(menu);
52424                menu.un("checkchange", onCheck);
52425            }
52426        },
52427
52428        // private
52429        registerCheckable : function(menuItem){
52430            var g = menuItem.group;
52431            if(g){
52432                if(!groups[g]){
52433                    groups[g] = [];
52434                }
52435                groups[g].push(menuItem);
52436                menuItem.on("beforecheckchange", onBeforeCheck);
52437            }
52438        },
52439
52440        // private
52441        unregisterCheckable : function(menuItem){
52442            var g = menuItem.group;
52443            if(g){
52444                groups[g].remove(menuItem);
52445                menuItem.un("beforecheckchange", onBeforeCheck);
52446            }
52447        },
52448
52449        getCheckedItem : function(groupId){
52450            var g = groups[groupId];
52451            if(g){
52452                for(var i = 0, l = g.length; i < l; i++){
52453                    if(g[i].checked){
52454                        return g[i];
52455                    }
52456                }
52457            }
52458            return null;
52459        },
52460
52461        setCheckedItem : function(groupId, itemId){
52462            var g = groups[groupId];
52463            if(g){
52464                for(var i = 0, l = g.length; i < l; i++){
52465                    if(g[i].id == itemId){
52466                        g[i].setChecked(true);
52467                    }
52468                }
52469            }
52470            return null;
52471        }
52472    };
52473 }();
52474 /**
52475  * @class Ext.menu.BaseItem
52476  * @extends Ext.Component
52477  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
52478  * management and base configuration options shared by all menu components.
52479  * @constructor
52480  * Creates a new BaseItem
52481  * @param {Object} config Configuration options
52482  * @xtype menubaseitem
52483  */
52484 Ext.menu.BaseItem = function(config){
52485     Ext.menu.BaseItem.superclass.constructor.call(this, config);
52486
52487     this.addEvents(
52488         /**
52489          * @event click
52490          * Fires when this item is clicked
52491          * @param {Ext.menu.BaseItem} this
52492          * @param {Ext.EventObject} e
52493          */
52494         'click',
52495         /**
52496          * @event activate
52497          * Fires when this item is activated
52498          * @param {Ext.menu.BaseItem} this
52499          */
52500         'activate',
52501         /**
52502          * @event deactivate
52503          * Fires when this item is deactivated
52504          * @param {Ext.menu.BaseItem} this
52505          */
52506         'deactivate'
52507     );
52508
52509     if(this.handler){
52510         this.on("click", this.handler, this.scope);
52511     }
52512 };
52513
52514 Ext.extend(Ext.menu.BaseItem, Ext.Component, {
52515     /**
52516      * @property parentMenu
52517      * @type Ext.menu.Menu
52518      * The parent Menu of this Item.
52519      */
52520     /**
52521      * @cfg {Function} handler
52522      * A function that will handle the click event of this menu item (optional).
52523      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
52524      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
52525      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
52526      * </ul></div>
52527      */
52528     /**
52529      * @cfg {Object} scope
52530      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
52531      */
52532     /**
52533      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
52534      */
52535     canActivate : false,
52536     /**
52537      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
52538      */
52539     activeClass : "x-menu-item-active",
52540     /**
52541      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
52542      */
52543     hideOnClick : true,
52544     /**
52545      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
52546      */
52547     clickHideDelay : 1,
52548
52549     // private
52550     ctype : "Ext.menu.BaseItem",
52551
52552     // private
52553     actionMode : "container",
52554
52555     // private
52556     onRender : function(container, position){
52557         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
52558         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
52559             this.parentMenu = this.ownerCt;
52560         }else{
52561             this.container.addClass('x-menu-list-item');
52562             this.mon(this.el, 'click', this.onClick, this);
52563             this.mon(this.el, 'mouseenter', this.activate, this);
52564             this.mon(this.el, 'mouseleave', this.deactivate, this);
52565         }
52566     },
52567
52568     /**
52569      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
52570      * config property).  If an existing handler is already registered, it will be unregistered for you.
52571      * @param {Function} handler The function that should be called on click
52572      * @param {Object} scope The scope that should be passed to the handler
52573      */
52574     setHandler : function(handler, scope){
52575         if(this.handler){
52576             this.un("click", this.handler, this.scope);
52577         }
52578         this.on("click", this.handler = handler, this.scope = scope);
52579     },
52580
52581     // private
52582     onClick : function(e){
52583         if(!this.disabled && this.fireEvent("click", this, e) !== false
52584                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
52585             this.handleClick(e);
52586         }else{
52587             e.stopEvent();
52588         }
52589     },
52590
52591     // private
52592     activate : function(){
52593         if(this.disabled){
52594             return false;
52595         }
52596         var li = this.container;
52597         li.addClass(this.activeClass);
52598         this.region = li.getRegion().adjust(2, 2, -2, -2);
52599         this.fireEvent("activate", this);
52600         return true;
52601     },
52602
52603     // private
52604     deactivate : function(){
52605         this.container.removeClass(this.activeClass);
52606         this.fireEvent("deactivate", this);
52607     },
52608
52609     // private
52610     shouldDeactivate : function(e){
52611         return !this.region || !this.region.contains(e.getPoint());
52612     },
52613
52614     // private
52615     handleClick : function(e){
52616         var pm = this.parentMenu;
52617         if(this.hideOnClick){
52618             if(pm.floating){
52619                 pm.hide.defer(this.clickHideDelay, pm, [true]);
52620             }else{
52621                 pm.deactivateActive();
52622             }
52623         }
52624     },
52625
52626     // private. Do nothing
52627     expandMenu : Ext.emptyFn,
52628
52629     // private. Do nothing
52630     hideMenu : Ext.emptyFn
52631 });
52632 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
52633  * @class Ext.menu.TextItem
52634  * @extends Ext.menu.BaseItem
52635  * Adds a static text string to a menu, usually used as either a heading or group separator.
52636  * @constructor
52637  * Creates a new TextItem
52638  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
52639  * is applied as a config object (and should contain a <tt>text</tt> property).
52640  * @xtype menutextitem
52641  */
52642 Ext.menu.TextItem = function(cfg){
52643     if(typeof cfg == 'string'){
52644         cfg = {text: cfg}
52645     }
52646     Ext.menu.TextItem.superclass.constructor.call(this, cfg);
52647 };
52648
52649 Ext.extend(Ext.menu.TextItem, Ext.menu.BaseItem, {
52650     /**
52651      * @cfg {String} text The text to display for this item (defaults to '')
52652      */
52653     /**
52654      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
52655      */
52656     hideOnClick : false,
52657     /**
52658      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
52659      */
52660     itemCls : "x-menu-text",
52661
52662     // private
52663     onRender : function(){
52664         var s = document.createElement("span");
52665         s.className = this.itemCls;
52666         s.innerHTML = this.text;
52667         this.el = s;
52668         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
52669     }
52670 });
52671 Ext.reg('menutextitem', Ext.menu.TextItem);/**
52672  * @class Ext.menu.Separator
52673  * @extends Ext.menu.BaseItem
52674  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
52675  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
52676  * @constructor
52677  * @param {Object} config Configuration options
52678  * @xtype menuseparator
52679  */
52680 Ext.menu.Separator = function(config){
52681     Ext.menu.Separator.superclass.constructor.call(this, config);
52682 };
52683
52684 Ext.extend(Ext.menu.Separator, Ext.menu.BaseItem, {
52685     /**
52686      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
52687      */
52688     itemCls : "x-menu-sep",
52689     /**
52690      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
52691      */
52692     hideOnClick : false,
52693     
52694     /** 
52695      * @cfg {String} activeClass
52696      * @hide 
52697      */
52698     activeClass: '',
52699
52700     // private
52701     onRender : function(li){
52702         var s = document.createElement("span");
52703         s.className = this.itemCls;
52704         s.innerHTML = "&#160;";
52705         this.el = s;
52706         li.addClass("x-menu-sep-li");
52707         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
52708     }
52709 });
52710 Ext.reg('menuseparator', Ext.menu.Separator);/**\r
52711  * @class Ext.menu.Item\r
52712  * @extends Ext.menu.BaseItem\r
52713  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static\r
52714  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific\r
52715  * activation and click handling.\r
52716  * @constructor\r
52717  * Creates a new Item\r
52718  * @param {Object} config Configuration options\r
52719  * @xtype menuitem\r
52720  */\r
52721 Ext.menu.Item = function(config){\r
52722     Ext.menu.Item.superclass.constructor.call(this, config);\r
52723     if(this.menu){\r
52724         this.menu = Ext.menu.MenuMgr.get(this.menu);\r
52725     }\r
52726 };\r
52727 Ext.extend(Ext.menu.Item, Ext.menu.BaseItem, {\r
52728     /**\r
52729      * @property menu\r
52730      * @type Ext.menu.Menu\r
52731      * The submenu associated with this Item if one was configured.\r
52732      */\r
52733     /**\r
52734      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an\r
52735      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.\r
52736      */\r
52737     /**\r
52738      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If\r
52739      * icon is specified {@link #iconCls} should not be.\r
52740      */\r
52741     /**\r
52742      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for\r
52743      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.\r
52744      */\r
52745     /**\r
52746      * @cfg {String} text The text to display in this item (defaults to '').\r
52747      */\r
52748     /**\r
52749      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').\r
52750      */\r
52751     /**\r
52752      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').\r
52753      */\r
52754     /**\r
52755      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')\r
52756      */\r
52757     itemCls : 'x-menu-item',\r
52758     /**\r
52759      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)\r
52760      */\r
52761     canActivate : true,\r
52762     /**\r
52763      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)\r
52764      */\r
52765     showDelay: 200,\r
52766     // doc'd in BaseItem\r
52767     hideDelay: 200,\r
52768 \r
52769     // private\r
52770     ctype: 'Ext.menu.Item',\r
52771 \r
52772     // private\r
52773     onRender : function(container, position){\r
52774         if (!this.itemTpl) {\r
52775             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(\r
52776                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',\r
52777                     '<tpl if="hrefTarget">',\r
52778                         ' target="{hrefTarget}"',\r
52779                     '</tpl>',\r
52780                  '>',\r
52781                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',\r
52782                      '<span class="x-menu-item-text">{text}</span>',\r
52783                  '</a>'\r
52784              );\r
52785         }\r
52786         var a = this.getTemplateArgs();\r
52787         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);\r
52788         this.iconEl = this.el.child('img.x-menu-item-icon');\r
52789         this.textEl = this.el.child('.x-menu-item-text');\r
52790         Ext.menu.Item.superclass.onRender.call(this, container, position);\r
52791     },\r
52792 \r
52793     getTemplateArgs: function() {\r
52794         return {\r
52795             id: this.id,\r
52796             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),\r
52797             href: this.href || '#',\r
52798             hrefTarget: this.hrefTarget,\r
52799             icon: this.icon || Ext.BLANK_IMAGE_URL,\r
52800             iconCls: this.iconCls || '',\r
52801             text: this.itemText||this.text||'&#160;'\r
52802         };\r
52803     },\r
52804 \r
52805     /**\r
52806      * Sets the text to display in this menu item\r
52807      * @param {String} text The text to display\r
52808      */\r
52809     setText : function(text){\r
52810         this.text = text||'&#160;';\r
52811         if(this.rendered){\r
52812             this.textEl.update(this.text);\r
52813             this.parentMenu.layout.doAutoSize();\r
52814         }\r
52815     },\r
52816 \r
52817     /**\r
52818      * Sets the CSS class to apply to the item's icon element\r
52819      * @param {String} cls The CSS class to apply\r
52820      */\r
52821     setIconClass : function(cls){\r
52822         var oldCls = this.iconCls;\r
52823         this.iconCls = cls;\r
52824         if(this.rendered){\r
52825             this.iconEl.replaceClass(oldCls, this.iconCls);\r
52826         }\r
52827     },\r
52828     \r
52829     //private\r
52830     beforeDestroy: function(){\r
52831         if (this.menu){\r
52832             this.menu.destroy();\r
52833         }\r
52834         Ext.menu.Item.superclass.beforeDestroy.call(this);\r
52835     },\r
52836 \r
52837     // private\r
52838     handleClick : function(e){\r
52839         if(!this.href){ // if no link defined, stop the event automatically\r
52840             e.stopEvent();\r
52841         }\r
52842         Ext.menu.Item.superclass.handleClick.apply(this, arguments);\r
52843     },\r
52844 \r
52845     // private\r
52846     activate : function(autoExpand){\r
52847         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){\r
52848             this.focus();\r
52849             if(autoExpand){\r
52850                 this.expandMenu();\r
52851             }\r
52852         }\r
52853         return true;\r
52854     },\r
52855 \r
52856     // private\r
52857     shouldDeactivate : function(e){\r
52858         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){\r
52859             if(this.menu && this.menu.isVisible()){\r
52860                 return !this.menu.getEl().getRegion().contains(e.getPoint());\r
52861             }\r
52862             return true;\r
52863         }\r
52864         return false;\r
52865     },\r
52866 \r
52867     // private\r
52868     deactivate : function(){\r
52869         Ext.menu.Item.superclass.deactivate.apply(this, arguments);\r
52870         this.hideMenu();\r
52871     },\r
52872 \r
52873     // private\r
52874     expandMenu : function(autoActivate){\r
52875         if(!this.disabled && this.menu){\r
52876             clearTimeout(this.hideTimer);\r
52877             delete this.hideTimer;\r
52878             if(!this.menu.isVisible() && !this.showTimer){\r
52879                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);\r
52880             }else if (this.menu.isVisible() && autoActivate){\r
52881                 this.menu.tryActivate(0, 1);\r
52882             }\r
52883         }\r
52884     },\r
52885 \r
52886     // private\r
52887     deferExpand : function(autoActivate){\r
52888         delete this.showTimer;\r
52889         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);\r
52890         if(autoActivate){\r
52891             this.menu.tryActivate(0, 1);\r
52892         }\r
52893     },\r
52894 \r
52895     // private\r
52896     hideMenu : function(){\r
52897         clearTimeout(this.showTimer);\r
52898         delete this.showTimer;\r
52899         if(!this.hideTimer && this.menu && this.menu.isVisible()){\r
52900             this.hideTimer = this.deferHide.defer(this.hideDelay, this);\r
52901         }\r
52902     },\r
52903 \r
52904     // private\r
52905     deferHide : function(){\r
52906         delete this.hideTimer;\r
52907         if(this.menu.over){\r
52908             this.parentMenu.setActiveItem(this, false);\r
52909         }else{\r
52910             this.menu.hide();\r
52911         }\r
52912     }\r
52913 });\r
52914 Ext.reg('menuitem', Ext.menu.Item);/**
52915  * @class Ext.menu.CheckItem
52916  * @extends Ext.menu.Item
52917  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
52918  * @constructor
52919  * Creates a new CheckItem
52920  * @param {Object} config Configuration options
52921  * @xtype menucheckitem
52922  */
52923 Ext.menu.CheckItem = function(config){
52924     Ext.menu.CheckItem.superclass.constructor.call(this, config);
52925     this.addEvents(
52926         /**
52927          * @event beforecheckchange
52928          * Fires before the checked value is set, providing an opportunity to cancel if needed
52929          * @param {Ext.menu.CheckItem} this
52930          * @param {Boolean} checked The new checked value that will be set
52931          */
52932         "beforecheckchange" ,
52933         /**
52934          * @event checkchange
52935          * Fires after the checked value has been set
52936          * @param {Ext.menu.CheckItem} this
52937          * @param {Boolean} checked The checked value that was set
52938          */
52939         "checkchange"
52940     );
52941     /**
52942      * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
52943      * is provided, it will be called automatically when the checkchange event fires.
52944      * @param {Ext.menu.CheckItem} this
52945      * @param {Boolean} checked The checked value that was set
52946      * @method checkHandler
52947      */
52948     if(this.checkHandler){
52949         this.on('checkchange', this.checkHandler, this.scope);
52950     }
52951     Ext.menu.MenuMgr.registerCheckable(this);
52952 };
52953 Ext.extend(Ext.menu.CheckItem, Ext.menu.Item, {
52954     /**
52955      * @cfg {String} group
52956      * All check items with the same group name will automatically be grouped into a single-select
52957      * radio button group (defaults to '')
52958      */
52959     /**
52960      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
52961      */
52962     itemCls : "x-menu-item x-menu-check-item",
52963     /**
52964      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
52965      */
52966     groupClass : "x-menu-group-item",
52967
52968     /**
52969      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
52970      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
52971      * initialized with checked = true will be rendered as checked.
52972      */
52973     checked: false,
52974
52975     // private
52976     ctype: "Ext.menu.CheckItem",
52977
52978     // private
52979     onRender : function(c){
52980         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
52981         if(this.group){
52982             this.el.addClass(this.groupClass);
52983         }
52984         if(this.checked){
52985             this.checked = false;
52986             this.setChecked(true, true);
52987         }
52988     },
52989
52990     // private
52991     destroy : function(){
52992         Ext.menu.MenuMgr.unregisterCheckable(this);
52993         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
52994     },
52995
52996     /**
52997      * Set the checked state of this item
52998      * @param {Boolean} checked The new checked value
52999      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
53000      */
53001     setChecked : function(state, suppressEvent){
53002         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
53003             if(this.container){
53004                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
53005             }
53006             this.checked = state;
53007             if(suppressEvent !== true){
53008                 this.fireEvent("checkchange", this, state);
53009             }
53010         }
53011     },
53012
53013     // private
53014     handleClick : function(e){
53015        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
53016            this.setChecked(!this.checked);
53017        }
53018        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
53019     }
53020 });
53021 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**\r
53022  * @class Ext.menu.DateMenu\r
53023  * @extends Ext.menu.Menu\r
53024  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>\r
53025  * <p>Notes:</p><div class="mdetail-params"><ul>\r
53026  * <li>Although not listed here, the <b>constructor</b> for this class\r
53027  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>\r
53028  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be\r
53029  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.\r
53030  * Applying {@link Ext.DatePicker DatePicker} configuration settings to\r
53031  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>\r
53032  * </ul></div>\r
53033  * @xtype datemenu\r
53034  */\r
53035  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {\r
53036     /** \r
53037      * @cfg {Boolean} enableScrolling\r
53038      * @hide \r
53039      */\r
53040     enableScrolling : false,\r
53041     /**\r
53042      * @cfg {Function} handler\r
53043      * Optional. A function that will handle the select event of this menu.\r
53044      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
53045      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>\r
53046      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>\r
53047      * </ul></div>\r
53048      */\r
53049     /**\r
53050      * @cfg {Object} scope\r
53051      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
53052      * function will be called.  Defaults to this DateMenu instance.\r
53053      */    \r
53054     /** \r
53055      * @cfg {Boolean} hideOnClick\r
53056      * False to continue showing the menu after a date is selected, defaults to true.\r
53057      */\r
53058     hideOnClick : true,\r
53059     \r
53060     /** \r
53061      * @cfg {String} pickerId\r
53062      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.\r
53063      */\r
53064     pickerId : null,\r
53065     \r
53066     /** \r
53067      * @cfg {Number} maxHeight\r
53068      * @hide \r
53069      */\r
53070     /** \r
53071      * @cfg {Number} scrollIncrement\r
53072      * @hide \r
53073      */\r
53074     /**\r
53075      * The {@link Ext.DatePicker} instance for this DateMenu\r
53076      * @property picker\r
53077      * @type DatePicker\r
53078      */\r
53079     cls : 'x-date-menu',\r
53080     \r
53081     /**\r
53082      * @event click\r
53083      * @hide\r
53084      */\r
53085     \r
53086     /**\r
53087      * @event itemclick\r
53088      * @hide\r
53089      */\r
53090 \r
53091     initComponent : function(){\r
53092         this.on('beforeshow', this.onBeforeShow, this);\r
53093         if(this.strict = (Ext.isIE7 && Ext.isStrict)){\r
53094             this.on('show', this.onShow, this, {single: true, delay: 20});\r
53095         }\r
53096         Ext.apply(this, {\r
53097             plain: true,\r
53098             showSeparator: false,\r
53099             items: this.picker = new Ext.DatePicker(Ext.applyIf({\r
53100                 internalRender: this.strict || !Ext.isIE,\r
53101                 ctCls: 'x-menu-date-item',\r
53102                 id: this.pickerId\r
53103             }, this.initialConfig))\r
53104         });\r
53105         this.picker.purgeListeners();\r
53106         Ext.menu.DateMenu.superclass.initComponent.call(this);\r
53107         /**\r
53108          * @event select\r
53109          * Fires when a date is selected from the {@link #picker Ext.DatePicker}\r
53110          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}\r
53111          * @param {Date} date The selected date\r
53112          */\r
53113         this.relayEvents(this.picker, ['select']);\r
53114         this.on('select', this.menuHide, this);\r
53115         if(this.handler){\r
53116             this.on('select', this.handler, this.scope || this);\r
53117         }\r
53118     },\r
53119 \r
53120     menuHide : function() {\r
53121         if(this.hideOnClick){\r
53122             this.hide(true);\r
53123         }\r
53124     },\r
53125 \r
53126     onBeforeShow : function(){\r
53127         if(this.picker){\r
53128             this.picker.hideMonthPicker(true);\r
53129         }\r
53130     },\r
53131 \r
53132     onShow : function(){\r
53133         var el = this.picker.getEl();\r
53134         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode\r
53135     }\r
53136  });\r
53137  Ext.reg('datemenu', Ext.menu.DateMenu);\r
53138  /**\r
53139  * @class Ext.menu.ColorMenu\r
53140  * @extends Ext.menu.Menu\r
53141  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>\r
53142  * <p>Notes:</p><div class="mdetail-params"><ul>\r
53143  * <li>Although not listed here, the <b>constructor</b> for this class\r
53144  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>\r
53145  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be\r
53146  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.\r
53147  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to\r
53148  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>\r
53149  * </ul></div> * \r
53150  * @xtype colormenu\r
53151  */\r
53152  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {\r
53153     /** \r
53154      * @cfg {Boolean} enableScrolling\r
53155      * @hide \r
53156      */\r
53157     enableScrolling : false,\r
53158     /**\r
53159      * @cfg {Function} handler\r
53160      * Optional. A function that will handle the select event of this menu.\r
53161      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
53162      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>\r
53163      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>\r
53164      * </ul></div>\r
53165      */\r
53166     /**\r
53167      * @cfg {Object} scope\r
53168      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
53169      * function will be called.  Defaults to this ColorMenu instance.\r
53170      */    \r
53171     \r
53172     /** \r
53173      * @cfg {Boolean} hideOnClick\r
53174      * False to continue showing the menu after a color is selected, defaults to true.\r
53175      */\r
53176     hideOnClick : true,\r
53177     \r
53178     cls : 'x-color-menu',\r
53179     \r
53180     /** \r
53181      * @cfg {String} paletteId\r
53182      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.\r
53183      */\r
53184     paletteId : null,\r
53185     \r
53186     /** \r
53187      * @cfg {Number} maxHeight\r
53188      * @hide \r
53189      */\r
53190     /** \r
53191      * @cfg {Number} scrollIncrement\r
53192      * @hide \r
53193      */\r
53194     /**\r
53195      * @property palette\r
53196      * @type ColorPalette\r
53197      * The {@link Ext.ColorPalette} instance for this ColorMenu\r
53198      */\r
53199     \r
53200     \r
53201     /**\r
53202      * @event click\r
53203      * @hide\r
53204      */\r
53205     \r
53206     /**\r
53207      * @event itemclick\r
53208      * @hide\r
53209      */\r
53210     \r
53211     initComponent : function(){\r
53212         Ext.apply(this, {\r
53213             plain: true,\r
53214             showSeparator: false,\r
53215             items: this.palette = new Ext.ColorPalette(Ext.applyIf({\r
53216                 id: this.paletteId\r
53217             }, this.initialConfig))\r
53218         });\r
53219         this.palette.purgeListeners();\r
53220         Ext.menu.ColorMenu.superclass.initComponent.call(this);\r
53221         /**\r
53222          * @event select\r
53223          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}\r
53224          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}\r
53225          * @param {String} color The 6-digit color hex code (without the # symbol)\r
53226          */\r
53227         this.relayEvents(this.palette, ['select']);\r
53228         this.on('select', this.menuHide, this);\r
53229         if(this.handler){\r
53230             this.on('select', this.handler, this.scope || this);\r
53231         }\r
53232     },\r
53233 \r
53234     menuHide : function(){\r
53235         if(this.hideOnClick){\r
53236             this.hide(true);\r
53237         }\r
53238     }\r
53239 });\r
53240 Ext.reg('colormenu', Ext.menu.ColorMenu);\r
53241 /**
53242  * @class Ext.form.Field
53243  * @extends Ext.BoxComponent
53244  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
53245  * @constructor
53246  * Creates a new Field
53247  * @param {Object} config Configuration options
53248  * @xtype field
53249  */
53250 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
53251     /**
53252      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
53253      * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
53254      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
53255      * is not supported and should be avoided.
53256      */
53257     /**
53258      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
53259      * not those which are built via applyTo (defaults to undefined).
53260      */
53261     /**
53262      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
53263      */
53264     /**
53265      * @cfg {String} name The field's HTML name attribute (defaults to '').
53266      * <b>Note</b>: this property must be set if this field is to be automatically included with
53267      * {@link Ext.form.BasicForm#submit form submit()}.
53268      */
53269     /**
53270      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
53271      */
53272
53273     /**
53274      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
53275      */
53276     invalidClass : 'x-form-invalid',
53277     /**
53278      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
53279      * (defaults to 'The value in this field is invalid')
53280      */
53281     invalidText : 'The value in this field is invalid',
53282     /**
53283      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
53284      */
53285     focusClass : 'x-form-focus',
53286     /**
53287      * @cfg {Boolean} preventMark
53288      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
53289      * Defaults to <tt>false</tt>.
53290      */
53291     /**
53292      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
53293       automatic validation (defaults to 'keyup').
53294      */
53295     validationEvent : 'keyup',
53296     /**
53297      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
53298      */
53299     validateOnBlur : true,
53300     /**
53301      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
53302      * is initiated (defaults to 250)
53303      */
53304     validationDelay : 250,
53305     /**
53306      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
53307      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
53308      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
53309      * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
53310      */
53311     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
53312     /**
53313      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
53314      */
53315     fieldClass : 'x-form-field',
53316     /**
53317      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values
53318      * (defaults to 'qtip'):
53319      *<pre>
53320 Value         Description
53321 -----------   ----------------------------------------------------------------------
53322 qtip          Display a quick tip when the user hovers over the field
53323 title         Display a default browser title attribute popup
53324 under         Add a block div beneath the field containing the error text
53325 side          Add an error icon to the right of the field with a popup on hover
53326 [element id]  Add the error text directly to the innerHTML of the specified element
53327 </pre>
53328      */
53329     msgTarget : 'qtip',
53330     /**
53331      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
53332      * (defaults to 'normal').
53333      */
53334     msgFx : 'normal',
53335     /**
53336      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
53337      * (defaults to <tt>false</tt>).
53338      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
53339      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
53340      * ComboBox or DateField; it gives you the option of forcing the user to choose
53341      * via the trigger without typing in the text box. To hide the trigger use
53342      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
53343      */
53344     readOnly : false,
53345     /**
53346      * @cfg {Boolean} disabled True to disable the field (defaults to false).
53347      * <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>,
53348      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
53349      */
53350     disabled : false,
53351
53352     // private
53353     isFormField : true,
53354
53355     // private
53356     msgDisplay: '',
53357
53358     // private
53359     hasFocus : false,
53360
53361     // private
53362     initComponent : function(){
53363         Ext.form.Field.superclass.initComponent.call(this);
53364         this.addEvents(
53365             /**
53366              * @event focus
53367              * Fires when this field receives input focus.
53368              * @param {Ext.form.Field} this
53369              */
53370             'focus',
53371             /**
53372              * @event blur
53373              * Fires when this field loses input focus.
53374              * @param {Ext.form.Field} this
53375              */
53376             'blur',
53377             /**
53378              * @event specialkey
53379              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
53380              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
53381              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
53382              * For example: <pre><code>
53383 var form = new Ext.form.FormPanel({
53384     ...
53385     items: [{
53386             fieldLabel: 'Field 1',
53387             name: 'field1',
53388             allowBlank: false
53389         },{
53390             fieldLabel: 'Field 2',
53391             name: 'field2',
53392             listeners: {
53393                 specialkey: function(field, e){
53394                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
53395                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
53396                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
53397                         var form = field.ownerCt.getForm();
53398                         form.submit();
53399                     }
53400                 }
53401             }
53402         }
53403     ],
53404     ...
53405 });
53406              * </code></pre>
53407              * @param {Ext.form.Field} this
53408              * @param {Ext.EventObject} e The event object
53409              */
53410             'specialkey',
53411             /**
53412              * @event change
53413              * Fires just before the field blurs if the field value has changed.
53414              * @param {Ext.form.Field} this
53415              * @param {Mixed} newValue The new value
53416              * @param {Mixed} oldValue The original value
53417              */
53418             'change',
53419             /**
53420              * @event invalid
53421              * Fires after the field has been marked as invalid.
53422              * @param {Ext.form.Field} this
53423              * @param {String} msg The validation message
53424              */
53425             'invalid',
53426             /**
53427              * @event valid
53428              * Fires after the field has been validated with no errors.
53429              * @param {Ext.form.Field} this
53430              */
53431             'valid'
53432         );
53433     },
53434
53435     /**
53436      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
53437      * attribute of the field if available.
53438      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
53439      */
53440     getName : function(){
53441         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
53442     },
53443
53444     // private
53445     onRender : function(ct, position){
53446         if(!this.el){
53447             var cfg = this.getAutoCreate();
53448
53449             if(!cfg.name){
53450                 cfg.name = this.name || this.id;
53451             }
53452             if(this.inputType){
53453                 cfg.type = this.inputType;
53454             }
53455             this.autoEl = cfg;
53456         }
53457         Ext.form.Field.superclass.onRender.call(this, ct, position);
53458
53459         var type = this.el.dom.type;
53460         if(type){
53461             if(type == 'password'){
53462                 type = 'text';
53463             }
53464             this.el.addClass('x-form-'+type);
53465         }
53466         if(this.readOnly){
53467             this.el.dom.readOnly = true;
53468         }
53469         if(this.tabIndex !== undefined){
53470             this.el.dom.setAttribute('tabIndex', this.tabIndex);
53471         }
53472
53473         this.el.addClass([this.fieldClass, this.cls]);
53474     },
53475
53476     // private
53477     getItemCt : function(){
53478         return this.itemCt;
53479     },
53480
53481     // private
53482     initValue : function(){
53483         if(this.value !== undefined){
53484             this.setValue(this.value);
53485         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
53486             this.setValue(this.el.dom.value);
53487         }
53488         /**
53489          * The original value of the field as configured in the {@link #value} configuration, or
53490          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
53491          * setting is <code>true</code>.
53492          * @type mixed
53493          * @property originalValue
53494          */
53495         this.originalValue = this.getValue();
53496     },
53497
53498     /**
53499      * <p>Returns true if the value of this Field has been changed from its original value.
53500      * Will return false if the field is disabled or has not been rendered yet.</p>
53501      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
53502      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
53503      * then the <i>original value</i> is updated when the values are loaded by
53504      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
53505      * @return {Boolean} True if this field has been changed from its original value (and
53506      * is not disabled), false otherwise.
53507      */
53508     isDirty : function() {
53509         if(this.disabled || !this.rendered) {
53510             return false;
53511         }
53512         return String(this.getValue()) !== String(this.originalValue);
53513     },
53514
53515     // private
53516     afterRender : function(){
53517         Ext.form.Field.superclass.afterRender.call(this);
53518         this.initEvents();
53519         this.initValue();
53520     },
53521
53522     // private
53523     fireKey : function(e){
53524         if(e.isSpecialKey()){
53525             this.fireEvent('specialkey', this, e);
53526         }
53527     },
53528
53529     /**
53530      * Resets the current field value to the originally loaded value and clears any validation messages.
53531      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
53532      */
53533     reset : function(){
53534         this.setValue(this.originalValue);
53535         this.clearInvalid();
53536     },
53537
53538     // private
53539     initEvents : function(){
53540         this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey,  this);
53541         this.mon(this.el, 'focus', this.onFocus, this);
53542
53543         // standardise buffer across all browsers + OS-es for consistent event order.
53544         // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
53545         this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
53546     },
53547
53548     // private
53549     preFocus: Ext.emptyFn,
53550
53551     // private
53552     onFocus : function(){
53553         this.preFocus();
53554         if(this.focusClass){
53555             this.el.addClass(this.focusClass);
53556         }
53557         if(!this.hasFocus){
53558             this.hasFocus = true;
53559             this.startValue = this.getValue();
53560             this.fireEvent('focus', this);
53561         }
53562     },
53563
53564     // private
53565     beforeBlur : Ext.emptyFn,
53566
53567     // private
53568     onBlur : function(){
53569         this.beforeBlur();
53570         if(this.focusClass){
53571             this.el.removeClass(this.focusClass);
53572         }
53573         this.hasFocus = false;
53574         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent != 'blur')){
53575             this.validate();
53576         }
53577         var v = this.getValue();
53578         if(String(v) !== String(this.startValue)){
53579             this.fireEvent('change', this, v, this.startValue);
53580         }
53581         this.fireEvent('blur', this);
53582         this.postBlur();
53583     },
53584
53585     // private
53586     postBlur : Ext.emptyFn,
53587
53588     /**
53589      * Returns whether or not the field value is currently valid by
53590      * {@link #validateValue validating} the {@link #processValue processed value}
53591      * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
53592      * @param {Boolean} preventMark True to disable marking the field invalid
53593      * @return {Boolean} True if the value is valid, else false
53594      */
53595     isValid : function(preventMark){
53596         if(this.disabled){
53597             return true;
53598         }
53599         var restore = this.preventMark;
53600         this.preventMark = preventMark === true;
53601         var v = this.validateValue(this.processValue(this.getRawValue()));
53602         this.preventMark = restore;
53603         return v;
53604     },
53605
53606     /**
53607      * Validates the field value
53608      * @return {Boolean} True if the value is valid, else false
53609      */
53610     validate : function(){
53611         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
53612             this.clearInvalid();
53613             return true;
53614         }
53615         return false;
53616     },
53617
53618     /**
53619      * This method should only be overridden if necessary to prepare raw values
53620      * for validation (see {@link #validate} and {@link #isValid}).  This method
53621      * is expected to return the processed value for the field which will
53622      * be used for validation (see validateValue method).
53623      * @param {Mixed} value
53624      */
53625     processValue : function(value){
53626         return value;
53627     },
53628
53629     /**
53630      * @private
53631      * Subclasses should provide the validation implementation by overriding this
53632      * @param {Mixed} value
53633      */
53634     validateValue : function(value){
53635         return true;
53636     },
53637
53638     /**
53639      * Mark this field as invalid, using {@link #msgTarget} to determine how to
53640      * display the error and applying {@link #invalidClass} to the field's element.
53641      * <b>Note</b>: this method does not actually make the field
53642      * {@link #isValid invalid}.
53643      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
53644      */
53645     markInvalid : function(msg){
53646         if(!this.rendered || this.preventMark){ // not rendered
53647             return;
53648         }
53649         msg = msg || this.invalidText;
53650
53651         var mt = this.getMessageHandler();
53652         if(mt){
53653             mt.mark(this, msg);
53654         }else if(this.msgTarget){
53655             this.el.addClass(this.invalidClass);
53656             var t = Ext.getDom(this.msgTarget);
53657             if(t){
53658                 t.innerHTML = msg;
53659                 t.style.display = this.msgDisplay;
53660             }
53661         }
53662         this.fireEvent('invalid', this, msg);
53663     },
53664
53665     /**
53666      * Clear any invalid styles/messages for this field
53667      */
53668     clearInvalid : function(){
53669         if(!this.rendered || this.preventMark){ // not rendered
53670             return;
53671         }
53672         this.el.removeClass(this.invalidClass);
53673         var mt = this.getMessageHandler();
53674         if(mt){
53675             mt.clear(this);
53676         }else if(this.msgTarget){
53677             this.el.removeClass(this.invalidClass);
53678             var t = Ext.getDom(this.msgTarget);
53679             if(t){
53680                 t.innerHTML = '';
53681                 t.style.display = 'none';
53682             }
53683         }
53684         this.fireEvent('valid', this);
53685     },
53686
53687     // private
53688     getMessageHandler : function(){
53689         return Ext.form.MessageTargets[this.msgTarget];
53690     },
53691
53692     // private
53693     getErrorCt : function(){
53694         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
53695             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
53696     },
53697
53698     // private
53699     alignErrorIcon : function(){
53700         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
53701     },
53702
53703     /**
53704      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
53705      * @return {Mixed} value The field value
53706      */
53707     getRawValue : function(){
53708         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
53709         if(v === this.emptyText){
53710             v = '';
53711         }
53712         return v;
53713     },
53714
53715     /**
53716      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
53717      * @return {Mixed} value The field value
53718      */
53719     getValue : function(){
53720         if(!this.rendered) {
53721             return this.value;
53722         }
53723         var v = this.el.getValue();
53724         if(v === this.emptyText || v === undefined){
53725             v = '';
53726         }
53727         return v;
53728     },
53729
53730     /**
53731      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
53732      * @param {Mixed} value The value to set
53733      * @return {Mixed} value The field value that is set
53734      */
53735     setRawValue : function(v){
53736         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
53737     },
53738
53739     /**
53740      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
53741      * @param {Mixed} value The value to set
53742      * @return {Ext.form.Field} this
53743      */
53744     setValue : function(v){
53745         this.value = v;
53746         if(this.rendered){
53747             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
53748             this.validate();
53749         }
53750         return this;
53751     },
53752
53753     // private, does not work for all fields
53754     append : function(v){
53755          this.setValue([this.getValue(), v].join(''));
53756     }
53757
53758     /**
53759      * @cfg {Boolean} autoWidth @hide
53760      */
53761     /**
53762      * @cfg {Boolean} autoHeight @hide
53763      */
53764
53765     /**
53766      * @cfg {String} autoEl @hide
53767      */
53768 });
53769
53770
53771 Ext.form.MessageTargets = {
53772     'qtip' : {
53773         mark: function(field, msg){
53774             field.el.addClass(field.invalidClass);
53775             field.el.dom.qtip = msg;
53776             field.el.dom.qclass = 'x-form-invalid-tip';
53777             if(Ext.QuickTips){ // fix for floating editors interacting with DND
53778                 Ext.QuickTips.enable();
53779             }
53780         },
53781         clear: function(field){
53782             field.el.removeClass(field.invalidClass);
53783             field.el.dom.qtip = '';
53784         }
53785     },
53786     'title' : {
53787         mark: function(field, msg){
53788             field.el.addClass(field.invalidClass);
53789             field.el.dom.title = msg;
53790         },
53791         clear: function(field){
53792             field.el.dom.title = '';
53793         }
53794     },
53795     'under' : {
53796         mark: function(field, msg){
53797             field.el.addClass(field.invalidClass);
53798             if(!field.errorEl){
53799                 var elp = field.getErrorCt();
53800                 if(!elp){ // field has no container el
53801                     field.el.dom.title = msg;
53802                     return;
53803                 }
53804                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
53805                 field.errorEl.setWidth(elp.getWidth(true)-20);
53806             }
53807             field.errorEl.update(msg);
53808             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
53809         },
53810         clear: function(field){
53811             field.el.removeClass(field.invalidClass);
53812             if(field.errorEl){
53813                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
53814             }else{
53815                 field.el.dom.title = '';
53816             }
53817         }
53818     },
53819     'side' : {
53820         mark: function(field, msg){
53821             field.el.addClass(field.invalidClass);
53822             if(!field.errorIcon){
53823                 var elp = field.getErrorCt();
53824                 if(!elp){ // field has no container el
53825                     field.el.dom.title = msg;
53826                     return;
53827                 }
53828                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
53829             }
53830             field.alignErrorIcon();
53831             field.errorIcon.dom.qtip = msg;
53832             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
53833             field.errorIcon.show();
53834             field.on('resize', field.alignErrorIcon, field);
53835         },
53836         clear: function(field){
53837             field.el.removeClass(field.invalidClass);
53838             if(field.errorIcon){
53839                 field.errorIcon.dom.qtip = '';
53840                 field.errorIcon.hide();
53841                 field.un('resize', field.alignErrorIcon, field);
53842             }else{
53843                 field.el.dom.title = '';
53844             }
53845         }
53846     }
53847 };
53848
53849 // anything other than normal should be considered experimental
53850 Ext.form.Field.msgFx = {
53851     normal : {
53852         show: function(msgEl, f){
53853             msgEl.setDisplayed('block');
53854         },
53855
53856         hide : function(msgEl, f){
53857             msgEl.setDisplayed(false).update('');
53858         }
53859     },
53860
53861     slide : {
53862         show: function(msgEl, f){
53863             msgEl.slideIn('t', {stopFx:true});
53864         },
53865
53866         hide : function(msgEl, f){
53867             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
53868         }
53869     },
53870
53871     slideRight : {
53872         show: function(msgEl, f){
53873             msgEl.fixDisplay();
53874             msgEl.alignTo(f.el, 'tl-tr');
53875             msgEl.slideIn('l', {stopFx:true});
53876         },
53877
53878         hide : function(msgEl, f){
53879             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
53880         }
53881     }
53882 };
53883 Ext.reg('field', Ext.form.Field);
53884 /**
53885  * @class Ext.form.TextField
53886  * @extends Ext.form.Field
53887  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
53888  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
53889  * and {@link Ext.form.ComboBox}).</p>
53890  * <p><b><u>Validation</u></b></p>
53891  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
53892  * <p><b><u>Alter Validation Behavior</u></b></p>
53893  * <p>Validation behavior for each field can be configured:</p>
53894  * <div class="mdetail-params"><ul>
53895  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
53896  * show if any validation step above does not provide a message when invalid</li>
53897  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
53898  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
53899  * but before being validated</li>
53900  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
53901  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
53902  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
53903  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
53904  * </ul></div>
53905  * 
53906  * @constructor Creates a new TextField
53907  * @param {Object} config Configuration options
53908  * 
53909  * @xtype textfield
53910  */
53911 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
53912     /**
53913      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
53914      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
53915      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
53916      */
53917     /**
53918      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
53919      * before validation (defaults to <tt>null</tt>).
53920      */
53921     /**
53922      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
53923      * (defaults to <tt>false</tt>)
53924      */
53925     grow : false,
53926     /**
53927      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
53928      * to <tt>30</tt>)
53929      */
53930     growMin : 30,
53931     /**
53932      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
53933      * to <tt>800</tt>)
53934      */
53935     growMax : 800,
53936     /**
53937      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
53938      */
53939     vtype : null,
53940     /**
53941      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
53942      * not match (defaults to <tt>null</tt>)
53943      */
53944     maskRe : null,
53945     /**
53946      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
53947      * to <tt>false</tt>)
53948      */
53949     disableKeyFilter : false,
53950     /**
53951      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
53952      * <tt>true</tt>)
53953      */
53954     allowBlank : true,
53955     /**
53956      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
53957      */
53958     minLength : 0,
53959     /**
53960      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
53961      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
53962      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
53963      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
53964      * any attributes you want to a field, for example:<pre><code>
53965 var myField = new Ext.form.NumberField({
53966     id: 'mobile',
53967     anchor:'90%',
53968     fieldLabel: 'Mobile',
53969     maxLength: 16, // for validation
53970     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
53971 });
53972 </code></pre>
53973      */
53974     maxLength : Number.MAX_VALUE,
53975     /**
53976      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
53977      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
53978      */
53979     minLengthText : 'The minimum length for this field is {0}',
53980     /**
53981      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
53982      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
53983      */
53984     maxLengthText : 'The maximum length for this field is {0}',
53985     /**
53986      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
53987      * receives input focus (defaults to <tt>false</tt>)
53988      */
53989     selectOnFocus : false,
53990     /**
53991      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
53992      * fails (defaults to <tt>'This field is required'</tt>)
53993      */
53994     blankText : 'This field is required',
53995     /**
53996      * @cfg {Function} validator
53997      * <p>A custom validation function to be called during field validation ({@link #validateValue})
53998      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
53999      * developer to override the default validation process.</p>
54000      * <br><p>This function will be passed the following Parameters:</p>
54001      * <div class="mdetail-params"><ul>
54002      * <li><code>value</code>: <i>Mixed</i>
54003      * <div class="sub-desc">The current field value</div></li>
54004      * </ul></div>
54005      * <br><p>This function is to Return:</p>
54006      * <div class="mdetail-params"><ul>
54007      * <li><code>true</code>: <i>Boolean</i>
54008      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
54009      * <li><code>msg</code>: <i>String</i>
54010      * <div class="sub-desc">An error message if the value is invalid</div></li>
54011      * </ul></div>
54012      */
54013     validator : null,
54014     /**
54015      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
54016      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
54017      * <b><tt>{@link #regexText}</tt></b>.
54018      */
54019     regex : null,
54020     /**
54021      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
54022      * test fails during validation (defaults to <tt>''</tt>)
54023      */
54024     regexText : '',
54025     /**
54026      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
54027      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
54028      * with a {@link #name}.
54029      */
54030     emptyText : null,
54031     /**
54032      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
54033      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
54034      * depending on the current field value.
54035      */
54036     emptyClass : 'x-form-empty-field',
54037
54038     /**
54039      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
54040      * field (defaults to <tt>false</tt>)
54041      */
54042
54043     initComponent : function(){
54044         Ext.form.TextField.superclass.initComponent.call(this);
54045         this.addEvents(
54046             /**
54047              * @event autosize
54048              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
54049              * may not have actually changed size according to the default logic, but this event provides
54050              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
54051              * @param {Ext.form.Field} this This text field
54052              * @param {Number} width The new field width
54053              */
54054             'autosize',
54055
54056             /**
54057              * @event keydown
54058              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
54059              * is set to true.
54060              * @param {Ext.form.TextField} this This text field
54061              * @param {Ext.EventObject} e
54062              */
54063             'keydown',
54064             /**
54065              * @event keyup
54066              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
54067              * is set to true.
54068              * @param {Ext.form.TextField} this This text field
54069              * @param {Ext.EventObject} e
54070              */
54071             'keyup',
54072             /**
54073              * @event keypress
54074              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
54075              * is set to true.
54076              * @param {Ext.form.TextField} this This text field
54077              * @param {Ext.EventObject} e
54078              */
54079             'keypress'
54080         );
54081     },
54082
54083     // private
54084     initEvents : function(){
54085         Ext.form.TextField.superclass.initEvents.call(this);
54086         if(this.validationEvent == 'keyup'){
54087             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
54088             this.mon(this.el, 'keyup', this.filterValidation, this);
54089         }
54090         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
54091                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
54092         }
54093         if(this.selectOnFocus || this.emptyText){            
54094             this.mon(this.el, 'mousedown', this.onMouseDown, this);
54095             
54096             if(this.emptyText){
54097                 this.applyEmptyText();
54098             }
54099         }
54100         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
54101                 this.mon(this.el, 'keypress', this.filterKeys, this);
54102         }
54103         if(this.grow){
54104                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
54105                         this.mon(this.el, 'click', this.autoSize, this);
54106         }
54107         if(this.enableKeyEvents){
54108             this.mon(this.el, {
54109                 scope: this,
54110                 keyup: this.onKeyUp,
54111                 keydown: this.onKeyDown,
54112                 keypress: this.onKeyPress
54113             });
54114         }
54115     },
54116     
54117     onMouseDown: function(e){
54118         if(!this.hasFocus){
54119             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
54120         }
54121     },
54122
54123     processValue : function(value){
54124         if(this.stripCharsRe){
54125             var newValue = value.replace(this.stripCharsRe, '');
54126             if(newValue !== value){
54127                 this.setRawValue(newValue);
54128                 return newValue;
54129             }
54130         }
54131         return value;
54132     },
54133
54134     filterValidation : function(e){
54135         if(!e.isNavKeyPress()){
54136             this.validationTask.delay(this.validationDelay);
54137         }
54138     },
54139     
54140     //private
54141     onDisable: function(){
54142         Ext.form.TextField.superclass.onDisable.call(this);
54143         if(Ext.isIE){
54144             this.el.dom.unselectable = 'on';
54145         }
54146     },
54147     
54148     //private
54149     onEnable: function(){
54150         Ext.form.TextField.superclass.onEnable.call(this);
54151         if(Ext.isIE){
54152             this.el.dom.unselectable = '';
54153         }
54154     },
54155
54156     // private
54157     onKeyUpBuffered : function(e){
54158         if(!e.isNavKeyPress()){
54159             this.autoSize();
54160         }
54161     },
54162
54163     // private
54164     onKeyUp : function(e){
54165         this.fireEvent('keyup', this, e);
54166     },
54167
54168     // private
54169     onKeyDown : function(e){
54170         this.fireEvent('keydown', this, e);
54171     },
54172
54173     // private
54174     onKeyPress : function(e){
54175         this.fireEvent('keypress', this, e);
54176     },
54177
54178     /**
54179      * Resets the current field value to the originally-loaded value and clears any validation messages.
54180      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
54181      * original value was blank.
54182      */
54183     reset : function(){
54184         Ext.form.TextField.superclass.reset.call(this);
54185         this.applyEmptyText();
54186     },
54187
54188     applyEmptyText : function(){
54189         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
54190             this.setRawValue(this.emptyText);
54191             this.el.addClass(this.emptyClass);
54192         }
54193     },
54194
54195     // private
54196     preFocus : function(){
54197         var el = this.el;
54198         if(this.emptyText){
54199             if(el.dom.value == this.emptyText){
54200                 this.setRawValue('');
54201             }
54202             el.removeClass(this.emptyClass);
54203         }
54204         if(this.selectOnFocus){
54205             el.dom.select();
54206         }
54207     },
54208
54209     // private
54210     postBlur : function(){
54211         this.applyEmptyText();
54212     },
54213
54214     // private
54215     filterKeys : function(e){
54216         // special keys don't generate charCodes, so leave them alone
54217         if(e.ctrlKey || e.isSpecialKey()){
54218             return;
54219         }
54220         
54221         if(!this.maskRe.test(String.fromCharCode(e.getCharCode()))){
54222             e.stopEvent();
54223         }
54224     },
54225
54226     setValue : function(v){
54227         if(this.emptyText && this.el && !Ext.isEmpty(v)){
54228             this.el.removeClass(this.emptyClass);
54229         }
54230         Ext.form.TextField.superclass.setValue.apply(this, arguments);
54231         this.applyEmptyText();
54232         this.autoSize();
54233         return this;
54234     },
54235
54236     /**
54237      * <p>Validates a value according to the field's validation rules and marks the field as invalid
54238      * if the validation fails. Validation rules are processed in the following order:</p>
54239      * <div class="mdetail-params"><ul>
54240      * 
54241      * <li><b>1. Field specific validator</b>
54242      * <div class="sub-desc">
54243      * <p>A validator offers a way to customize and reuse a validation specification.
54244      * If a field is configured with a <code>{@link #validator}</code>
54245      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
54246      * function is expected to return either:
54247      * <div class="mdetail-params"><ul>
54248      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
54249      * <li>a String to represent the invalid message if invalid (validation halts).</li>
54250      * </ul></div>
54251      * </div></li>
54252      * 
54253      * <li><b>2. Basic Validation</b>
54254      * <div class="sub-desc">
54255      * <p>If the <code>{@link #validator}</code> has not halted validation,
54256      * basic validation proceeds as follows:</p>
54257      * 
54258      * <div class="mdetail-params"><ul>
54259      * 
54260      * <li><code>{@link #allowBlank}</code> : (Invalid message =
54261      * <code>{@link #emptyText}</code>)<div class="sub-desc">
54262      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
54263      * blank field will cause validation to halt at this step and return
54264      * Boolean true or false accordingly.  
54265      * </div></li>
54266      * 
54267      * <li><code>{@link #minLength}</code> : (Invalid message =
54268      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
54269      * If the passed value does not satisfy the <code>{@link #minLength}</code>
54270      * specified, validation halts.
54271      * </div></li>
54272      * 
54273      * <li><code>{@link #maxLength}</code> : (Invalid message =
54274      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
54275      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
54276      * specified, validation halts.
54277      * </div></li>
54278      * 
54279      * </ul></div>
54280      * </div></li>
54281      * 
54282      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
54283      * <div class="sub-desc">
54284      * <p>If none of the prior validation steps halts validation, a field
54285      * configured with a <code>{@link #vtype}</code> will utilize the
54286      * corresponding {@link Ext.form.VTypes VTypes} validation function.
54287      * If invalid, either the field's <code>{@link #vtypeText}</code> or
54288      * the VTypes vtype Text property will be used for the invalid message.
54289      * Keystrokes on the field will be filtered according to the VTypes
54290      * vtype Mask property.</p>
54291      * </div></li>
54292      * 
54293      * <li><b>4. Field specific regex test</b>
54294      * <div class="sub-desc">
54295      * <p>If none of the prior validation steps halts validation, a field's
54296      * configured <code>{@link #regex}</code> test will be processed.
54297      * The invalid message for this test is configured with
54298      * <code>{@link #regexText}</code>.</p>
54299      * </div></li>
54300      * 
54301      * @param {Mixed} value The value to validate
54302      * @return {Boolean} True if the value is valid, else false
54303      */
54304     validateValue : function(value){
54305         if(Ext.isFunction(this.validator)){
54306             var msg = this.validator(value);
54307             if(msg !== true){
54308                 this.markInvalid(msg);
54309                 return false;
54310             }
54311         }
54312         if(value.length < 1 || value === this.emptyText){ // if it's blank
54313              if(this.allowBlank){
54314                  this.clearInvalid();
54315                  return true;
54316              }else{
54317                  this.markInvalid(this.blankText);
54318                  return false;
54319              }
54320         }
54321         if(value.length < this.minLength){
54322             this.markInvalid(String.format(this.minLengthText, this.minLength));
54323             return false;
54324         }
54325         if(value.length > this.maxLength){
54326             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
54327             return false;
54328         }       
54329         if(this.vtype){
54330             var vt = Ext.form.VTypes;
54331             if(!vt[this.vtype](value, this)){
54332                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
54333                 return false;
54334             }
54335         }
54336         if(this.regex && !this.regex.test(value)){
54337             this.markInvalid(this.regexText);
54338             return false;
54339         }
54340         return true;
54341     },
54342
54343     /**
54344      * Selects text in this field
54345      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
54346      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
54347      */
54348     selectText : function(start, end){
54349         var v = this.getRawValue();
54350         var doFocus = false;
54351         if(v.length > 0){
54352             start = start === undefined ? 0 : start;
54353             end = end === undefined ? v.length : end;
54354             var d = this.el.dom;
54355             if(d.setSelectionRange){
54356                 d.setSelectionRange(start, end);
54357             }else if(d.createTextRange){
54358                 var range = d.createTextRange();
54359                 range.moveStart('character', start);
54360                 range.moveEnd('character', end-v.length);
54361                 range.select();
54362             }
54363             doFocus = Ext.isGecko || Ext.isOpera;
54364         }else{
54365             doFocus = true;
54366         }
54367         if(doFocus){
54368             this.focus();
54369         }
54370     },
54371
54372     /**
54373      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
54374      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
54375      */
54376     autoSize : function(){
54377         if(!this.grow || !this.rendered){
54378             return;
54379         }
54380         if(!this.metrics){
54381             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
54382         }
54383         var el = this.el;
54384         var v = el.dom.value;
54385         var d = document.createElement('div');
54386         d.appendChild(document.createTextNode(v));
54387         v = d.innerHTML;
54388         d = null;
54389         Ext.removeNode(d);
54390         v += '&#160;';
54391         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
54392         this.el.setWidth(w);
54393         this.fireEvent('autosize', this, w);
54394     },
54395         
54396         onDestroy: function(){
54397                 if(this.validationTask){
54398                         this.validationTask.cancel();
54399                         this.validationTask = null;
54400                 }
54401                 Ext.form.TextField.superclass.onDestroy.call(this);
54402         }
54403 });
54404 Ext.reg('textfield', Ext.form.TextField);
54405 /**\r
54406  * @class Ext.form.TriggerField\r
54407  * @extends Ext.form.TextField\r
54408  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).\r
54409  * The trigger has no default action, so you must assign a function to implement the trigger click handler by\r
54410  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox\r
54411  * for which you can provide a custom implementation.  For example:\r
54412  * <pre><code>\r
54413 var trigger = new Ext.form.TriggerField();\r
54414 trigger.onTriggerClick = myTriggerFn;\r
54415 trigger.applyToMarkup('my-field');\r
54416 </code></pre>\r
54417  *\r
54418  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.\r
54419  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.\r
54420  * \r
54421  * @constructor\r
54422  * Create a new TriggerField.\r
54423  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied\r
54424  * to the base TextField)\r
54425  * @xtype trigger\r
54426  */\r
54427 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {\r
54428     /**\r
54429      * @cfg {String} triggerClass\r
54430      * An additional CSS class used to style the trigger button.  The trigger will always get the\r
54431      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.\r
54432      */\r
54433     /**\r
54434      * @cfg {Mixed} triggerConfig\r
54435      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the\r
54436      * trigger element for this Field. (Optional).</p>\r
54437      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>\r
54438      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning\r
54439      * and appearance of the trigger.  Defaults to:</p>\r
54440      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>\r
54441      */\r
54442     /**\r
54443      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default\r
54444      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.\r
54445      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>\r
54446      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>\r
54447      */\r
54448     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},\r
54449     /**\r
54450      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base\r
54451      * text field (defaults to <tt>false</tt>)\r
54452      */\r
54453     hideTrigger:false,\r
54454     /**\r
54455      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,\r
54456      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>)\r
54457      */\r
54458     editable: true,\r
54459     /**\r
54460      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to\r
54461      * <tt>x-trigger-wrap-focus</tt>.\r
54462      */\r
54463     wrapFocusClass: 'x-trigger-wrap-focus',\r
54464     /**\r
54465      * @hide \r
54466      * @method autoSize\r
54467      */\r
54468     autoSize: Ext.emptyFn,\r
54469     // private\r
54470     monitorTab : true,\r
54471     // private\r
54472     deferHeight : true,\r
54473     // private\r
54474     mimicing : false,\r
54475     \r
54476     actionMode: 'wrap',\r
54477     \r
54478     defaultTriggerWidth: 17,\r
54479 \r
54480     // private\r
54481     onResize : function(w, h){\r
54482         Ext.form.TriggerField.superclass.onResize.call(this, w, h);\r
54483         var tw = this.getTriggerWidth();\r
54484         if(Ext.isNumber(w)){\r
54485             this.el.setWidth(w - tw);\r
54486         }\r
54487         this.wrap.setWidth(this.el.getWidth() + tw);\r
54488     },\r
54489     \r
54490     getTriggerWidth: function(){\r
54491         var tw = this.trigger.getWidth();\r
54492         if(!this.hideTrigger && tw === 0){\r
54493             tw = this.defaultTriggerWidth;\r
54494         }\r
54495         return tw;\r
54496     },\r
54497 \r
54498     // private\r
54499     alignErrorIcon : function(){\r
54500         if(this.wrap){\r
54501             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);\r
54502         }\r
54503     },\r
54504 \r
54505     // private\r
54506     onRender : function(ct, position){\r
54507         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();\r
54508         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);\r
54509 \r
54510         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});\r
54511         this.trigger = this.wrap.createChild(this.triggerConfig ||\r
54512                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});\r
54513         if(this.hideTrigger){\r
54514             this.trigger.setDisplayed(false);\r
54515         }\r
54516         this.initTrigger();\r
54517         if(!this.width){\r
54518             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());\r
54519         }\r
54520         if(!this.editable){\r
54521             this.editable = true;\r
54522             this.setEditable(false);\r
54523         }\r
54524         this.resizeEl = this.positionEl = this.wrap;\r
54525     },\r
54526 \r
54527     afterRender : function(){\r
54528         Ext.form.TriggerField.superclass.afterRender.call(this);\r
54529     },\r
54530 \r
54531     // private\r
54532     initTrigger : function(){\r
54533         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});\r
54534         this.trigger.addClassOnOver('x-form-trigger-over');\r
54535         this.trigger.addClassOnClick('x-form-trigger-click');\r
54536     },\r
54537 \r
54538     // private\r
54539     onDestroy : function(){\r
54540         Ext.destroy(this.trigger, this.wrap);\r
54541         if (this.mimicing){\r
54542             this.doc.un('mousedown', this.mimicBlur, this);\r
54543         }\r
54544         Ext.form.TriggerField.superclass.onDestroy.call(this);\r
54545     },\r
54546 \r
54547     // private\r
54548     onFocus : function(){\r
54549         Ext.form.TriggerField.superclass.onFocus.call(this);\r
54550         if(!this.mimicing){\r
54551             this.wrap.addClass(this.wrapFocusClass);\r
54552             this.mimicing = true;\r
54553             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});\r
54554             if(this.monitorTab){\r
54555                 this.on('specialkey', this.checkTab, this);\r
54556             }\r
54557         }\r
54558     },\r
54559 \r
54560     // private\r
54561     checkTab : function(me, e){\r
54562         if(e.getKey() == e.TAB){\r
54563             this.triggerBlur();\r
54564         }\r
54565     },\r
54566 \r
54567     // private\r
54568     onBlur : Ext.emptyFn,\r
54569 \r
54570     // private\r
54571     mimicBlur : function(e){\r
54572         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){\r
54573             this.triggerBlur();\r
54574         }\r
54575     },\r
54576 \r
54577     // private\r
54578     triggerBlur : function(){\r
54579         this.mimicing = false;\r
54580         this.doc.un('mousedown', this.mimicBlur, this);\r
54581         if(this.monitorTab && this.el){\r
54582             this.un('specialkey', this.checkTab, this);\r
54583         }\r
54584         Ext.form.TriggerField.superclass.onBlur.call(this);\r
54585         if(this.wrap){\r
54586             this.wrap.removeClass(this.wrapFocusClass);\r
54587         }\r
54588     },\r
54589 \r
54590     beforeBlur : Ext.emptyFn, \r
54591     \r
54592     /**\r
54593      * Allow or prevent the user from directly editing the field text.  If false is passed,\r
54594      * the user will only be able to modify the field using the trigger.  This method\r
54595      * is the runtime equivalent of setting the 'editable' config option at config time.\r
54596      * @param {Boolean} value True to allow the user to directly edit the field text\r
54597      */\r
54598     setEditable : function(value){\r
54599         if(value == this.editable){\r
54600             return;\r
54601         }\r
54602         this.editable = value;\r
54603         if(!value){\r
54604             this.el.addClass('x-trigger-noedit').on('click', this.onTriggerClick, this).dom.setAttribute('readOnly', true);\r
54605         }else{\r
54606             this.el.removeClass('x-trigger-noedit').un('click', this.onTriggerClick,  this).dom.removeAttribute('readOnly');\r
54607         }\r
54608     },\r
54609 \r
54610     // private\r
54611     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.\r
54612     validateBlur : function(e){\r
54613         return true;\r
54614     },\r
54615 \r
54616     /**\r
54617      * The function that should handle the trigger's click event.  This method does nothing by default\r
54618      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for\r
54619      * sample implementations.\r
54620      * @method\r
54621      * @param {EventObject} e\r
54622      */\r
54623     onTriggerClick : Ext.emptyFn\r
54624 \r
54625     /**\r
54626      * @cfg {Boolean} grow @hide\r
54627      */\r
54628     /**\r
54629      * @cfg {Number} growMin @hide\r
54630      */\r
54631     /**\r
54632      * @cfg {Number} growMax @hide\r
54633      */\r
54634 });\r
54635 \r
54636 /**\r
54637  * @class Ext.form.TwinTriggerField\r
54638  * @extends Ext.form.TriggerField\r
54639  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class\r
54640  * to be extended by an implementing class.  For an example of implementing this class, see the custom\r
54641  * SearchField implementation here:\r
54642  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>\r
54643  */\r
54644 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {\r
54645     /**\r
54646      * @cfg {Mixed} triggerConfig\r
54647      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements\r
54648      * for this Field. (Optional).</p>\r
54649      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.\r
54650      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see\r
54651      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>\r
54652      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,\r
54653      * positioning and appearance of the triggers.</p>\r
54654      */\r
54655     /**\r
54656      * @cfg {String} trigger1Class\r
54657      * An additional CSS class used to style the trigger button.  The trigger will always get the\r
54658      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.\r
54659      */\r
54660     /**\r
54661      * @cfg {String} trigger2Class\r
54662      * An additional CSS class used to style the trigger button.  The trigger will always get the\r
54663      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.\r
54664      */\r
54665 \r
54666     initComponent : function(){\r
54667         Ext.form.TwinTriggerField.superclass.initComponent.call(this);\r
54668 \r
54669         this.triggerConfig = {\r
54670             tag:'span', cls:'x-form-twin-triggers', cn:[\r
54671             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},\r
54672             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}\r
54673         ]};\r
54674     },\r
54675 \r
54676     getTrigger : function(index){\r
54677         return this.triggers[index];\r
54678     },\r
54679 \r
54680     initTrigger : function(){\r
54681         var ts = this.trigger.select('.x-form-trigger', true);\r
54682         var triggerField = this;\r
54683         ts.each(function(t, all, index){\r
54684             var triggerIndex = 'Trigger'+(index+1);\r
54685             t.hide = function(){\r
54686                 var w = triggerField.wrap.getWidth();\r
54687                 this.dom.style.display = 'none';\r
54688                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());\r
54689                 this['hidden' + triggerIndex] = true;\r
54690             };\r
54691             t.show = function(){\r
54692                 var w = triggerField.wrap.getWidth();\r
54693                 this.dom.style.display = '';\r
54694                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());\r
54695                 this['hidden' + triggerIndex] = false;\r
54696             };\r
54697             \r
54698             if(this['hide'+triggerIndex]){\r
54699                 t.dom.style.display = 'none';\r
54700                 this['hidden' + triggerIndex] = true;\r
54701             }\r
54702             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});\r
54703             t.addClassOnOver('x-form-trigger-over');\r
54704             t.addClassOnClick('x-form-trigger-click');\r
54705         }, this);\r
54706         this.triggers = ts.elements;\r
54707     },\r
54708     \r
54709     getTriggerWidth: function(){\r
54710         var tw = 0;\r
54711         Ext.each(this.triggers, function(t, index){\r
54712             var triggerIndex = 'Trigger' + (index + 1),\r
54713                 w = t.getWidth();\r
54714             if(w === 0 && !this['hidden' + triggerIndex]){\r
54715                 tw += this.defaultTriggerWidth;\r
54716             }else{\r
54717                 tw += w;\r
54718             }\r
54719         }, this);\r
54720         return tw;\r
54721     },\r
54722     \r
54723     // private\r
54724     onDestroy : function() {\r
54725         Ext.destroy(this.triggers);\r
54726         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);\r
54727     },\r
54728 \r
54729     /**\r
54730      * The function that should handle the trigger's click event.  This method does nothing by default\r
54731      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}\r
54732      * for additional information.  \r
54733      * @method\r
54734      * @param {EventObject} e\r
54735      */\r
54736     onTrigger1Click : Ext.emptyFn,\r
54737     /**\r
54738      * The function that should handle the trigger's click event.  This method does nothing by default\r
54739      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}\r
54740      * for additional information.  \r
54741      * @method\r
54742      * @param {EventObject} e\r
54743      */\r
54744     onTrigger2Click : Ext.emptyFn\r
54745 });\r
54746 Ext.reg('trigger', Ext.form.TriggerField);/**
54747  * @class Ext.form.TextArea
54748  * @extends Ext.form.TextField
54749  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
54750  * support for auto-sizing.
54751  * @constructor
54752  * Creates a new TextArea
54753  * @param {Object} config Configuration options
54754  * @xtype textarea
54755  */
54756 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
54757     /**
54758      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
54759      * (defaults to <tt>60</tt>)
54760      */
54761     growMin : 60,
54762     /**
54763      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
54764      * (defaults to <tt>1000</tt>)
54765      */
54766     growMax: 1000,
54767     growAppend : '&#160;\n&#160;',
54768     growPad : Ext.isWebKit ? -6 : 0,
54769
54770     enterIsSpecial : false,
54771
54772     /**
54773      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
54774      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
54775      * <tt>false</tt>.
54776      */
54777     preventScrollbars: false,
54778     /**
54779      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
54780      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
54781      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
54782      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
54783      */
54784
54785     // private
54786     onRender : function(ct, position){
54787         if(!this.el){
54788             this.defaultAutoCreate = {
54789                 tag: "textarea",
54790                 style:"width:100px;height:60px;",
54791                 autocomplete: "off"
54792             };
54793         }
54794         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
54795         if(this.grow){
54796             this.textSizeEl = Ext.DomHelper.append(document.body, {
54797                 tag: "pre", cls: "x-form-grow-sizer"
54798             });
54799             if(this.preventScrollbars){
54800                 this.el.setStyle("overflow", "hidden");
54801             }
54802             this.el.setHeight(this.growMin);
54803         }
54804     },
54805
54806     onDestroy : function(){
54807         Ext.destroy(this.textSizeEl);
54808         Ext.form.TextArea.superclass.onDestroy.call(this);
54809     },
54810
54811     fireKey : function(e){
54812         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
54813             this.fireEvent("specialkey", this, e);
54814         }
54815     },
54816
54817     // private
54818     onKeyUp : function(e){
54819         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
54820             this.autoSize();
54821         }
54822         Ext.form.TextArea.superclass.onKeyUp.call(this, e);
54823     },
54824
54825     /**
54826      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
54827      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
54828      */
54829     autoSize: function(){
54830         if(!this.grow || !this.textSizeEl){
54831             return;
54832         }
54833         var el = this.el;
54834         var v = el.dom.value;
54835         var ts = this.textSizeEl;
54836         ts.innerHTML = '';
54837         ts.appendChild(document.createTextNode(v));
54838         v = ts.innerHTML;
54839         Ext.fly(ts).setWidth(this.el.getWidth());
54840         if(v.length < 1){
54841             v = "&#160;&#160;";
54842         }else{
54843             v += this.growAppend;
54844             if(Ext.isIE){
54845                 v = v.replace(/\n/g, '<br />');
54846             }
54847         }
54848         ts.innerHTML = v;
54849         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin) + this.growPad);
54850         if(h != this.lastHeight){
54851             this.lastHeight = h;
54852             this.el.setHeight(h);
54853             this.fireEvent("autosize", this, h);
54854         }
54855     }
54856 });
54857 Ext.reg('textarea', Ext.form.TextArea);/**
54858  * @class Ext.form.NumberField
54859  * @extends Ext.form.TextField
54860  * Numeric text field that provides automatic keystroke filtering and numeric validation.
54861  * @constructor
54862  * Creates a new NumberField
54863  * @param {Object} config Configuration options
54864  * @xtype numberfield
54865  */
54866 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
54867     /**
54868      * @cfg {RegExp} stripCharsRe @hide
54869      */
54870     /**
54871      * @cfg {RegExp} maskRe @hide
54872      */
54873     /**
54874      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
54875      */
54876     fieldClass: "x-form-field x-form-num-field",
54877     /**
54878      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
54879      */
54880     allowDecimals : true,
54881     /**
54882      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
54883      */
54884     decimalSeparator : ".",
54885     /**
54886      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
54887      */
54888     decimalPrecision : 2,
54889     /**
54890      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
54891      */
54892     allowNegative : true,
54893     /**
54894      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
54895      */
54896     minValue : Number.NEGATIVE_INFINITY,
54897     /**
54898      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
54899      */
54900     maxValue : Number.MAX_VALUE,
54901     /**
54902      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
54903      */
54904     minText : "The minimum value for this field is {0}",
54905     /**
54906      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
54907      */
54908     maxText : "The maximum value for this field is {0}",
54909     /**
54910      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
54911      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
54912      */
54913     nanText : "{0} is not a valid number",
54914     /**
54915      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
54916      */
54917     baseChars : "0123456789",
54918
54919     // private
54920     initEvents : function(){
54921         var allowed = this.baseChars + '';
54922         if (this.allowDecimals) {
54923             allowed += this.decimalSeparator;
54924         }
54925         if (this.allowNegative) {
54926             allowed += '-';
54927         }
54928         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
54929         Ext.form.NumberField.superclass.initEvents.call(this);
54930     },
54931
54932     // private
54933     validateValue : function(value){
54934         if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
54935             return false;
54936         }
54937         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
54938              return true;
54939         }
54940         value = String(value).replace(this.decimalSeparator, ".");
54941         if(isNaN(value)){
54942             this.markInvalid(String.format(this.nanText, value));
54943             return false;
54944         }
54945         var num = this.parseValue(value);
54946         if(num < this.minValue){
54947             this.markInvalid(String.format(this.minText, this.minValue));
54948             return false;
54949         }
54950         if(num > this.maxValue){
54951             this.markInvalid(String.format(this.maxText, this.maxValue));
54952             return false;
54953         }
54954         return true;
54955     },
54956
54957     getValue : function(){
54958         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
54959     },
54960
54961     setValue : function(v){
54962         v = typeof v == 'number' ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
54963         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
54964         return Ext.form.NumberField.superclass.setValue.call(this, v);
54965     },
54966
54967     // private
54968     parseValue : function(value){
54969         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
54970         return isNaN(value) ? '' : value;
54971     },
54972
54973     // private
54974     fixPrecision : function(value){
54975         var nan = isNaN(value);
54976         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
54977            return nan ? '' : value;
54978         }
54979         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
54980     },
54981
54982     beforeBlur : function(){
54983         var v = this.parseValue(this.getRawValue());
54984         if(!Ext.isEmpty(v)){
54985             this.setValue(this.fixPrecision(v));
54986         }
54987     }
54988 });
54989 Ext.reg('numberfield', Ext.form.NumberField);/**
54990  * @class Ext.form.DateField
54991  * @extends Ext.form.TriggerField
54992  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
54993  * @constructor
54994  * Create a new DateField
54995  * @param {Object} config
54996  * @xtype datefield
54997  */
54998 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
54999     /**
55000      * @cfg {String} format
55001      * The default date format string which can be overriden for localization support.  The format must be
55002      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
55003      */
55004     format : "m/d/Y",
55005     /**
55006      * @cfg {String} altFormats
55007      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
55008      * does not match the defined format (defaults to
55009      * <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>).
55010      */
55011     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",
55012     /**
55013      * @cfg {String} disabledDaysText
55014      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
55015      */
55016     disabledDaysText : "Disabled",
55017     /**
55018      * @cfg {String} disabledDatesText
55019      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
55020      */
55021     disabledDatesText : "Disabled",
55022     /**
55023      * @cfg {String} minText
55024      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
55025      * <tt>'The date in this field must be after {minValue}'</tt>).
55026      */
55027     minText : "The date in this field must be equal to or after {0}",
55028     /**
55029      * @cfg {String} maxText
55030      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
55031      * <tt>'The date in this field must be before {maxValue}'</tt>).
55032      */
55033     maxText : "The date in this field must be equal to or before {0}",
55034     /**
55035      * @cfg {String} invalidText
55036      * The error text to display when the date in the field is invalid (defaults to
55037      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
55038      */
55039     invalidText : "{0} is not a valid date - it must be in the format {1}",
55040     /**
55041      * @cfg {String} triggerClass
55042      * An additional CSS class used to style the trigger button.  The trigger will always get the
55043      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
55044      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
55045      */
55046     triggerClass : 'x-form-date-trigger',
55047     /**
55048      * @cfg {Boolean} showToday
55049      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
55050      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
55051      */
55052     showToday : true,
55053     /**
55054      * @cfg {Date/String} minValue
55055      * The minimum allowed date. Can be either a Javascript date object or a string date in a
55056      * valid format (defaults to null).
55057      */
55058     /**
55059      * @cfg {Date/String} maxValue
55060      * The maximum allowed date. Can be either a Javascript date object or a string date in a
55061      * valid format (defaults to null).
55062      */
55063     /**
55064      * @cfg {Array} disabledDays
55065      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
55066 // disable Sunday and Saturday:
55067 disabledDays:  [0, 6]
55068 // disable weekdays:
55069 disabledDays: [1,2,3,4,5]
55070      * </code></pre>
55071      */
55072     /**
55073      * @cfg {Array} disabledDates
55074      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
55075      * expression so they are very powerful. Some examples:<pre><code>
55076 // disable these exact dates:
55077 disabledDates: ["03/08/2003", "09/16/2003"]
55078 // disable these days for every year:
55079 disabledDates: ["03/08", "09/16"]
55080 // only match the beginning (useful if you are using short years):
55081 disabledDates: ["^03/08"]
55082 // disable every day in March 2006:
55083 disabledDates: ["03/../2006"]
55084 // disable every day in every March:
55085 disabledDates: ["^03"]
55086      * </code></pre>
55087      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
55088      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
55089      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
55090      */
55091     /**
55092      * @cfg {String/Object} autoCreate
55093      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
55094      * specification object:<pre><code>
55095      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
55096      * </code></pre>
55097      */
55098
55099     // private
55100     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
55101
55102     initComponent : function(){
55103         Ext.form.DateField.superclass.initComponent.call(this);
55104
55105         this.addEvents(
55106             /**
55107              * @event select
55108              * Fires when a date is selected via the date picker.
55109              * @param {Ext.form.DateField} this
55110              * @param {Date} date The date that was selected
55111              */
55112             'select'
55113         );
55114
55115         if(Ext.isString(this.minValue)){
55116             this.minValue = this.parseDate(this.minValue);
55117         }
55118         if(Ext.isString(this.maxValue)){
55119             this.maxValue = this.parseDate(this.maxValue);
55120         }
55121         this.disabledDatesRE = null;
55122         this.initDisabledDays();
55123     },
55124
55125     // private
55126     initDisabledDays : function(){
55127         if(this.disabledDates){
55128             var dd = this.disabledDates,
55129                 len = dd.length - 1, 
55130                 re = "(?:";
55131                 
55132             Ext.each(dd, function(d, i){
55133                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
55134                 if(i != len){
55135                     re += '|';
55136                 }
55137             }, this);
55138             this.disabledDatesRE = new RegExp(re + ')');
55139         }
55140     },
55141
55142     /**
55143      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
55144      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
55145      * for details on supported values) used to disable a pattern of dates.
55146      */
55147     setDisabledDates : function(dd){
55148         this.disabledDates = dd;
55149         this.initDisabledDays();
55150         if(this.menu){
55151             this.menu.picker.setDisabledDates(this.disabledDatesRE);
55152         }
55153     },
55154
55155     /**
55156      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
55157      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
55158      * config for details on supported values.
55159      */
55160     setDisabledDays : function(dd){
55161         this.disabledDays = dd;
55162         if(this.menu){
55163             this.menu.picker.setDisabledDays(dd);
55164         }
55165     },
55166
55167     /**
55168      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
55169      * @param {Date} value The minimum date that can be selected
55170      */
55171     setMinValue : function(dt){
55172         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
55173         if(this.menu){
55174             this.menu.picker.setMinDate(this.minValue);
55175         }
55176     },
55177
55178     /**
55179      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
55180      * @param {Date} value The maximum date that can be selected
55181      */
55182     setMaxValue : function(dt){
55183         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
55184         if(this.menu){
55185             this.menu.picker.setMaxDate(this.maxValue);
55186         }
55187     },
55188
55189     // private
55190     validateValue : function(value){
55191         value = this.formatDate(value);
55192         if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
55193             return false;
55194         }
55195         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
55196              return true;
55197         }
55198         var svalue = value;
55199         value = this.parseDate(value);
55200         if(!value){
55201             this.markInvalid(String.format(this.invalidText, svalue, this.format));
55202             return false;
55203         }
55204         var time = value.getTime();
55205         if(this.minValue && time < this.minValue.getTime()){
55206             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
55207             return false;
55208         }
55209         if(this.maxValue && time > this.maxValue.getTime()){
55210             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
55211             return false;
55212         }
55213         if(this.disabledDays){
55214             var day = value.getDay();
55215             for(var i = 0; i < this.disabledDays.length; i++) {
55216                 if(day === this.disabledDays[i]){
55217                     this.markInvalid(this.disabledDaysText);
55218                     return false;
55219                 }
55220             }
55221         }
55222         var fvalue = this.formatDate(value);
55223         if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
55224             this.markInvalid(String.format(this.disabledDatesText, fvalue));
55225             return false;
55226         }
55227         return true;
55228     },
55229
55230     // private
55231     // Provides logic to override the default TriggerField.validateBlur which just returns true
55232     validateBlur : function(){
55233         return !this.menu || !this.menu.isVisible();
55234     },
55235
55236     /**
55237      * Returns the current date value of the date field.
55238      * @return {Date} The date value
55239      */
55240     getValue : function(){
55241         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
55242     },
55243
55244     /**
55245      * Sets the value of the date field.  You can pass a date object or any string that can be
55246      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
55247      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
55248      * <br />Usage:
55249      * <pre><code>
55250 //All of these calls set the same date value (May 4, 2006)
55251
55252 //Pass a date object:
55253 var dt = new Date('5/4/2006');
55254 dateField.setValue(dt);
55255
55256 //Pass a date string (default format):
55257 dateField.setValue('05/04/2006');
55258
55259 //Pass a date string (custom format):
55260 dateField.format = 'Y-m-d';
55261 dateField.setValue('2006-05-04');
55262 </code></pre>
55263      * @param {String/Date} date The date or valid date string
55264      * @return {Ext.form.Field} this
55265      */
55266     setValue : function(date){
55267         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
55268     },
55269
55270     // private
55271     parseDate : function(value){
55272         if(!value || Ext.isDate(value)){
55273             return value;
55274         }
55275         var v = Date.parseDate(value, this.format);
55276         if(!v && this.altFormats){
55277             if(!this.altFormatsArray){
55278                 this.altFormatsArray = this.altFormats.split("|");
55279             }
55280             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
55281                 v = Date.parseDate(value, this.altFormatsArray[i]);
55282             }
55283         }
55284         return v;
55285     },
55286
55287     // private
55288     onDestroy : function(){
55289                 Ext.destroy(this.menu);
55290         Ext.form.DateField.superclass.onDestroy.call(this);
55291     },
55292
55293     // private
55294     formatDate : function(date){
55295         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
55296     },
55297
55298     /**
55299      * @method onTriggerClick
55300      * @hide
55301      */
55302     // private
55303     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
55304     onTriggerClick : function(){
55305         if(this.disabled){
55306             return;
55307         }
55308         if(this.menu == null){
55309             this.menu = new Ext.menu.DateMenu({
55310                 hideOnClick: false
55311             });
55312         }
55313         this.onFocus();
55314         Ext.apply(this.menu.picker,  {
55315             minDate : this.minValue,
55316             maxDate : this.maxValue,
55317             disabledDatesRE : this.disabledDatesRE,
55318             disabledDatesText : this.disabledDatesText,
55319             disabledDays : this.disabledDays,
55320             disabledDaysText : this.disabledDaysText,
55321             format : this.format,
55322             showToday : this.showToday,
55323             minText : String.format(this.minText, this.formatDate(this.minValue)),
55324             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
55325         });
55326         this.menu.picker.setValue(this.getValue() || new Date());
55327         this.menu.show(this.el, "tl-bl?");
55328         this.menuEvents('on');
55329     },
55330     
55331     //private
55332     menuEvents: function(method){
55333         this.menu[method]('select', this.onSelect, this);
55334         this.menu[method]('hide', this.onMenuHide, this);
55335         this.menu[method]('show', this.onFocus, this);
55336     },
55337     
55338     onSelect: function(m, d){
55339         this.setValue(d);
55340         this.fireEvent('select', this, d);
55341         this.menu.hide();
55342     },
55343     
55344     onMenuHide: function(){
55345         this.focus(false, 60);
55346         this.menuEvents('un');
55347     },
55348
55349     // private
55350     beforeBlur : function(){
55351         var v = this.parseDate(this.getRawValue());
55352         if(v){
55353             this.setValue(v);
55354         }
55355     }
55356
55357     /**
55358      * @cfg {Boolean} grow @hide
55359      */
55360     /**
55361      * @cfg {Number} growMin @hide
55362      */
55363     /**
55364      * @cfg {Number} growMax @hide
55365      */
55366     /**
55367      * @hide
55368      * @method autoSize
55369      */
55370 });
55371 Ext.reg('datefield', Ext.form.DateField);/**\r
55372  * @class Ext.form.DisplayField\r
55373  * @extends Ext.form.Field\r
55374  * A display-only text field which is not validated and not submitted.\r
55375  * @constructor\r
55376  * Creates a new DisplayField.\r
55377  * @param {Object} config Configuration options\r
55378  * @xtype displayfield\r
55379  */\r
55380 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {\r
55381     validationEvent : false,\r
55382     validateOnBlur : false,\r
55383     defaultAutoCreate : {tag: "div"},\r
55384     /**\r
55385      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)\r
55386      */\r
55387     fieldClass : "x-form-display-field",\r
55388     /**\r
55389      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to\r
55390      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than\r
55391      * rendering them as string literals per the default logic.\r
55392      */\r
55393     htmlEncode: false,\r
55394 \r
55395     // private\r
55396     initEvents : Ext.emptyFn,\r
55397 \r
55398     isValid : function(){\r
55399         return true;\r
55400     },\r
55401 \r
55402     validate : function(){\r
55403         return true;\r
55404     },\r
55405 \r
55406     getRawValue : function(){\r
55407         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');\r
55408         if(v === this.emptyText){\r
55409             v = '';\r
55410         }\r
55411         if(this.htmlEncode){\r
55412             v = Ext.util.Format.htmlDecode(v);\r
55413         }\r
55414         return v;\r
55415     },\r
55416 \r
55417     getValue : function(){\r
55418         return this.getRawValue();\r
55419     },\r
55420     \r
55421     getName: function() {\r
55422         return this.name;\r
55423     },\r
55424 \r
55425     setRawValue : function(v){\r
55426         if(this.htmlEncode){\r
55427             v = Ext.util.Format.htmlEncode(v);\r
55428         }\r
55429         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);\r
55430     },\r
55431 \r
55432     setValue : function(v){\r
55433         this.setRawValue(v);\r
55434         return this;\r
55435     }\r
55436     /** \r
55437      * @cfg {String} inputType \r
55438      * @hide\r
55439      */\r
55440     /** \r
55441      * @cfg {Boolean} disabled \r
55442      * @hide\r
55443      */\r
55444     /** \r
55445      * @cfg {Boolean} readOnly \r
55446      * @hide\r
55447      */\r
55448     /** \r
55449      * @cfg {Boolean} validateOnBlur \r
55450      * @hide\r
55451      */\r
55452     /** \r
55453      * @cfg {Number} validationDelay \r
55454      * @hide\r
55455      */\r
55456     /** \r
55457      * @cfg {String/Boolean} validationEvent \r
55458      * @hide\r
55459      */\r
55460 });\r
55461 \r
55462 Ext.reg('displayfield', Ext.form.DisplayField);\r
55463 /**\r
55464  * @class Ext.form.ComboBox\r
55465  * @extends Ext.form.TriggerField\r
55466  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>\r
55467  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is\r
55468  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input\r
55469  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field\r
55470  * which is named according to the {@link #name}.</p>\r
55471  * <p><b><u>Events</u></b></p>\r
55472  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>\r
55473 var cb = new Ext.form.ComboBox({\r
55474     // all of your config options\r
55475     listeners:{\r
55476          scope: yourScope,\r
55477          'select': yourFunction\r
55478     }\r
55479 });\r
55480 \r
55481 // Alternatively, you can assign events after the object is created:\r
55482 var cb = new Ext.form.ComboBox(yourOptions);\r
55483 cb.on('select', yourFunction, yourScope);\r
55484  * </code></pre></p>\r
55485  *\r
55486  * <p><b><u>ComboBox in Grid</u></b></p>\r
55487  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}\r
55488  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement\r
55489  * a reusable render, for example:<pre><code>\r
55490 // create reusable renderer\r
55491 Ext.util.Format.comboRenderer = function(combo){\r
55492     return function(value){\r
55493         var record = combo.findRecord(combo.{@link #valueField}, value);\r
55494         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};\r
55495     }\r
55496 }\r
55497 \r
55498 // create the combo instance\r
55499 var combo = new Ext.form.ComboBox({\r
55500     {@link #typeAhead}: true,\r
55501     {@link #triggerAction}: 'all',\r
55502     {@link #lazyRender}:true,\r
55503     {@link #mode}: 'local',\r
55504     {@link #store}: new Ext.data.ArrayStore({\r
55505         id: 0,\r
55506         fields: [\r
55507             'myId',\r
55508             'displayText'\r
55509         ],\r
55510         data: [[1, 'item1'], [2, 'item2']]\r
55511     }),\r
55512     {@link #valueField}: 'myId',\r
55513     {@link #displayField}: 'displayText'\r
55514 });\r
55515 \r
55516 // snippet of column model used within grid\r
55517 var cm = new Ext.grid.ColumnModel([{\r
55518        ...\r
55519     },{\r
55520        header: "Some Header",\r
55521        dataIndex: 'whatever',\r
55522        width: 130,\r
55523        editor: combo, // specify reference to combo instance\r
55524        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer\r
55525     },\r
55526     ...\r
55527 ]);\r
55528  * </code></pre></p>\r
55529  *\r
55530  * <p><b><u>Filtering</u></b></p>\r
55531  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox\r
55532  * store manually see <tt>{@link #lastQuery}</tt>.</p>\r
55533  * @constructor\r
55534  * Create a new ComboBox.\r
55535  * @param {Object} config Configuration options\r
55536  * @xtype combo\r
55537  */\r
55538 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {\r
55539     /**\r
55540      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.\r
55541      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or\r
55542      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.\r
55543      */\r
55544     /**\r
55545      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested\r
55546      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),\r
55547      * defaults to <tt>false</tt>).\r
55548      */\r
55549     /**\r
55550      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default\r
55551      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.\r
55552      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>\r
55553      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>\r
55554      */\r
55555     /**\r
55556      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).\r
55557      * Acceptable values for this property are:\r
55558      * <div class="mdetail-params"><ul>\r
55559      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>\r
55560      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,\r
55561      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.\r
55562      * <div class="mdetail-params"><ul>\r
55563      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">\r
55564      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo\r
55565      * {@link #valueField} and {@link #displayField})</div></li>\r
55566      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">\r
55567      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo\r
55568      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.\r
55569      * </div></li></ul></div></li></ul></div>\r
55570      * <p>See also <tt>{@link #mode}</tt>.</p>\r
55571      */\r
55572     /**\r
55573      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of\r
55574      * the dropdown list (defaults to undefined, with no header element)\r
55575      */\r
55576 \r
55577     // private\r
55578     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},\r
55579     /**\r
55580      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown\r
55581      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}\r
55582      */\r
55583     /**\r
55584      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this\r
55585      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if\r
55586      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on\r
55587      * the store configuration}).\r
55588      * <p>See also <tt>{@link #valueField}</tt>.</p>\r
55589      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a\r
55590      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not\r
55591      * active.</p>\r
55592      */\r
55593     /**\r
55594      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this\r
55595      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if\r
55596      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on\r
55597      * the store configuration}).\r
55598      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be\r
55599      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>\r
55600      */\r
55601     /**\r
55602      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the\r
55603      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically\r
55604      * post during a form submission.  See also {@link #valueField}.\r
55605      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.\r
55606      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since\r
55607      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and\r
55608      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>\r
55609      */\r
55610     /**\r
55611      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided\r
55612      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>\r
55613      * and combo {@link Ext.Component#id id} should be different, since no two DOM\r
55614      * nodes should share the same id.\r
55615      */\r
55616     /**\r
55617      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is\r
55618      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured\r
55619      * <tt>{@link Ext.form.Field#value value}</tt>.\r
55620      */\r
55621     /**\r
55622      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class\r
55623      * applied the dropdown list element (defaults to '').\r
55624      */\r
55625     listClass : '',\r
55626     /**\r
55627      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list\r
55628      * (defaults to <tt>'x-combo-selected'</tt>)\r
55629      */\r
55630     selectedClass : 'x-combo-selected',\r
55631     /**\r
55632      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.\r
55633      * (defaults to '')\r
55634      */\r
55635     listEmptyText: '',\r
55636     /**\r
55637      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always\r
55638      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified\r
55639      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).\r
55640      */\r
55641     triggerClass : 'x-form-arrow-trigger',\r
55642     /**\r
55643      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for\r
55644      * 4-way shadow, and <tt>"drop"</tt> for bottom-right\r
55645      */\r
55646     shadow : 'sides',\r
55647     /**\r
55648      * @cfg {String} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details\r
55649      * on supported anchor positions (defaults to <tt>'tl-bl?'</tt>)\r
55650      */\r
55651     listAlign : 'tl-bl?',\r
55652     /**\r
55653      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown\r
55654      * (defaults to <tt>300</tt>)\r
55655      */\r
55656     maxHeight : 300,\r
55657     /**\r
55658      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its\r
55659      * distance to the viewport edges (defaults to <tt>90</tt>)\r
55660      */\r
55661     minHeight : 90,\r
55662     /**\r
55663      * @cfg {String} triggerAction The action to execute when the trigger is clicked.\r
55664      * <div class="mdetail-params"><ul>\r
55665      * <li><b><tt>'query'</tt></b> : <b>Default</b>\r
55666      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>\r
55667      * <li><b><tt>'all'</tt></b> :\r
55668      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>\r
55669      * </ul></div>\r
55670      * <p>See also <code>{@link #queryParam}</code>.</p>\r
55671      */\r
55672     triggerAction : 'query',\r
55673     /**\r
55674      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and\r
55675      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if\r
55676      * <tt>{@link #mode} = 'local'</tt>, does not apply if\r
55677      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).\r
55678      */\r
55679     minChars : 4,\r
55680     /**\r
55681      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being\r
55682      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults\r
55683      * to <tt>false</tt>)\r
55684      */\r
55685     typeAhead : false,\r
55686     /**\r
55687      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and\r
55688      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>\r
55689      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)\r
55690      */\r
55691     queryDelay : 500,\r
55692     /**\r
55693      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the\r
55694      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and\r
55695      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>\r
55696      * (defaults to <tt>0</tt>).\r
55697      */\r
55698     pageSize : 0,\r
55699     /**\r
55700      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.\r
55701      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to\r
55702      * <tt>false</tt>).\r
55703      */\r
55704     selectOnFocus : false,\r
55705     /**\r
55706      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)\r
55707      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)\r
55708      */\r
55709     queryParam : 'query',\r
55710     /**\r
55711      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies\r
55712      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)\r
55713      */\r
55714     loadingText : 'Loading...',\r
55715     /**\r
55716      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list\r
55717      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).\r
55718      * Defaults to <tt>false</tt>.\r
55719      */\r
55720     resizable : false,\r
55721     /**\r
55722      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if\r
55723      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)\r
55724      */\r
55725     handleHeight : 8,\r
55726     /**\r
55727      * @cfg {String} allQuery The text query to send to the server to return all records for the list\r
55728      * with no filtering (defaults to '')\r
55729      */\r
55730     allQuery: '',\r
55731     /**\r
55732      * @cfg {String} mode Acceptable values are:\r
55733      * <div class="mdetail-params"><ul>\r
55734      * <li><b><tt>'remote'</tt></b> : <b>Default</b>\r
55735      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger\r
55736      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is\r
55737      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store\r
55738      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>\r
55739      * <li><b><tt>'local'</tt></b> :\r
55740      * <p class="sub-desc">ComboBox loads local data</p>\r
55741      * <pre><code>\r
55742 var combo = new Ext.form.ComboBox({\r
55743     renderTo: document.body,\r
55744     mode: 'local',\r
55745     store: new Ext.data.ArrayStore({\r
55746         id: 0,\r
55747         fields: [\r
55748             'myId',  // numeric value is the key\r
55749             'displayText'\r
55750         ],\r
55751         data: [[1, 'item1'], [2, 'item2']]  // data is local\r
55752     }),\r
55753     valueField: 'myId',\r
55754     displayField: 'displayText',\r
55755     triggerAction: 'all'\r
55756 });\r
55757      * </code></pre></li>\r
55758      * </ul></div>\r
55759      */\r
55760     mode: 'remote',\r
55761     /**\r
55762      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will\r
55763      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)\r
55764      */\r
55765     minListWidth : 70,\r
55766     /**\r
55767      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,\r
55768      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)\r
55769      */\r
55770     forceSelection : false,\r
55771     /**\r
55772      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed\r
55773      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)\r
55774      */\r
55775     typeAheadDelay : 250,\r
55776     /**\r
55777      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in\r
55778      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this\r
55779      * default text is used, it means there is no value set and no validation will occur on this field.\r
55780      */\r
55781 \r
55782     /**\r
55783      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused\r
55784      * (defaults to <tt>true</tt>)\r
55785      */\r
55786     lazyInit : true,\r
55787 \r
55788     /**\r
55789      * The value of the match string used to filter the store. Delete this property to force a requery.\r
55790      * Example use:\r
55791      * <pre><code>\r
55792 var combo = new Ext.form.ComboBox({\r
55793     ...\r
55794     mode: 'remote',\r
55795     ...\r
55796     listeners: {\r
55797         // delete the previous query in the beforequery event or set\r
55798         // combo.lastQuery = null (this will reload the store the next time it expands)\r
55799         beforequery: function(qe){\r
55800             delete qe.combo.lastQuery;\r
55801         }\r
55802     }\r
55803 });\r
55804      * </code></pre>\r
55805      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used\r
55806      * configure the combo with <tt>lastQuery=''</tt>. Example use:\r
55807      * <pre><code>\r
55808 var combo = new Ext.form.ComboBox({\r
55809     ...\r
55810     mode: 'local',\r
55811     triggerAction: 'all',\r
55812     lastQuery: ''\r
55813 });\r
55814      * </code></pre>\r
55815      * @property lastQuery\r
55816      * @type String\r
55817      */\r
55818 \r
55819     // private\r
55820     initComponent : function(){\r
55821         Ext.form.ComboBox.superclass.initComponent.call(this);\r
55822         this.addEvents(\r
55823             /**\r
55824              * @event expand\r
55825              * Fires when the dropdown list is expanded\r
55826              * @param {Ext.form.ComboBox} combo This combo box\r
55827              */\r
55828             'expand',\r
55829             /**\r
55830              * @event collapse\r
55831              * Fires when the dropdown list is collapsed\r
55832              * @param {Ext.form.ComboBox} combo This combo box\r
55833              */\r
55834             'collapse',\r
55835             /**\r
55836              * @event beforeselect\r
55837              * Fires before a list item is selected. Return false to cancel the selection.\r
55838              * @param {Ext.form.ComboBox} combo This combo box\r
55839              * @param {Ext.data.Record} record The data record returned from the underlying store\r
55840              * @param {Number} index The index of the selected item in the dropdown list\r
55841              */\r
55842             'beforeselect',\r
55843             /**\r
55844              * @event select\r
55845              * Fires when a list item is selected\r
55846              * @param {Ext.form.ComboBox} combo This combo box\r
55847              * @param {Ext.data.Record} record The data record returned from the underlying store\r
55848              * @param {Number} index The index of the selected item in the dropdown list\r
55849              */\r
55850             'select',\r
55851             /**\r
55852              * @event beforequery\r
55853              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's\r
55854              * cancel property to true.\r
55855              * @param {Object} queryEvent An object that has these properties:<ul>\r
55856              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>\r
55857              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>\r
55858              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>\r
55859              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>\r
55860              * </ul>\r
55861              */\r
55862             'beforequery'\r
55863         );\r
55864         if(this.transform){\r
55865             var s = Ext.getDom(this.transform);\r
55866             if(!this.hiddenName){\r
55867                 this.hiddenName = s.name;\r
55868             }\r
55869             if(!this.store){\r
55870                 this.mode = 'local';\r
55871                 var d = [], opts = s.options;\r
55872                 for(var i = 0, len = opts.length;i < len; i++){\r
55873                     var o = opts[i],\r
55874                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;\r
55875                     if(o.selected && Ext.isEmpty(this.value, true)) {\r
55876                         this.value = value;\r
55877                     }\r
55878                     d.push([value, o.text]);\r
55879                 }\r
55880                 this.store = new Ext.data.ArrayStore({\r
55881                     'id': 0,\r
55882                     fields: ['value', 'text'],\r
55883                     data : d,\r
55884                     autoDestroy: true\r
55885                 });\r
55886                 this.valueField = 'value';\r
55887                 this.displayField = 'text';\r
55888             }\r
55889             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference\r
55890             if(!this.lazyRender){\r
55891                 this.target = true;\r
55892                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);\r
55893                 this.render(this.el.parentNode, s);\r
55894                 Ext.removeNode(s); // remove it\r
55895             }else{\r
55896                 Ext.removeNode(s); // remove it\r
55897             }\r
55898         }\r
55899         //auto-configure store from local array data\r
55900         else if(this.store){\r
55901             this.store = Ext.StoreMgr.lookup(this.store);\r
55902             if(this.store.autoCreated){\r
55903                 this.displayField = this.valueField = 'field1';\r
55904                 if(!this.store.expandData){\r
55905                     this.displayField = 'field2';\r
55906                 }\r
55907                 this.mode = 'local';\r
55908             }\r
55909         }\r
55910 \r
55911         this.selectedIndex = -1;\r
55912         if(this.mode == 'local'){\r
55913             if(!Ext.isDefined(this.initialConfig.queryDelay)){\r
55914                 this.queryDelay = 10;\r
55915             }\r
55916             if(!Ext.isDefined(this.initialConfig.minChars)){\r
55917                 this.minChars = 0;\r
55918             }\r
55919         }\r
55920     },\r
55921 \r
55922     // private\r
55923     onRender : function(ct, position){\r
55924         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);\r
55925         if(this.hiddenName){\r
55926             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,\r
55927                     id: (this.hiddenId||this.hiddenName)}, 'before', true);\r
55928 \r
55929             // prevent input submission\r
55930             this.el.dom.removeAttribute('name');\r
55931         }\r
55932         if(Ext.isGecko){\r
55933             this.el.dom.setAttribute('autocomplete', 'off');\r
55934         }\r
55935 \r
55936         if(!this.lazyInit){\r
55937             this.initList();\r
55938         }else{\r
55939             this.on('focus', this.initList, this, {single: true});\r
55940         }\r
55941     },\r
55942 \r
55943     // private\r
55944     initValue : function(){\r
55945         Ext.form.ComboBox.superclass.initValue.call(this);\r
55946         if(this.hiddenField){\r
55947             this.hiddenField.value =\r
55948                 Ext.isDefined(this.hiddenValue) ? this.hiddenValue :\r
55949                 Ext.isDefined(this.value) ? this.value : '';\r
55950         }\r
55951     },\r
55952 \r
55953     // private\r
55954     initList : function(){\r
55955         if(!this.list){\r
55956             var cls = 'x-combo-list';\r
55957 \r
55958             this.list = new Ext.Layer({\r
55959                 parentEl: this.getListParent(),\r
55960                 shadow: this.shadow,\r
55961                 cls: [cls, this.listClass].join(' '),\r
55962                 constrain:false\r
55963             });\r
55964 \r
55965             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);\r
55966             this.list.setSize(lw, 0);\r
55967             this.list.swallowEvent('mousewheel');\r
55968             this.assetHeight = 0;\r
55969             if(this.syncFont !== false){\r
55970                 this.list.setStyle('font-size', this.el.getStyle('font-size'));\r
55971             }\r
55972             if(this.title){\r
55973                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});\r
55974                 this.assetHeight += this.header.getHeight();\r
55975             }\r
55976 \r
55977             this.innerList = this.list.createChild({cls:cls+'-inner'});\r
55978             this.mon(this.innerList, 'mouseover', this.onViewOver, this);\r
55979             this.mon(this.innerList, 'mousemove', this.onViewMove, this);\r
55980             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));\r
55981 \r
55982             if(this.pageSize){\r
55983                 this.footer = this.list.createChild({cls:cls+'-ft'});\r
55984                 this.pageTb = new Ext.PagingToolbar({\r
55985                     store: this.store,\r
55986                     pageSize: this.pageSize,\r
55987                     renderTo:this.footer\r
55988                 });\r
55989                 this.assetHeight += this.footer.getHeight();\r
55990             }\r
55991 \r
55992             if(!this.tpl){\r
55993                 /**\r
55994                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to\r
55995                 * use to display each item in the dropdown list. The dropdown list is displayed in a\r
55996                 * DataView. See {@link #view}.</p>\r
55997                 * <p>The default template string is:</p><pre><code>\r
55998                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'\r
55999                 * </code></pre>\r
56000                 * <p>Override the default value to create custom UI layouts for items in the list.\r
56001                 * For example:</p><pre><code>\r
56002                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'\r
56003                 * </code></pre>\r
56004                 * <p>The template <b>must</b> contain one or more substitution parameters using field\r
56005                 * names from the Combo's</b> {@link #store Store}. In the example above an\r
56006                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>\r
56007                 * <p>To preserve the default visual look of list items, add the CSS class name\r
56008                 * <pre>x-combo-list-item</pre> to the template's container element.</p>\r
56009                 * <p>Also see {@link #itemSelector} for additional details.</p>\r
56010                 */\r
56011                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';\r
56012                 /**\r
56013                  * @cfg {String} itemSelector\r
56014                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be\r
56015                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown\r
56016                  * display will be working with.</p>\r
56017                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been\r
56018                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>\r
56019                  * to dropdown list items</b>\r
56020                  */\r
56021             }\r
56022 \r
56023             /**\r
56024             * The {@link Ext.DataView DataView} used to display the ComboBox's options.\r
56025             * @type Ext.DataView\r
56026             */\r
56027             this.view = new Ext.DataView({\r
56028                 applyTo: this.innerList,\r
56029                 tpl: this.tpl,\r
56030                 singleSelect: true,\r
56031                 selectedClass: this.selectedClass,\r
56032                 itemSelector: this.itemSelector || '.' + cls + '-item',\r
56033                 emptyText: this.listEmptyText\r
56034             });\r
56035 \r
56036             this.mon(this.view, 'click', this.onViewClick, this);\r
56037 \r
56038             this.bindStore(this.store, true);\r
56039 \r
56040             if(this.resizable){\r
56041                 this.resizer = new Ext.Resizable(this.list,  {\r
56042                    pinned:true, handles:'se'\r
56043                 });\r
56044                 this.mon(this.resizer, 'resize', function(r, w, h){\r
56045                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;\r
56046                     this.listWidth = w;\r
56047                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));\r
56048                     this.restrictHeight();\r
56049                 }, this);\r
56050 \r
56051                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');\r
56052             }\r
56053         }\r
56054     },\r
56055 \r
56056     /**\r
56057      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>\r
56058      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered\r
56059      * to a different Element. An example might be rendering the list inside a Menu so that clicking\r
56060      * the list does not hide the Menu:<pre><code>\r
56061 var store = new Ext.data.ArrayStore({\r
56062     autoDestroy: true,\r
56063     fields: ['initials', 'fullname'],\r
56064     data : [\r
56065         ['FF', 'Fred Flintstone'],\r
56066         ['BR', 'Barney Rubble']\r
56067     ]\r
56068 });\r
56069 \r
56070 var combo = new Ext.form.ComboBox({\r
56071     store: store,\r
56072     displayField: 'fullname',\r
56073     emptyText: 'Select a name...',\r
56074     forceSelection: true,\r
56075     getListParent: function() {\r
56076         return this.el.up('.x-menu');\r
56077     },\r
56078     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu\r
56079     mode: 'local',\r
56080     selectOnFocus: true,\r
56081     triggerAction: 'all',\r
56082     typeAhead: true,\r
56083     width: 135\r
56084 });\r
56085 \r
56086 var menu = new Ext.menu.Menu({\r
56087     id: 'mainMenu',\r
56088     items: [\r
56089         combo // A Field in a Menu\r
56090     ]\r
56091 });\r
56092 </code></pre>\r
56093      */\r
56094     getListParent : function() {\r
56095         return document.body;\r
56096     },\r
56097 \r
56098     /**\r
56099      * Returns the store associated with this combo.\r
56100      * @return {Ext.data.Store} The store\r
56101      */\r
56102     getStore : function(){\r
56103         return this.store;\r
56104     },\r
56105 \r
56106     // private\r
56107     bindStore : function(store, initial){\r
56108         if(this.store && !initial){\r
56109             if(this.store !== store && this.store.autoDestroy){\r
56110                 this.store.destroy();\r
56111             }else{\r
56112                 this.store.un('beforeload', this.onBeforeLoad, this);\r
56113                 this.store.un('load', this.onLoad, this);\r
56114                 this.store.un('exception', this.collapse, this);\r
56115             }\r
56116             if(!store){\r
56117                 this.store = null;\r
56118                 if(this.view){\r
56119                     this.view.bindStore(null);\r
56120                 }\r
56121                 if(this.pageTb){\r
56122                     this.pageTb.bindStore(null);\r
56123                 }\r
56124             }\r
56125         }\r
56126         if(store){\r
56127             if(!initial) {\r
56128                 this.lastQuery = null;\r
56129                 if(this.pageTb) {\r
56130                     this.pageTb.bindStore(store);\r
56131                 }\r
56132             }\r
56133 \r
56134             this.store = Ext.StoreMgr.lookup(store);\r
56135             this.store.on({\r
56136                 scope: this,\r
56137                 beforeload: this.onBeforeLoad,\r
56138                 load: this.onLoad,\r
56139                 exception: this.collapse\r
56140             });\r
56141 \r
56142             if(this.view){\r
56143                 this.view.bindStore(store);\r
56144             }\r
56145         }\r
56146     },\r
56147 \r
56148     // private\r
56149     initEvents : function(){\r
56150         Ext.form.ComboBox.superclass.initEvents.call(this);\r
56151 \r
56152         this.keyNav = new Ext.KeyNav(this.el, {\r
56153             "up" : function(e){\r
56154                 this.inKeyMode = true;\r
56155                 this.selectPrev();\r
56156             },\r
56157 \r
56158             "down" : function(e){\r
56159                 if(!this.isExpanded()){\r
56160                     this.onTriggerClick();\r
56161                 }else{\r
56162                     this.inKeyMode = true;\r
56163                     this.selectNext();\r
56164                 }\r
56165             },\r
56166 \r
56167             "enter" : function(e){\r
56168                 this.onViewClick();\r
56169             },\r
56170 \r
56171             "esc" : function(e){\r
56172                 this.collapse();\r
56173             },\r
56174 \r
56175             "tab" : function(e){\r
56176                 this.onViewClick(false);\r
56177                 return true;\r
56178             },\r
56179 \r
56180             scope : this,\r
56181 \r
56182             doRelay : function(e, h, hname){\r
56183                 if(hname == 'down' || this.scope.isExpanded()){\r
56184                     // this MUST be called before ComboBox#fireKey()\r
56185                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);\r
56186                     if(!Ext.isIE && Ext.EventManager.useKeydown){\r
56187                         // call Combo#fireKey() for browsers which use keydown event (except IE)\r
56188                         this.scope.fireKey(e);\r
56189                     }\r
56190                     return relay;\r
56191                 }\r
56192                 return true;\r
56193             },\r
56194 \r
56195             forceKeyDown : true,\r
56196             defaultEventAction: 'stopEvent'\r
56197         });\r
56198         this.queryDelay = Math.max(this.queryDelay || 10,\r
56199                 this.mode == 'local' ? 10 : 250);\r
56200         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);\r
56201         if(this.typeAhead){\r
56202             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);\r
56203         }\r
56204         if(this.editable !== false && !this.enableKeyEvents){\r
56205             this.mon(this.el, 'keyup', this.onKeyUp, this);\r
56206         }\r
56207     },\r
56208 \r
56209     // private\r
56210     onDestroy : function(){\r
56211         if (this.dqTask){\r
56212             this.dqTask.cancel();\r
56213             this.dqTask = null;\r
56214         }\r
56215         this.bindStore(null);\r
56216         Ext.destroy(\r
56217             this.resizer,\r
56218             this.view,\r
56219             this.pageTb,\r
56220             this.list\r
56221         );\r
56222         Ext.form.ComboBox.superclass.onDestroy.call(this);\r
56223     },\r
56224 \r
56225     // private\r
56226     fireKey : function(e){\r
56227         if (!this.isExpanded()) {\r
56228             Ext.form.ComboBox.superclass.fireKey.call(this, e);\r
56229         }\r
56230     },\r
56231 \r
56232     // private\r
56233     onResize : function(w, h){\r
56234         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);\r
56235         if(this.isVisible() && this.list){\r
56236             this.doResize(w);\r
56237         }else{\r
56238             this.bufferSize = w;\r
56239         }\r
56240     },\r
56241     \r
56242     doResize: function(w){\r
56243         if(!Ext.isDefined(this.listWidth)){\r
56244             var lw = Math.max(w, this.minListWidth);\r
56245             this.list.setWidth(lw);\r
56246             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));\r
56247         }    \r
56248     },\r
56249 \r
56250     // private\r
56251     onEnable : function(){\r
56252         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);\r
56253         if(this.hiddenField){\r
56254             this.hiddenField.disabled = false;\r
56255         }\r
56256     },\r
56257 \r
56258     // private\r
56259     onDisable : function(){\r
56260         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);\r
56261         if(this.hiddenField){\r
56262             this.hiddenField.disabled = true;\r
56263         }\r
56264     },\r
56265 \r
56266     // private\r
56267     onBeforeLoad : function(){\r
56268         if(!this.hasFocus){\r
56269             return;\r
56270         }\r
56271         this.innerList.update(this.loadingText ?\r
56272                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');\r
56273         this.restrictHeight();\r
56274         this.selectedIndex = -1;\r
56275     },\r
56276 \r
56277     // private\r
56278     onLoad : function(){\r
56279         if(!this.hasFocus){\r
56280             return;\r
56281         }\r
56282         if(this.store.getCount() > 0 || this.listEmptyText){\r
56283             this.expand();\r
56284             this.restrictHeight();\r
56285             if(this.lastQuery == this.allQuery){\r
56286                 if(this.editable){\r
56287                     this.el.dom.select();\r
56288                 }\r
56289                 if(!this.selectByValue(this.value, true)){\r
56290                     this.select(0, true);\r
56291                 }\r
56292             }else{\r
56293                 this.selectNext();\r
56294                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){\r
56295                     this.taTask.delay(this.typeAheadDelay);\r
56296                 }\r
56297             }\r
56298         }else{\r
56299             this.onEmptyResults();\r
56300         }\r
56301         //this.el.focus();\r
56302     },\r
56303 \r
56304     // private\r
56305     onTypeAhead : function(){\r
56306         if(this.store.getCount() > 0){\r
56307             var r = this.store.getAt(0);\r
56308             var newValue = r.data[this.displayField];\r
56309             var len = newValue.length;\r
56310             var selStart = this.getRawValue().length;\r
56311             if(selStart != len){\r
56312                 this.setRawValue(newValue);\r
56313                 this.selectText(selStart, newValue.length);\r
56314             }\r
56315         }\r
56316     },\r
56317 \r
56318     // private\r
56319     onSelect : function(record, index){\r
56320         if(this.fireEvent('beforeselect', this, record, index) !== false){\r
56321             this.setValue(record.data[this.valueField || this.displayField]);\r
56322             this.collapse();\r
56323             this.fireEvent('select', this, record, index);\r
56324         }\r
56325     },\r
56326 \r
56327     // inherit docs\r
56328     getName: function(){\r
56329         var hf = this.hiddenField;\r
56330         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);\r
56331     },\r
56332 \r
56333     /**\r
56334      * Returns the currently selected field value or empty string if no value is set.\r
56335      * @return {String} value The selected value\r
56336      */\r
56337     getValue : function(){\r
56338         if(this.valueField){\r
56339             return Ext.isDefined(this.value) ? this.value : '';\r
56340         }else{\r
56341             return Ext.form.ComboBox.superclass.getValue.call(this);\r
56342         }\r
56343     },\r
56344 \r
56345     /**\r
56346      * Clears any text/value currently set in the field\r
56347      */\r
56348     clearValue : function(){\r
56349         if(this.hiddenField){\r
56350             this.hiddenField.value = '';\r
56351         }\r
56352         this.setRawValue('');\r
56353         this.lastSelectionText = '';\r
56354         this.applyEmptyText();\r
56355         this.value = '';\r
56356     },\r
56357 \r
56358     /**\r
56359      * Sets the specified value into the field.  If the value finds a match, the corresponding record text\r
56360      * will be displayed in the field.  If the value does not match the data value of an existing item,\r
56361      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.\r
56362      * Otherwise the field will be blank (although the value will still be set).\r
56363      * @param {String} value The value to match\r
56364      * @return {Ext.form.Field} this\r
56365      */\r
56366     setValue : function(v){\r
56367         var text = v;\r
56368         if(this.valueField){\r
56369             var r = this.findRecord(this.valueField, v);\r
56370             if(r){\r
56371                 text = r.data[this.displayField];\r
56372             }else if(Ext.isDefined(this.valueNotFoundText)){\r
56373                 text = this.valueNotFoundText;\r
56374             }\r
56375         }\r
56376         this.lastSelectionText = text;\r
56377         if(this.hiddenField){\r
56378             this.hiddenField.value = v;\r
56379         }\r
56380         Ext.form.ComboBox.superclass.setValue.call(this, text);\r
56381         this.value = v;\r
56382         return this;\r
56383     },\r
56384 \r
56385     // private\r
56386     findRecord : function(prop, value){\r
56387         var record;\r
56388         if(this.store.getCount() > 0){\r
56389             this.store.each(function(r){\r
56390                 if(r.data[prop] == value){\r
56391                     record = r;\r
56392                     return false;\r
56393                 }\r
56394             });\r
56395         }\r
56396         return record;\r
56397     },\r
56398 \r
56399     // private\r
56400     onViewMove : function(e, t){\r
56401         this.inKeyMode = false;\r
56402     },\r
56403 \r
56404     // private\r
56405     onViewOver : function(e, t){\r
56406         if(this.inKeyMode){ // prevent key nav and mouse over conflicts\r
56407             return;\r
56408         }\r
56409         var item = this.view.findItemFromChild(t);\r
56410         if(item){\r
56411             var index = this.view.indexOf(item);\r
56412             this.select(index, false);\r
56413         }\r
56414     },\r
56415 \r
56416     // private\r
56417     onViewClick : function(doFocus){\r
56418         var index = this.view.getSelectedIndexes()[0],\r
56419             s = this.store,\r
56420             r = s.getAt(index);\r
56421         if(r){\r
56422             this.onSelect(r, index);\r
56423         }else if(s.getCount() === 0){\r
56424             this.onEmptyResults();\r
56425         }\r
56426         if(doFocus !== false){\r
56427             this.el.focus();\r
56428         }\r
56429     },\r
56430 \r
56431     // private\r
56432     restrictHeight : function(){\r
56433         this.innerList.dom.style.height = '';\r
56434         var inner = this.innerList.dom,\r
56435             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,\r
56436             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),\r
56437             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,\r
56438             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,\r
56439             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;\r
56440             \r
56441         h = Math.min(h, space, this.maxHeight);\r
56442 \r
56443         this.innerList.setHeight(h);\r
56444         this.list.beginUpdate();\r
56445         this.list.setHeight(h+pad);\r
56446         this.list.alignTo(this.wrap, this.listAlign);\r
56447         this.list.endUpdate();\r
56448     },\r
56449 \r
56450     // private\r
56451     onEmptyResults : function(){\r
56452         this.collapse();\r
56453     },\r
56454 \r
56455     /**\r
56456      * Returns true if the dropdown list is expanded, else false.\r
56457      */\r
56458     isExpanded : function(){\r
56459         return this.list && this.list.isVisible();\r
56460     },\r
56461 \r
56462     /**\r
56463      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.\r
56464      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.\r
56465      * @param {String} value The data value of the item to select\r
56466      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the\r
56467      * selected item if it is not currently in view (defaults to true)\r
56468      * @return {Boolean} True if the value matched an item in the list, else false\r
56469      */\r
56470     selectByValue : function(v, scrollIntoView){\r
56471         if(!Ext.isEmpty(v, true)){\r
56472             var r = this.findRecord(this.valueField || this.displayField, v);\r
56473             if(r){\r
56474                 this.select(this.store.indexOf(r), scrollIntoView);\r
56475                 return true;\r
56476             }\r
56477         }\r
56478         return false;\r
56479     },\r
56480 \r
56481     /**\r
56482      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.\r
56483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.\r
56484      * @param {Number} index The zero-based index of the list item to select\r
56485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the\r
56486      * selected item if it is not currently in view (defaults to true)\r
56487      */\r
56488     select : function(index, scrollIntoView){\r
56489         this.selectedIndex = index;\r
56490         this.view.select(index);\r
56491         if(scrollIntoView !== false){\r
56492             var el = this.view.getNode(index);\r
56493             if(el){\r
56494                 this.innerList.scrollChildIntoView(el, false);\r
56495             }\r
56496         }\r
56497     },\r
56498 \r
56499     // private\r
56500     selectNext : function(){\r
56501         var ct = this.store.getCount();\r
56502         if(ct > 0){\r
56503             if(this.selectedIndex == -1){\r
56504                 this.select(0);\r
56505             }else if(this.selectedIndex < ct-1){\r
56506                 this.select(this.selectedIndex+1);\r
56507             }\r
56508         }\r
56509     },\r
56510 \r
56511     // private\r
56512     selectPrev : function(){\r
56513         var ct = this.store.getCount();\r
56514         if(ct > 0){\r
56515             if(this.selectedIndex == -1){\r
56516                 this.select(0);\r
56517             }else if(this.selectedIndex !== 0){\r
56518                 this.select(this.selectedIndex-1);\r
56519             }\r
56520         }\r
56521     },\r
56522 \r
56523     // private\r
56524     onKeyUp : function(e){\r
56525         var k = e.getKey();\r
56526         if(this.editable !== false && (k == e.BACKSPACE || !e.isSpecialKey())){\r
56527             this.lastKey = k;\r
56528             this.dqTask.delay(this.queryDelay);\r
56529         }\r
56530         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);\r
56531     },\r
56532 \r
56533     // private\r
56534     validateBlur : function(){\r
56535         return !this.list || !this.list.isVisible();\r
56536     },\r
56537 \r
56538     // private\r
56539     initQuery : function(){\r
56540         this.doQuery(this.getRawValue());\r
56541     },\r
56542 \r
56543     // private\r
56544     beforeBlur : function(){\r
56545         var val = this.getRawValue(),\r
56546             rec = this.findRecord(this.displayField, val);\r
56547         if(!rec && this.forceSelection){\r
56548             if(val.length > 0 && val != this.emptyText){\r
56549                 this.el.dom.value = Ext.isDefined(this.lastSelectionText) ? this.lastSelectionText : '';\r
56550                 this.applyEmptyText();\r
56551             }else{\r
56552                 this.clearValue();\r
56553             }\r
56554         }else{\r
56555             if(rec){\r
56556                 val = rec.get(this.valueField || this.displayField);\r
56557             }\r
56558             this.setValue(val);\r
56559         }\r
56560     },\r
56561 \r
56562     /**\r
56563      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the\r
56564      * query allowing the query action to be canceled if needed.\r
56565      * @param {String} query The SQL query to execute\r
56566      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer\r
56567      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It\r
56568      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)\r
56569      */\r
56570     doQuery : function(q, forceAll){\r
56571         q = Ext.isEmpty(q) ? '' : q;\r
56572         var qe = {\r
56573             query: q,\r
56574             forceAll: forceAll,\r
56575             combo: this,\r
56576             cancel:false\r
56577         };\r
56578         if(this.fireEvent('beforequery', qe)===false || qe.cancel){\r
56579             return false;\r
56580         }\r
56581         q = qe.query;\r
56582         forceAll = qe.forceAll;\r
56583         if(forceAll === true || (q.length >= this.minChars)){\r
56584             if(this.lastQuery !== q){\r
56585                 this.lastQuery = q;\r
56586                 if(this.mode == 'local'){\r
56587                     this.selectedIndex = -1;\r
56588                     if(forceAll){\r
56589                         this.store.clearFilter();\r
56590                     }else{\r
56591                         this.store.filter(this.displayField, q);\r
56592                     }\r
56593                     this.onLoad();\r
56594                 }else{\r
56595                     this.store.baseParams[this.queryParam] = q;\r
56596                     this.store.load({\r
56597                         params: this.getParams(q)\r
56598                     });\r
56599                     this.expand();\r
56600                 }\r
56601             }else{\r
56602                 this.selectedIndex = -1;\r
56603                 this.onLoad();\r
56604             }\r
56605         }\r
56606     },\r
56607 \r
56608     // private\r
56609     getParams : function(q){\r
56610         var p = {};\r
56611         //p[this.queryParam] = q;\r
56612         if(this.pageSize){\r
56613             p.start = 0;\r
56614             p.limit = this.pageSize;\r
56615         }\r
56616         return p;\r
56617     },\r
56618 \r
56619     /**\r
56620      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.\r
56621      */\r
56622     collapse : function(){\r
56623         if(!this.isExpanded()){\r
56624             return;\r
56625         }\r
56626         this.list.hide();\r
56627         Ext.getDoc().un('mousewheel', this.collapseIf, this);\r
56628         Ext.getDoc().un('mousedown', this.collapseIf, this);\r
56629         this.fireEvent('collapse', this);\r
56630     },\r
56631 \r
56632     // private\r
56633     collapseIf : function(e){\r
56634         if(!e.within(this.wrap) && !e.within(this.list)){\r
56635             this.collapse();\r
56636         }\r
56637     },\r
56638 \r
56639     /**\r
56640      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.\r
56641      */\r
56642     expand : function(){\r
56643         if(this.isExpanded() || !this.hasFocus){\r
56644             return;\r
56645         }\r
56646         if(this.bufferSize){\r
56647             this.doResize(this.bufferSize);\r
56648             delete this.bufferSize;\r
56649         }\r
56650         this.list.alignTo(this.wrap, this.listAlign);\r
56651         this.list.show();\r
56652         if(Ext.isGecko2){\r
56653             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac\r
56654         }\r
56655         Ext.getDoc().on({\r
56656             scope: this,\r
56657             mousewheel: this.collapseIf,\r
56658             mousedown: this.collapseIf\r
56659         });\r
56660         this.fireEvent('expand', this);\r
56661     },\r
56662 \r
56663     /**\r
56664      * @method onTriggerClick\r
56665      * @hide\r
56666      */\r
56667     // private\r
56668     // Implements the default empty TriggerField.onTriggerClick function\r
56669     onTriggerClick : function(){\r
56670         if(this.disabled){\r
56671             return;\r
56672         }\r
56673         if(this.isExpanded()){\r
56674             this.collapse();\r
56675             this.el.focus();\r
56676         }else {\r
56677             this.onFocus({});\r
56678             if(this.triggerAction == 'all') {\r
56679                 this.doQuery(this.allQuery, true);\r
56680             } else {\r
56681                 this.doQuery(this.getRawValue());\r
56682             }\r
56683             this.el.focus();\r
56684         }\r
56685     }\r
56686 \r
56687     /**\r
56688      * @hide\r
56689      * @method autoSize\r
56690      */\r
56691     /**\r
56692      * @cfg {Boolean} grow @hide\r
56693      */\r
56694     /**\r
56695      * @cfg {Number} growMin @hide\r
56696      */\r
56697     /**\r
56698      * @cfg {Number} growMax @hide\r
56699      */\r
56700 \r
56701 });\r
56702 Ext.reg('combo', Ext.form.ComboBox);/**
56703  * @class Ext.form.Checkbox
56704  * @extends Ext.form.Field
56705  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
56706  * @constructor
56707  * Creates a new Checkbox
56708  * @param {Object} config Configuration options
56709  * @xtype checkbox
56710  */
56711 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
56712     /**
56713      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56714      */
56715     focusClass : undefined,
56716     /**
56717      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
56718      */
56719     fieldClass : 'x-form-field',
56720     /**
56721      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
56722      */
56723     checked : false,
56724     /**
56725      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56726      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
56727      */
56728     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
56729     /**
56730      * @cfg {String} boxLabel The text that appears beside the checkbox
56731      */
56732     /**
56733      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
56734      */
56735     /**
56736      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of 
56737      * handling the check event). The handler is passed the following parameters:
56738      * <div class="mdetail-params"><ul>
56739      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
56740      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
56741      * </ul></div>
56742      */
56743     /**
56744      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
56745      * (defaults to this Checkbox).
56746      */
56747
56748     // private
56749     actionMode : 'wrap',
56750     
56751         // private
56752     initComponent : function(){
56753         Ext.form.Checkbox.superclass.initComponent.call(this);
56754         this.addEvents(
56755             /**
56756              * @event check
56757              * Fires when the checkbox is checked or unchecked.
56758              * @param {Ext.form.Checkbox} this This checkbox
56759              * @param {Boolean} checked The new checked value
56760              */
56761             'check'
56762         );
56763     },
56764
56765     // private
56766     onResize : function(){
56767         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
56768         if(!this.boxLabel && !this.fieldLabel){
56769             this.el.alignTo(this.wrap, 'c-c');
56770         }
56771     },
56772
56773     // private
56774     initEvents : function(){
56775         Ext.form.Checkbox.superclass.initEvents.call(this);
56776         this.mon(this.el, {
56777             scope: this,
56778             click: this.onClick,
56779             change: this.onClick
56780         });
56781     },
56782
56783     /**
56784      * @hide
56785      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
56786      * @method
56787      */
56788     markInvalid : Ext.emptyFn,
56789     /**
56790      * @hide
56791      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
56792      * @method
56793      */
56794     clearInvalid : Ext.emptyFn,
56795
56796     // private
56797     onRender : function(ct, position){
56798         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
56799         if(this.inputValue !== undefined){
56800             this.el.dom.value = this.inputValue;
56801         }
56802         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
56803         if(this.boxLabel){
56804             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
56805         }
56806         if(this.checked){
56807             this.setValue(true);
56808         }else{
56809             this.checked = this.el.dom.checked;
56810         }
56811         // Need to repaint for IE, otherwise positioning is broken
56812         if(Ext.isIE){
56813             this.wrap.repaint();
56814         }
56815         this.resizeEl = this.positionEl = this.wrap;
56816     },
56817
56818     // private
56819     onDestroy : function(){
56820         Ext.destroy(this.wrap);
56821         Ext.form.Checkbox.superclass.onDestroy.call(this);
56822     },
56823
56824     // private
56825     initValue : function() {
56826         this.originalValue = this.getValue();
56827     },
56828
56829     /**
56830      * Returns the checked state of the checkbox.
56831      * @return {Boolean} True if checked, else false
56832      */
56833     getValue : function(){
56834         if(this.rendered){
56835             return this.el.dom.checked;
56836         }
56837         return this.checked;
56838     },
56839
56840         // private
56841     onClick : function(){
56842         if(this.el.dom.checked != this.checked){
56843             this.setValue(this.el.dom.checked);
56844         }
56845     },
56846
56847     /**
56848      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
56849      * <code>{@link #handler}</code> (if configured).
56850      * @param {Boolean/String} checked The following values will check the checkbox:
56851      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
56852      * @return {Ext.form.Field} this
56853      */
56854     setValue : function(v){
56855         var checked = this.checked ;
56856         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
56857         if(this.rendered){
56858             this.el.dom.checked = this.checked;
56859             this.el.dom.defaultChecked = this.checked;
56860         }
56861         if(checked != this.checked){
56862             this.fireEvent('check', this, this.checked);
56863             if(this.handler){
56864                 this.handler.call(this.scope || this, this, this.checked);
56865             }
56866         }
56867         return this;
56868     }
56869 });
56870 Ext.reg('checkbox', Ext.form.Checkbox);
56871 /**\r
56872  * @class Ext.form.CheckboxGroup\r
56873  * @extends Ext.form.Field\r
56874  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>\r
56875  * <p>Sample usage:</p>\r
56876  * <pre><code>\r
56877 var myCheckboxGroup = new Ext.form.CheckboxGroup({\r
56878     id:'myGroup',\r
56879     xtype: 'checkboxgroup',\r
56880     fieldLabel: 'Single Column',\r
56881     itemCls: 'x-check-group-alt',\r
56882     // Put all controls in a single column with width 100%\r
56883     columns: 1,\r
56884     items: [\r
56885         {boxLabel: 'Item 1', name: 'cb-col-1'},\r
56886         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},\r
56887         {boxLabel: 'Item 3', name: 'cb-col-3'}\r
56888     ]\r
56889 });\r
56890  * </code></pre>\r
56891  * @constructor\r
56892  * Creates a new CheckboxGroup\r
56893  * @param {Object} config Configuration options\r
56894  * @xtype checkboxgroup\r
56895  */\r
56896 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {\r
56897     /**\r
56898      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects\r
56899      * to arrange in the group.\r
56900      */\r
56901     /**\r
56902      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped\r
56903      * checkbox/radio controls using automatic layout.  This config can take several types of values:\r
56904      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width\r
56905      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>\r
56906      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be \r
56907      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>\r
56908      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer\r
56909      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will\r
56910      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float\r
56911      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field\r
56912      * container you should do so.</p></li></ul>\r
56913      */\r
56914     columns : 'auto',\r
56915     /**\r
56916      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column \r
56917      * top to bottom before starting on the next column.  The number of controls in each column will be automatically\r
56918      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added\r
56919      * to columns one at a time, completely filling each row left to right before starting on the next row.\r
56920      */\r
56921     vertical : false,\r
56922     /**\r
56923      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).\r
56924      * If no items are selected at validation time, {@link @blankText} will be used as the error text.\r
56925      */\r
56926     allowBlank : true,\r
56927     /**\r
56928      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must \r
56929      * select at least one item in this group")\r
56930      */\r
56931     blankText : "You must select at least one item in this group",\r
56932     \r
56933     // private\r
56934     defaultType : 'checkbox',\r
56935     \r
56936     // private\r
56937     groupCls : 'x-form-check-group',\r
56938     \r
56939     // private\r
56940     initComponent: function(){\r
56941         this.addEvents(\r
56942             /**\r
56943              * @event change\r
56944              * Fires when the state of a child checkbox changes.\r
56945              * @param {Ext.form.CheckboxGroup} this\r
56946              * @param {Array} checked An array containing the checked boxes.\r
56947              */\r
56948             'change'\r
56949         );   \r
56950         Ext.form.CheckboxGroup.superclass.initComponent.call(this);\r
56951     },\r
56952     \r
56953     // private\r
56954     onRender : function(ct, position){\r
56955         if(!this.el){\r
56956             var panelCfg = {\r
56957                 id: this.id,\r
56958                 cls: this.groupCls,\r
56959                 layout: 'column',\r
56960                 border: false,\r
56961                 renderTo: ct,\r
56962                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.\r
56963             };\r
56964             var colCfg = {\r
56965                 defaultType: this.defaultType,\r
56966                 layout: 'form',\r
56967                 border: false,\r
56968                 defaults: {\r
56969                     hideLabel: true,\r
56970                     anchor: '100%'\r
56971                 }\r
56972             };\r
56973             \r
56974             if(this.items[0].items){\r
56975                 \r
56976                 // The container has standard ColumnLayout configs, so pass them in directly\r
56977                 \r
56978                 Ext.apply(panelCfg, {\r
56979                     layoutConfig: {columns: this.items.length},\r
56980                     defaults: this.defaults,\r
56981                     items: this.items\r
56982                 });\r
56983                 for(var i=0, len=this.items.length; i<len; i++){\r
56984                     Ext.applyIf(this.items[i], colCfg);\r
56985                 }\r
56986                 \r
56987             }else{\r
56988                 \r
56989                 // The container has field item configs, so we have to generate the column\r
56990                 // panels first then move the items into the columns as needed.\r
56991                 \r
56992                 var numCols, cols = [];\r
56993                 \r
56994                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item\r
56995                     this.columns = this.items.length;\r
56996                 }\r
56997                 if(!Ext.isArray(this.columns)){\r
56998                     var cs = [];\r
56999                     for(var i=0; i<this.columns; i++){\r
57000                         cs.push((100/this.columns)*.01); // distribute by even %\r
57001                     }\r
57002                     this.columns = cs;\r
57003                 }\r
57004                 \r
57005                 numCols = this.columns.length;\r
57006                 \r
57007                 // Generate the column configs with the correct width setting\r
57008                 for(var i=0; i<numCols; i++){\r
57009                     var cc = Ext.apply({items:[]}, colCfg);\r
57010                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];\r
57011                     if(this.defaults){\r
57012                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)\r
57013                     }\r
57014                     cols.push(cc);\r
57015                 };\r
57016                 \r
57017                 // Distribute the original items into the columns\r
57018                 if(this.vertical){\r
57019                     var rows = Math.ceil(this.items.length / numCols), ri = 0;\r
57020                     for(var i=0, len=this.items.length; i<len; i++){\r
57021                         if(i>0 && i%rows==0){\r
57022                             ri++;\r
57023                         }\r
57024                         if(this.items[i].fieldLabel){\r
57025                             this.items[i].hideLabel = false;\r
57026                         }\r
57027                         cols[ri].items.push(this.items[i]);\r
57028                     };\r
57029                 }else{\r
57030                     for(var i=0, len=this.items.length; i<len; i++){\r
57031                         var ci = i % numCols;\r
57032                         if(this.items[i].fieldLabel){\r
57033                             this.items[i].hideLabel = false;\r
57034                         }\r
57035                         cols[ci].items.push(this.items[i]);\r
57036                     };\r
57037                 }\r
57038                 \r
57039                 Ext.apply(panelCfg, {\r
57040                     layoutConfig: {columns: numCols},\r
57041                     items: cols\r
57042                 });\r
57043             }\r
57044             \r
57045             this.panel = new Ext.Panel(panelCfg);\r
57046             this.panel.ownerCt = this;\r
57047             this.el = this.panel.getEl();\r
57048             \r
57049             if(this.forId && this.itemCls){\r
57050                 var l = this.el.up(this.itemCls).child('label', true);\r
57051                 if(l){\r
57052                     l.setAttribute('htmlFor', this.forId);\r
57053                 }\r
57054             }\r
57055             \r
57056             var fields = this.panel.findBy(function(c){\r
57057                 return c.isFormField;\r
57058             }, this);\r
57059             \r
57060             this.items = new Ext.util.MixedCollection();\r
57061             this.items.addAll(fields);\r
57062         }\r
57063         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);\r
57064     },\r
57065     \r
57066     initValue : function(){\r
57067         if(this.value){\r
57068             this.setValue.apply(this, this.buffered ? this.value : [this.value]);\r
57069             delete this.buffered;\r
57070             delete this.value;\r
57071         }\r
57072     },\r
57073     \r
57074     afterRender : function(){\r
57075         Ext.form.CheckboxGroup.superclass.afterRender.call(this);\r
57076         this.eachItem(function(item){\r
57077             item.on('check', this.fireChecked, this);\r
57078             item.inGroup = true;\r
57079         });\r
57080     },\r
57081     \r
57082     // private\r
57083     doLayout: function(){\r
57084         //ugly method required to layout hidden items\r
57085         if(this.rendered){\r
57086             this.panel.forceLayout = this.ownerCt.forceLayout;\r
57087             this.panel.doLayout();\r
57088         }\r
57089     },\r
57090     \r
57091     // private\r
57092     fireChecked: function(){\r
57093         var arr = [];\r
57094         this.eachItem(function(item){\r
57095             if(item.checked){\r
57096                 arr.push(item);\r
57097             }\r
57098         });\r
57099         this.fireEvent('change', this, arr);\r
57100     },\r
57101     \r
57102     // private\r
57103     validateValue : function(value){\r
57104         if(!this.allowBlank){\r
57105             var blank = true;\r
57106             this.eachItem(function(f){\r
57107                 if(f.checked){\r
57108                     return (blank = false);\r
57109                 }\r
57110             });\r
57111             if(blank){\r
57112                 this.markInvalid(this.blankText);\r
57113                 return false;\r
57114             }\r
57115         }\r
57116         return true;\r
57117     },\r
57118     \r
57119     // private\r
57120     isDirty: function(){\r
57121         //override the behaviour to check sub items.\r
57122         if (this.disabled || !this.rendered) {\r
57123             return false;\r
57124         }\r
57125 \r
57126         var dirty = false;\r
57127         this.eachItem(function(item){\r
57128             if(item.isDirty()){\r
57129                 dirty = true;\r
57130                 return false;\r
57131             }\r
57132         });\r
57133         return dirty;\r
57134     },\r
57135     \r
57136     // private\r
57137     onDisable : function(){\r
57138         this.eachItem(function(item){\r
57139             item.disable();\r
57140         });\r
57141     },\r
57142 \r
57143     // private\r
57144     onEnable : function(){\r
57145         this.eachItem(function(item){\r
57146             item.enable();\r
57147         });\r
57148     },\r
57149     \r
57150     // private\r
57151     doLayout: function(){\r
57152         if(this.rendered){\r
57153             this.panel.forceLayout = this.ownerCt.forceLayout;\r
57154             this.panel.doLayout();\r
57155         }\r
57156     },\r
57157     \r
57158     // private\r
57159     onResize : function(w, h){\r
57160         this.panel.setSize(w, h);\r
57161         this.panel.doLayout();\r
57162     },\r
57163     \r
57164     // inherit docs from Field\r
57165     reset : function(){\r
57166         Ext.form.CheckboxGroup.superclass.reset.call(this);\r
57167         this.eachItem(function(c){\r
57168             if(c.reset){\r
57169                 c.reset();\r
57170             }\r
57171         });\r
57172     },\r
57173     \r
57174     /**\r
57175      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items\r
57176      * in the group. Examples illustrating how this method may be called:\r
57177      * <pre><code>\r
57178 // call with name and value\r
57179 myCheckboxGroup.setValue('cb-col-1', true);\r
57180 // call with an array of boolean values \r
57181 myCheckboxGroup.setValue([true, false, false]);\r
57182 // call with an object literal specifying item:value pairs\r
57183 myCheckboxGroup.setValue({\r
57184     'cb-col-2': false,\r
57185     'cb-col-3': true\r
57186 });\r
57187 // use comma separated string to set items with name to true (checked)\r
57188 myCheckboxGroup.setValue('cb-col-1,cb-col-3');\r
57189      * </code></pre>\r
57190      * See {@link Ext.form.Checkbox#setValue} for additional information.\r
57191      * @param {Mixed} id The checkbox to check, or as described by example shown.\r
57192      * @param {Boolean} value (optional) The value to set the item.\r
57193      * @return {Ext.form.CheckboxGroup} this\r
57194      */\r
57195     setValue: function(){\r
57196         if(this.rendered){\r
57197             this.onSetValue.apply(this, arguments);\r
57198         }else{\r
57199             this.buffered = true;\r
57200             this.value = arguments;\r
57201         }\r
57202         return this;\r
57203     },\r
57204     \r
57205     onSetValue: function(id, value){\r
57206         if(arguments.length == 1){\r
57207             if(Ext.isArray(id)){\r
57208                 // an array of boolean values\r
57209                 Ext.each(id, function(val, idx){\r
57210                     var item = this.items.itemAt(idx);\r
57211                     if(item){\r
57212                         item.setValue(val);\r
57213                     }\r
57214                 }, this);\r
57215             }else if(Ext.isObject(id)){\r
57216                 // set of name/value pairs\r
57217                 for(var i in id){\r
57218                     var f = this.getBox(i);\r
57219                     if(f){\r
57220                         f.setValue(id[i]);\r
57221                     }\r
57222                 }\r
57223             }else{\r
57224                 this.setValueForItem(id);\r
57225             }\r
57226         }else{\r
57227             var f = this.getBox(id);\r
57228             if(f){\r
57229                 f.setValue(value);\r
57230             }\r
57231         }\r
57232     },\r
57233     \r
57234     // private\r
57235     onDestroy: function(){\r
57236         Ext.destroy(this.panel);\r
57237         Ext.form.CheckboxGroup.superclass.onDestroy.call(this);\r
57238 \r
57239     },\r
57240     \r
57241     setValueForItem : function(val){\r
57242         val = String(val).split(',');\r
57243         this.eachItem(function(item){\r
57244             if(val.indexOf(item.inputValue)> -1){\r
57245                 item.setValue(true);\r
57246             }\r
57247         });\r
57248     },\r
57249     \r
57250     // private\r
57251     getBox : function(id){\r
57252         var box = null;\r
57253         this.eachItem(function(f){\r
57254             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){\r
57255                 box = f;\r
57256                 return false;\r
57257             }\r
57258         });\r
57259         return box;\r
57260     },\r
57261     \r
57262     /**\r
57263      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.\r
57264      * @return {Array} An array of the selected checkboxes.\r
57265      */\r
57266     getValue : function(){\r
57267         var out = [];\r
57268         this.eachItem(function(item){\r
57269             if(item.checked){\r
57270                 out.push(item);\r
57271             }\r
57272         });\r
57273         return out;\r
57274     },\r
57275     \r
57276     // private\r
57277     eachItem: function(fn){\r
57278         if(this.items && this.items.each){\r
57279             this.items.each(fn, this);\r
57280         }\r
57281     },\r
57282     \r
57283     /**\r
57284      * @cfg {String} name\r
57285      * @hide\r
57286      */\r
57287 \r
57288     /**\r
57289      * @method getRawValue\r
57290      * @hide\r
57291      */\r
57292     getRawValue : Ext.emptyFn,\r
57293     \r
57294     /**\r
57295      * @method setRawValue\r
57296      * @hide\r
57297      */\r
57298     setRawValue : Ext.emptyFn\r
57299     \r
57300 });\r
57301 \r
57302 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);\r
57303 /**
57304  * @class Ext.form.Radio
57305  * @extends Ext.form.Checkbox
57306  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
57307  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
57308  * @constructor
57309  * Creates a new Radio
57310  * @param {Object} config Configuration options
57311  * @xtype radio
57312  */
57313 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
57314     inputType: 'radio',
57315
57316     /**
57317      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
57318      * @method
57319      */
57320     markInvalid : Ext.emptyFn,
57321     /**
57322      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
57323      * @method
57324      */
57325     clearInvalid : Ext.emptyFn,
57326
57327     /**
57328      * If this radio is part of a group, it will return the selected value
57329      * @return {String}
57330      */
57331     getGroupValue : function(){
57332         var p = this.el.up('form') || Ext.getBody();
57333         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
57334         return c ? c.value : null;
57335     },
57336
57337     // private
57338     onClick : function(){
57339         if(this.el.dom.checked != this.checked){
57340                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
57341                         els.each(function(el){
57342                                 if(el.dom.id == this.id){
57343                                         this.setValue(true);
57344                                 }else{
57345                                         Ext.getCmp(el.dom.id).setValue(false);
57346                                 }
57347                         }, this);
57348                 }
57349     },
57350
57351     /**
57352      * Sets either the checked/unchecked status of this Radio, or, if a string value
57353      * is passed, checks a sibling Radio of the same name whose value is the value specified.
57354      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
57355      * @return {Ext.form.Field} this
57356      */
57357     setValue : function(v){
57358         if (typeof v == 'boolean') {
57359             Ext.form.Radio.superclass.setValue.call(this, v);
57360         } else {
57361             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
57362             if(r){
57363                 Ext.getCmp(r.id).setValue(true);
57364             }
57365         }
57366         return this;
57367     },
57368     
57369     // private
57370     getCheckEl: function(){
57371         if(this.inGroup){
57372             return this.el.up('.x-form-radio-group')
57373         }
57374         return this.el.up('form') || Ext.getBody();
57375     }
57376 });
57377 Ext.reg('radio', Ext.form.Radio);
57378 /**\r
57379  * @class Ext.form.RadioGroup\r
57380  * @extends Ext.form.CheckboxGroup\r
57381  * A grouping container for {@link Ext.form.Radio} controls.\r
57382  * @constructor\r
57383  * Creates a new RadioGroup\r
57384  * @param {Object} config Configuration options\r
57385  * @xtype radiogroup\r
57386  */\r
57387 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {\r
57388     /**\r
57389      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).\r
57390      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will\r
57391      * be used as the error text.\r
57392      */\r
57393     allowBlank : true,\r
57394     /**\r
57395      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails\r
57396      * (defaults to 'You must select one item in this group')\r
57397      */\r
57398     blankText : 'You must select one item in this group',\r
57399     \r
57400     // private\r
57401     defaultType : 'radio',\r
57402     \r
57403     // private\r
57404     groupCls : 'x-form-radio-group',\r
57405     \r
57406     /**\r
57407      * @event change\r
57408      * Fires when the state of a child radio changes.\r
57409      * @param {Ext.form.RadioGroup} this\r
57410      * @param {Ext.form.Radio} checked The checked radio\r
57411      */\r
57412     \r
57413     /**\r
57414      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.\r
57415      * @return {Ext.form.Radio} The selected radio.\r
57416      */\r
57417     getValue : function(){\r
57418         var out = null;\r
57419         this.eachItem(function(item){\r
57420             if(item.checked){\r
57421                 out = item;\r
57422                 return false;\r
57423             }\r
57424         });\r
57425         return out;\r
57426     },\r
57427     \r
57428     /**\r
57429      * Sets the checked radio in the group.\r
57430      * @param {String/Ext.form.Radio} id The radio to check.\r
57431      * @param {Boolean} value The value to set the radio.\r
57432      * @return {Ext.form.RadioGroup} this\r
57433      */\r
57434     onSetValue : function(id, value){\r
57435         if(arguments.length > 1){\r
57436             var f = this.getBox(id);\r
57437             if(f){\r
57438                 f.setValue(value);\r
57439                 if(f.checked){\r
57440                     this.eachItem(function(item){\r
57441                         if (item !== f){\r
57442                             item.setValue(false);\r
57443                         }\r
57444                     });\r
57445                 }\r
57446             }\r
57447         }else{\r
57448             this.setValueForItem(id);\r
57449         }\r
57450     },\r
57451     \r
57452     setValueForItem : function(val){\r
57453         val = String(val).split(',')[0];\r
57454         this.eachItem(function(item){\r
57455             item.setValue(val == item.inputValue);\r
57456         });\r
57457     },\r
57458     \r
57459     // private\r
57460     fireChecked : function(){\r
57461         if(!this.checkTask){\r
57462             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);\r
57463         }\r
57464         this.checkTask.delay(10);\r
57465     },\r
57466     \r
57467     // private\r
57468     bufferChecked : function(){\r
57469         var out = null;\r
57470         this.eachItem(function(item){\r
57471             if(item.checked){\r
57472                 out = item;\r
57473                 return false;\r
57474             }\r
57475         });\r
57476         this.fireEvent('change', this, out);\r
57477     },\r
57478     \r
57479     onDestroy : function(){\r
57480         if(this.checkTask){\r
57481             this.checkTask.cancel();\r
57482             this.checkTask = null;\r
57483         }\r
57484         Ext.form.RadioGroup.superclass.onDestroy.call(this);\r
57485     }\r
57486 \r
57487 });\r
57488 \r
57489 Ext.reg('radiogroup', Ext.form.RadioGroup);\r
57490 /**\r
57491  * @class Ext.form.Hidden\r
57492  * @extends Ext.form.Field\r
57493  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.\r
57494  * @constructor\r
57495  * Create a new Hidden field.\r
57496  * @param {Object} config Configuration options\r
57497  * @xtype hidden\r
57498  */\r
57499 Ext.form.Hidden = Ext.extend(Ext.form.Field, {\r
57500     // private\r
57501     inputType : 'hidden',\r
57502 \r
57503     // private\r
57504     onRender : function(){\r
57505         Ext.form.Hidden.superclass.onRender.apply(this, arguments);\r
57506     },\r
57507 \r
57508     // private\r
57509     initEvents : function(){\r
57510         this.originalValue = this.getValue();\r
57511     },\r
57512 \r
57513     // These are all private overrides\r
57514     setSize : Ext.emptyFn,\r
57515     setWidth : Ext.emptyFn,\r
57516     setHeight : Ext.emptyFn,\r
57517     setPosition : Ext.emptyFn,\r
57518     setPagePosition : Ext.emptyFn,\r
57519     markInvalid : Ext.emptyFn,\r
57520     clearInvalid : Ext.emptyFn\r
57521 });\r
57522 Ext.reg('hidden', Ext.form.Hidden);/**
57523  * @class Ext.form.BasicForm
57524  * @extends Ext.util.Observable
57525  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
57526  * input field management, validation, submission, and form loading services.</p>
57527  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
57528  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
57529  * <p><b><u>File Uploads</u></b></p>
57530  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
57531  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
57532  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
57533  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
57534  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
57535  * but removed after the return data has been gathered.</p>
57536  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
57537  * server is using JSON to send the return object, then the
57538  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
57539  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
57540  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
57541  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
57542  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
57543  * is created containing a <tt>responseText</tt> property in order to conform to the
57544  * requirements of event handlers and callbacks.</p>
57545  * <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>
57546  * and some server technologies (notably JEE) may require some custom processing in order to
57547  * retrieve parameter names and parameter values from the packet content.</p>
57548  * @constructor
57549  * @param {Mixed} el The form element or its id
57550  * @param {Object} config Configuration options
57551  */
57552 Ext.form.BasicForm = function(el, config){
57553     Ext.apply(this, config);
57554     if(Ext.isString(this.paramOrder)){
57555         this.paramOrder = this.paramOrder.split(/[\s,|]/);
57556     }
57557     /**
57558      * @property items
57559      * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form.
57560      * @type MixedCollection
57561      */
57562     this.items = new Ext.util.MixedCollection(false, function(o){
57563         return o.getItemId();
57564     });
57565     this.addEvents(
57566         /**
57567          * @event beforeaction
57568          * Fires before any action is performed. Return false to cancel the action.
57569          * @param {Form} this
57570          * @param {Action} action The {@link Ext.form.Action} to be performed
57571          */
57572         'beforeaction',
57573         /**
57574          * @event actionfailed
57575          * Fires when an action fails.
57576          * @param {Form} this
57577          * @param {Action} action The {@link Ext.form.Action} that failed
57578          */
57579         'actionfailed',
57580         /**
57581          * @event actioncomplete
57582          * Fires when an action is completed.
57583          * @param {Form} this
57584          * @param {Action} action The {@link Ext.form.Action} that completed
57585          */
57586         'actioncomplete'
57587     );
57588
57589     if(el){
57590         this.initEl(el);
57591     }
57592     Ext.form.BasicForm.superclass.constructor.call(this);
57593 };
57594
57595 Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
57596     /**
57597      * @cfg {String} method
57598      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
57599      */
57600     /**
57601      * @cfg {DataReader} reader
57602      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
57603      * data when executing 'load' actions. This is optional as there is built-in
57604      * support for processing JSON.  For additional information on using an XMLReader
57605      * see the example provided in examples/form/xml-form.html.
57606      */
57607     /**
57608      * @cfg {DataReader} errorReader
57609      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
57610      * read field error messages returned from 'submit' actions. This is optional
57611      * as there is built-in support for processing JSON.</p>
57612      * <p>The Records which provide messages for the invalid Fields must use the
57613      * Field name (or id) as the Record ID, and must contain a field called 'msg'
57614      * which contains the error message.</p>
57615      * <p>The errorReader does not have to be a full-blown implementation of a
57616      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
57617      * which returns an Array of Records in an object with the following
57618      * structure:</p><pre><code>
57619 {
57620     records: recordArray
57621 }
57622 </code></pre>
57623      */
57624     /**
57625      * @cfg {String} url
57626      * The URL to use for form actions if one isn't supplied in the
57627      * <code>{@link #doAction doAction} options</code>.
57628      */
57629     /**
57630      * @cfg {Boolean} fileUpload
57631      * Set to true if this form is a file upload.
57632      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
57633      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
57634      * DOM <tt>&lt;form></tt> element temporarily modified to have its
57635      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
57636      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
57637      * but removed after the return data has been gathered.</p>
57638      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
57639      * server is using JSON to send the return object, then the
57640      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
57641      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
57642      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
57643      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
57644      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
57645      * is created containing a <tt>responseText</tt> property in order to conform to the
57646      * requirements of event handlers and callbacks.</p>
57647      * <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>
57648      * and some server technologies (notably JEE) may require some custom processing in order to
57649      * retrieve parameter names and parameter values from the packet content.</p>
57650      */
57651     /**
57652      * @cfg {Object} baseParams
57653      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
57654      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
57655      */
57656     /**
57657      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
57658      */
57659     timeout: 30,
57660
57661     /**
57662      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
57663      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
57664      * Methods which have been imported by Ext.Direct can be specified here to load and submit
57665      * forms.
57666      * Such as the following:<pre><code>
57667 api: {
57668     load: App.ss.MyProfile.load,
57669     submit: App.ss.MyProfile.submit
57670 }
57671 </code></pre>
57672      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
57673      * to customize how the load method is invoked.
57674      * Submit actions will always use a standard form submit. The formHandler configuration must
57675      * be set on the associated server-side method which has been imported by Ext.Direct</p>
57676      */
57677
57678     /**
57679      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
57680      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
57681      * <code>load</code> configuration.</p>
57682      * <br><p>Specify the params in the order in which they must be executed on the
57683      * server-side as either (1) an Array of String values, or (2) a String of params
57684      * delimited by either whitespace, comma, or pipe. For example,
57685      * any of the following would be acceptable:</p><pre><code>
57686 paramOrder: ['param1','param2','param3']
57687 paramOrder: 'param1 param2 param3'
57688 paramOrder: 'param1,param2,param3'
57689 paramOrder: 'param1|param2|param'
57690      </code></pre>
57691      */
57692     paramOrder: undefined,
57693
57694     /**
57695      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
57696      * <code>load</code> configuration. Send parameters as a collection of named
57697      * arguments (defaults to <tt>false</tt>). Providing a
57698      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
57699      */
57700     paramsAsHash: false,
57701
57702     /**
57703      * @cfg {String} waitTitle
57704      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
57705      */
57706     waitTitle: 'Please Wait...',
57707
57708     // private
57709     activeAction : null,
57710
57711     /**
57712      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
57713      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
57714      */
57715     trackResetOnLoad : false,
57716
57717     /**
57718      * @cfg {Boolean} standardSubmit
57719      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
57720      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
57721      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
57722      * <code>options</code> to <code>{@link #submit}</code> are ignored because
57723      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
57724      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
57725      * to submit extra data, for example:</p>
57726      * <pre><code>
57727 new Ext.FormPanel({
57728     standardSubmit: true,
57729     baseParams: {
57730         foo: 'bar'
57731     },
57732     {@link url}: 'myProcess.php',
57733     items: [{
57734         xtype: 'textfield',
57735         name: 'userName'
57736     }],
57737     buttons: [{
57738         text: 'Save',
57739         handler: function(){
57740             var fp = this.ownerCt.ownerCt,
57741                 form = fp.getForm();
57742             if (form.isValid()) {
57743                 // check if there are baseParams and if
57744                 // hiddent items have been added already
57745                 if (fp.baseParams && !fp.paramsAdded) {
57746                     // add hidden items for all baseParams
57747                     for (i in fp.baseParams) {
57748                         fp.add({
57749                             xtype: 'hidden',
57750                             name: i,
57751                             value: fp.baseParams[i]
57752                         });
57753                     }
57754                     fp.doLayout();
57755                     // set a custom flag to prevent re-adding
57756                     fp.paramsAdded = true;
57757                 }
57758                 form.{@link #submit}();
57759             }
57760         }
57761     }]
57762 });
57763      * </code></pre>
57764      */
57765     /**
57766      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
57767      * element by passing it or its id or mask the form itself by passing in true.
57768      * @type Mixed
57769      * @property waitMsgTarget
57770      */
57771
57772     // private
57773     initEl : function(el){
57774         this.el = Ext.get(el);
57775         this.id = this.el.id || Ext.id();
57776         if(!this.standardSubmit){
57777             this.el.on('submit', this.onSubmit, this);
57778         }
57779         this.el.addClass('x-form');
57780     },
57781
57782     /**
57783      * Get the HTML form Element
57784      * @return Ext.Element
57785      */
57786     getEl: function(){
57787         return this.el;
57788     },
57789
57790     // private
57791     onSubmit : function(e){
57792         e.stopEvent();
57793     },
57794
57795     // private
57796     destroy: function() {
57797         this.items.each(function(f){
57798             Ext.destroy(f);
57799         });
57800         if(this.el){
57801             this.el.removeAllListeners();
57802             this.el.remove();
57803         }
57804         this.purgeListeners();
57805     },
57806
57807     /**
57808      * Returns true if client-side validation on the form is successful.
57809      * @return Boolean
57810      */
57811     isValid : function(){
57812         var valid = true;
57813         this.items.each(function(f){
57814            if(!f.validate()){
57815                valid = false;
57816            }
57817         });
57818         return valid;
57819     },
57820
57821     /**
57822      * <p>Returns true if any fields in this form have changed from their original values.</p>
57823      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
57824      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
57825      * or {@link #loadRecord}.</p>
57826      * @return Boolean
57827      */
57828     isDirty : function(){
57829         var dirty = false;
57830         this.items.each(function(f){
57831            if(f.isDirty()){
57832                dirty = true;
57833                return false;
57834            }
57835         });
57836         return dirty;
57837     },
57838
57839     /**
57840      * Performs a predefined action ({@link Ext.form.Action.Submit} or
57841      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
57842      * to perform application-specific processing.
57843      * @param {String/Object} actionName The name of the predefined action type,
57844      * or instance of {@link Ext.form.Action} to perform.
57845      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
57846      * All of the config options listed below are supported by both the
57847      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
57848      * actions unless otherwise noted (custom actions could also accept
57849      * other config options):<ul>
57850      *
57851      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
57852      * to the form's {@link #url}.)</div></li>
57853      *
57854      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
57855      * to the form's method, or POST if not defined)</div></li>
57856      *
57857      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
57858      * (defaults to the form's baseParams, or none if not defined)</p>
57859      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
57860      *
57861      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
57862      * (defaults to the form's default headers)</div></li>
57863      *
57864      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
57865      * be invoked after a successful response (see top of
57866      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
57867      * for a description of what constitutes a successful response).
57868      * The function is passed the following parameters:<ul>
57869      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
57870      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
57871      * <div class="sub-desc">The action object contains these properties of interest:<ul>
57872      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
57873      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
57874      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
57875      * </ul></div></li></ul></div></li>
57876      *
57877      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
57878      * failed transaction attempt. The function is passed the following parameters:<ul>
57879      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
57880      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
57881      * <div class="sub-desc">The action object contains these properties of interest:<ul>
57882      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
57883      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
57884      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
57885      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
57886      * </ul></div></li></ul></div></li>
57887      *
57888      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
57889      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
57890      *
57891      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
57892      * Determines whether a Form's fields are validated in a final call to
57893      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
57894      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
57895      *
57896      * @return {BasicForm} this
57897      */
57898     doAction : function(action, options){
57899         if(Ext.isString(action)){
57900             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
57901         }
57902         if(this.fireEvent('beforeaction', this, action) !== false){
57903             this.beforeAction(action);
57904             action.run.defer(100, action);
57905         }
57906         return this;
57907     },
57908
57909     /**
57910      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
57911      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
57912      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
57913      * <p>The following code:</p><pre><code>
57914 myFormPanel.getForm().submit({
57915     clientValidation: true,
57916     url: 'updateConsignment.php',
57917     params: {
57918         newStatus: 'delivered'
57919     },
57920     success: function(form, action) {
57921        Ext.Msg.alert('Success', action.result.msg);
57922     },
57923     failure: function(form, action) {
57924         switch (action.failureType) {
57925             case Ext.form.Action.CLIENT_INVALID:
57926                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
57927                 break;
57928             case Ext.form.Action.CONNECT_FAILURE:
57929                 Ext.Msg.alert('Failure', 'Ajax communication failed');
57930                 break;
57931             case Ext.form.Action.SERVER_INVALID:
57932                Ext.Msg.alert('Failure', action.result.msg);
57933        }
57934     }
57935 });
57936 </code></pre>
57937      * would process the following server response for a successful submission:<pre><code>
57938 {
57939     "success":true, // note this is Boolean, not string
57940     "msg":"Consignment updated"
57941 }
57942 </code></pre>
57943      * and the following server response for a failed submission:<pre><code>
57944 {
57945     "success":false, // note this is Boolean, not string
57946     "msg":"You do not have permission to perform this operation"
57947 }
57948 </code></pre>
57949      * @return {BasicForm} this
57950      */
57951     submit : function(options){
57952         if(this.standardSubmit){
57953             var v = this.isValid();
57954             if(v){
57955                 var el = this.el.dom;
57956                 if(this.url && Ext.isEmpty(el.action)){
57957                     el.action = this.url;
57958                 }
57959                 el.submit();
57960             }
57961             return v;
57962         }
57963         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
57964         this.doAction(submitAction, options);
57965         return this;
57966     },
57967
57968     /**
57969      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
57970      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
57971      * @return {BasicForm} this
57972      */
57973     load : function(options){
57974         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
57975         this.doAction(loadAction, options);
57976         return this;
57977     },
57978
57979     /**
57980      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
57981      * @param {Record} record The record to edit
57982      * @return {BasicForm} this
57983      */
57984     updateRecord : function(record){
57985         record.beginEdit();
57986         var fs = record.fields;
57987         fs.each(function(f){
57988             var field = this.findField(f.name);
57989             if(field){
57990                 record.set(f.name, field.getValue());
57991             }
57992         }, this);
57993         record.endEdit();
57994         return this;
57995     },
57996
57997     /**
57998      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
57999      * {@link Ext.data.Record#data record data}.
58000      * See also {@link #trackResetOnLoad}.
58001      * @param {Record} record The record to load
58002      * @return {BasicForm} this
58003      */
58004     loadRecord : function(record){
58005         this.setValues(record.data);
58006         return this;
58007     },
58008
58009     // private
58010     beforeAction : function(action){
58011         var o = action.options;
58012         if(o.waitMsg){
58013             if(this.waitMsgTarget === true){
58014                 this.el.mask(o.waitMsg, 'x-mask-loading');
58015             }else if(this.waitMsgTarget){
58016                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
58017                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
58018             }else{
58019                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
58020             }
58021         }
58022     },
58023
58024     // private
58025     afterAction : function(action, success){
58026         this.activeAction = null;
58027         var o = action.options;
58028         if(o.waitMsg){
58029             if(this.waitMsgTarget === true){
58030                 this.el.unmask();
58031             }else if(this.waitMsgTarget){
58032                 this.waitMsgTarget.unmask();
58033             }else{
58034                 Ext.MessageBox.updateProgress(1);
58035                 Ext.MessageBox.hide();
58036             }
58037         }
58038         if(success){
58039             if(o.reset){
58040                 this.reset();
58041             }
58042             Ext.callback(o.success, o.scope, [this, action]);
58043             this.fireEvent('actioncomplete', this, action);
58044         }else{
58045             Ext.callback(o.failure, o.scope, [this, action]);
58046             this.fireEvent('actionfailed', this, action);
58047         }
58048     },
58049
58050     /**
58051      * Find a {@link Ext.form.Field} in this form.
58052      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
58053      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
58054      * @return Field
58055      */
58056     findField : function(id){
58057         var field = this.items.get(id);
58058         if(!Ext.isObject(field)){
58059             this.items.each(function(f){
58060                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
58061                     field = f;
58062                     return false;
58063                 }
58064             });
58065         }
58066         return field || null;
58067     },
58068
58069
58070     /**
58071      * Mark fields in this form invalid in bulk.
58072      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
58073      * @return {BasicForm} this
58074      */
58075     markInvalid : function(errors){
58076         if(Ext.isArray(errors)){
58077             for(var i = 0, len = errors.length; i < len; i++){
58078                 var fieldError = errors[i];
58079                 var f = this.findField(fieldError.id);
58080                 if(f){
58081                     f.markInvalid(fieldError.msg);
58082                 }
58083             }
58084         }else{
58085             var field, id;
58086             for(id in errors){
58087                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
58088                     field.markInvalid(errors[id]);
58089                 }
58090             }
58091         }
58092         return this;
58093     },
58094
58095     /**
58096      * Set values for fields in this form in bulk.
58097      * @param {Array/Object} values Either an array in the form:<pre><code>
58098 [{id:'clientName', value:'Fred. Olsen Lines'},
58099  {id:'portOfLoading', value:'FXT'},
58100  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
58101      * or an object hash of the form:<pre><code>
58102 {
58103     clientName: 'Fred. Olsen Lines',
58104     portOfLoading: 'FXT',
58105     portOfDischarge: 'OSL'
58106 }</code></pre>
58107      * @return {BasicForm} this
58108      */
58109     setValues : function(values){
58110         if(Ext.isArray(values)){ // array of objects
58111             for(var i = 0, len = values.length; i < len; i++){
58112                 var v = values[i];
58113                 var f = this.findField(v.id);
58114                 if(f){
58115                     f.setValue(v.value);
58116                     if(this.trackResetOnLoad){
58117                         f.originalValue = f.getValue();
58118                     }
58119                 }
58120             }
58121         }else{ // object hash
58122             var field, id;
58123             for(id in values){
58124                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
58125                     field.setValue(values[id]);
58126                     if(this.trackResetOnLoad){
58127                         field.originalValue = field.getValue();
58128                     }
58129                 }
58130             }
58131         }
58132         return this;
58133     },
58134
58135     /**
58136      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
58137      * If multiple fields exist with the same name they are returned as an array.</p>
58138      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
58139      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
58140      * value can potentially be the emptyText of a field.</p>
58141      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
58142      * @return {String/Object}
58143      */
58144     getValues : function(asString){
58145         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
58146         if(asString === true){
58147             return fs;
58148         }
58149         return Ext.urlDecode(fs);
58150     },
58151
58152     getFieldValues : function(){
58153         var o = {};
58154         this.items.each(function(f){
58155            o[f.getName()] = f.getValue();
58156         });
58157         return o;
58158     },
58159
58160     /**
58161      * Clears all invalid messages in this form.
58162      * @return {BasicForm} this
58163      */
58164     clearInvalid : function(){
58165         this.items.each(function(f){
58166            f.clearInvalid();
58167         });
58168         return this;
58169     },
58170
58171     /**
58172      * Resets this form.
58173      * @return {BasicForm} this
58174      */
58175     reset : function(){
58176         this.items.each(function(f){
58177             f.reset();
58178         });
58179         return this;
58180     },
58181
58182     /**
58183      * Add Ext.form Components to this form's Collection. This does not result in rendering of
58184      * the passed Component, it just enables the form to validate Fields, and distribute values to
58185      * Fields.
58186      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
58187      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
58188      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
58189      * collection.</b></p>
58190      * @param {Field} field1
58191      * @param {Field} field2 (optional)
58192      * @param {Field} etc (optional)
58193      * @return {BasicForm} this
58194      */
58195     add : function(){
58196         this.items.addAll(Array.prototype.slice.call(arguments, 0));
58197         return this;
58198     },
58199
58200
58201     /**
58202      * Removes a field from the items collection (does NOT remove its markup).
58203      * @param {Field} field
58204      * @return {BasicForm} this
58205      */
58206     remove : function(field){
58207         this.items.remove(field);
58208         return this;
58209     },
58210
58211     /**
58212      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
58213      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
58214      * @return {BasicForm} this
58215      */
58216     render : function(){
58217         this.items.each(function(f){
58218             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
58219                 f.applyToMarkup(f.id);
58220             }
58221         });
58222         return this;
58223     },
58224
58225     /**
58226      * Calls {@link Ext#apply} for all fields in this form with the passed object.
58227      * @param {Object} values
58228      * @return {BasicForm} this
58229      */
58230     applyToFields : function(o){
58231         this.items.each(function(f){
58232            Ext.apply(f, o);
58233         });
58234         return this;
58235     },
58236
58237     /**
58238      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
58239      * @param {Object} values
58240      * @return {BasicForm} this
58241      */
58242     applyIfToFields : function(o){
58243         this.items.each(function(f){
58244            Ext.applyIf(f, o);
58245         });
58246         return this;
58247     },
58248
58249     callFieldMethod : function(fnName, args){
58250         args = args || [];
58251         this.items.each(function(f){
58252             if(Ext.isFunction(f[fnName])){
58253                 f[fnName].apply(f, args);
58254             }
58255         });
58256         return this;
58257     }
58258 });
58259
58260 // back compat
58261 Ext.BasicForm = Ext.form.BasicForm;/**
58262  * @class Ext.form.FormPanel
58263  * @extends Ext.Panel
58264  * <p>Standard form container.</p>
58265  * 
58266  * <p><b><u>Layout</u></b></p>
58267  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
58268  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
58269  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
58270  * {@link Ext.layout.FormLayout} layout manager.</p>
58271  * 
58272  * <p><b><u>BasicForm</u></b></p>
58273  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
58274  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
58275  * <div class="mdetail-params"><ul>
58276  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
58277  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
58278  * </ul></div>
58279  *  
58280  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
58281  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
58282  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
58283  * 
58284  * <p><b><u>Form Validation</u></b></p>
58285  * <p>For information on form validation see the following:</p>
58286  * <div class="mdetail-params"><ul>
58287  * <li>{@link Ext.form.TextField}</li>
58288  * <li>{@link Ext.form.VTypes}</li>
58289  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
58290  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
58291  * </ul></div>
58292  * 
58293  * <p><b><u>Form Submission</u></b></p>
58294  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
58295  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
58296  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
58297  * 
58298  * @constructor
58299  * @param {Object} config Configuration options
58300  * @xtype form
58301  */
58302 Ext.FormPanel = Ext.extend(Ext.Panel, {
58303         /**
58304          * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
58305          */
58306     /**
58307      * @cfg {Boolean} hideLabels
58308      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
58309      * <tt>false</tt>.</p>
58310      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
58311      */
58312     /**
58313      * @cfg {Number} labelPad
58314      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
58315      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
58316      */
58317     /**
58318      * @cfg {String} labelSeparator
58319      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
58320      */
58321     /**
58322      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
58323      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
58324      * for its fields) (defaults to <tt>100</tt>).
58325      */
58326     /**
58327      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
58328      */
58329     /**
58330      * @cfg {Array} buttons
58331      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
58332      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
58333      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
58334      * the form's valid/invalid state.</p>
58335      */
58336
58337
58338     /**
58339      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
58340      */
58341     minButtonWidth : 75,
58342
58343     /**
58344      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
58345      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
58346      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
58347      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
58348      * for its fields).
58349      */
58350     labelAlign : 'left',
58351
58352     /**
58353      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
58354      * regularly fires the {@link #clientvalidation} event passing that state.<br>
58355      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
58356      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
58357      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
58358      */
58359     monitorValid : false,
58360
58361     /**
58362      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
58363      */
58364     monitorPoll : 200,
58365
58366     /**
58367      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered. 
58368      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
58369      */
58370     layout : 'form',
58371
58372     // private
58373     initComponent : function(){
58374         this.form = this.createForm();
58375         Ext.FormPanel.superclass.initComponent.call(this);
58376
58377         this.bodyCfg = {
58378             tag: 'form',
58379             cls: this.baseCls + '-body',
58380             method : this.method || 'POST',
58381             id : this.formId || Ext.id()
58382         };
58383         if(this.fileUpload) {
58384             this.bodyCfg.enctype = 'multipart/form-data';
58385         }
58386         this.initItems();
58387         
58388         this.addEvents(
58389             /**
58390              * @event clientvalidation
58391              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
58392              * @param {Ext.form.FormPanel} this
58393              * @param {Boolean} valid true if the form has passed client-side validation
58394              */
58395             'clientvalidation'
58396         );
58397
58398         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
58399     },
58400
58401     // private
58402     createForm : function(){
58403         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
58404         return new Ext.form.BasicForm(null, config);
58405     },
58406
58407     // private
58408     initFields : function(){
58409         var f = this.form;
58410         var formPanel = this;
58411         var fn = function(c){
58412             if(formPanel.isField(c)){
58413                 f.add(c);
58414             }else if(c.findBy && c != formPanel){
58415                 formPanel.applySettings(c);
58416                 //each check required for check/radio groups.
58417                 if(c.items && c.items.each){
58418                     c.items.each(fn, this);
58419                 }
58420             }
58421         };
58422         this.items.each(fn, this);
58423     },
58424     
58425     // private
58426     applySettings: function(c){
58427         var ct = c.ownerCt;
58428         Ext.applyIf(c, {
58429             labelAlign: ct.labelAlign,
58430             labelWidth: ct.labelWidth,
58431             itemCls: ct.itemCls
58432         });
58433     },
58434
58435     // private
58436     getLayoutTarget : function(){
58437         return this.form.el;
58438     },
58439
58440     /**
58441      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
58442      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
58443      */
58444     getForm : function(){
58445         return this.form;
58446     },
58447
58448     // private
58449     onRender : function(ct, position){
58450         this.initFields();
58451         Ext.FormPanel.superclass.onRender.call(this, ct, position);
58452         this.form.initEl(this.body);
58453     },
58454     
58455     // private
58456     beforeDestroy : function(){
58457         this.stopMonitoring();
58458         Ext.FormPanel.superclass.beforeDestroy.call(this);
58459         /*
58460          * Clear the items here to prevent them being destroyed again.
58461          * Don't move this behaviour to BasicForm because it can be used
58462          * on it's own.
58463          */
58464         this.form.items.clear();
58465         Ext.destroy(this.form);
58466     },
58467
58468         // Determine if a Component is usable as a form Field.
58469     isField : function(c) {
58470         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
58471     },
58472
58473     // private
58474     initEvents : function(){
58475         Ext.FormPanel.superclass.initEvents.call(this);
58476         // Listeners are required here to catch bubbling events from children.
58477         this.on({
58478             scope: this,
58479             add: this.onAddEvent,
58480             remove: this.onRemoveEvent
58481         });
58482         if(this.monitorValid){ // initialize after render
58483             this.startMonitoring();
58484         }
58485     },
58486     
58487     // private
58488     onAdd: function(c){
58489         Ext.FormPanel.superclass.onAdd.call(this, c);  
58490         this.processAdd(c);
58491     },
58492     
58493     // private
58494     onAddEvent: function(ct, c){
58495         if(ct !== this){
58496             this.processAdd(c);
58497         }
58498     },
58499     
58500     // private
58501     processAdd : function(c){
58502                 // If a single form Field, add it
58503         if(this.isField(c)){
58504             this.form.add(c);
58505                 // If a Container, add any Fields it might contain
58506         }else if(c.findBy){
58507             this.applySettings(c);
58508             this.form.add.apply(this.form, c.findBy(this.isField));
58509         }
58510     },
58511     
58512     // private
58513     onRemove: function(c){
58514         Ext.FormPanel.superclass.onRemove.call(this, c);
58515         this.processRemove(c);
58516     },
58517     
58518     onRemoveEvent: function(ct, c){
58519         if(ct !== this){
58520             this.processRemove(c);
58521         }
58522     },
58523         
58524     // private
58525     processRemove : function(c){
58526                 // If a single form Field, remove it
58527         if(this.isField(c)){
58528                 this.form.remove(c);
58529                 // If a Container, remove any Fields it might contain
58530         }else if(c.findBy){
58531             Ext.each(c.findBy(this.isField), this.form.remove, this.form);
58532         }
58533     },
58534
58535     /**
58536      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
58537      * option "monitorValid"
58538      */
58539     startMonitoring : function(){
58540         if(!this.validTask){
58541             this.validTask = new Ext.util.TaskRunner();
58542             this.validTask.start({
58543                 run : this.bindHandler,
58544                 interval : this.monitorPoll || 200,
58545                 scope: this
58546             });
58547         }
58548     },
58549
58550     /**
58551      * Stops monitoring of the valid state of this form
58552      */
58553     stopMonitoring : function(){
58554         if(this.validTask){
58555             this.validTask.stopAll();
58556             this.validTask = null;
58557         }
58558     },
58559
58560     /**
58561      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
58562      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
58563      */
58564     load : function(){
58565         this.form.load.apply(this.form, arguments);  
58566     },
58567
58568     // private
58569     onDisable : function(){
58570         Ext.FormPanel.superclass.onDisable.call(this);
58571         if(this.form){
58572             this.form.items.each(function(){
58573                  this.disable();
58574             });
58575         }
58576     },
58577
58578     // private
58579     onEnable : function(){
58580         Ext.FormPanel.superclass.onEnable.call(this);
58581         if(this.form){
58582             this.form.items.each(function(){
58583                  this.enable();
58584             });
58585         }
58586     },
58587
58588     // private
58589     bindHandler : function(){
58590         var valid = true;
58591         this.form.items.each(function(f){
58592             if(!f.isValid(true)){
58593                 valid = false;
58594                 return false;
58595             }
58596         });
58597         if(this.fbar){
58598             var fitems = this.fbar.items.items;
58599             for(var i = 0, len = fitems.length; i < len; i++){
58600                 var btn = fitems[i];
58601                 if(btn.formBind === true && btn.disabled === valid){
58602                     btn.setDisabled(!valid);
58603                 }
58604             }
58605         }
58606         this.fireEvent('clientvalidation', this, valid);
58607     }
58608 });
58609 Ext.reg('form', Ext.FormPanel);
58610
58611 Ext.form.FormPanel = Ext.FormPanel;
58612
58613 /**\r
58614  * @class Ext.form.FieldSet\r
58615  * @extends Ext.Panel\r
58616  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.\r
58617  * <pre><code>\r
58618 var form = new Ext.FormPanel({\r
58619     title: 'Simple Form with FieldSets',\r
58620     labelWidth: 75, // label settings here cascade unless overridden\r
58621     url: 'save-form.php',\r
58622     frame:true,\r
58623     bodyStyle:'padding:5px 5px 0',\r
58624     width: 700,\r
58625     renderTo: document.body,\r
58626     layout:'column', // arrange items in columns\r
58627     defaults: {      // defaults applied to items\r
58628         layout: 'form',\r
58629         border: false,\r
58630         bodyStyle: 'padding:4px'\r
58631     },\r
58632     items: [{\r
58633         // Fieldset in Column 1\r
58634         xtype:'fieldset',\r
58635         columnWidth: 0.5,\r
58636         title: 'Fieldset 1',\r
58637         collapsible: true,\r
58638         autoHeight:true,\r
58639         defaults: {\r
58640             anchor: '-20' // leave room for error icon\r
58641         },\r
58642         defaultType: 'textfield',\r
58643         items :[{\r
58644                 fieldLabel: 'Field 1'\r
58645             }, {\r
58646                 fieldLabel: 'Field 2'\r
58647             }, {\r
58648                 fieldLabel: 'Field 3'\r
58649             }\r
58650         ]\r
58651     },{\r
58652         // Fieldset in Column 2 - Panel inside\r
58653         xtype:'fieldset',\r
58654         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header\r
58655         autoHeight:true,\r
58656         columnWidth: 0.5,\r
58657         checkboxToggle: true,\r
58658         collapsed: true, // fieldset initially collapsed\r
58659         layout:'anchor',\r
58660         items :[{\r
58661             xtype: 'panel',\r
58662             anchor: '100%',\r
58663             title: 'Panel inside a fieldset',\r
58664             frame: true,\r
58665             height: 100\r
58666         }]\r
58667     }]\r
58668 });\r
58669  * </code></pre>\r
58670  * @constructor\r
58671  * @param {Object} config Configuration options\r
58672  * @xtype fieldset\r
58673  */\r
58674 Ext.form.FieldSet = Ext.extend(Ext.Panel, {\r
58675     /**\r
58676      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just\r
58677      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults\r
58678      * to <tt>false</tt>).\r
58679      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.\r
58680      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element\r
58681      * is:</p><pre><code>\r
58682      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}\r
58683      * </code></pre>   \r
58684      */\r
58685     /**\r
58686      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>\r
58687      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).\r
58688      */\r
58689     /**\r
58690      * @cfg {Boolean} collapsible\r
58691      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically\r
58692      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse\r
58693      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.\r
58694      */\r
58695     /**\r
58696      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.\r
58697      */\r
58698     /**\r
58699      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see \r
58700      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).\r
58701      * This property cascades to child containers.\r
58702      */\r
58703     /**\r
58704      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).\r
58705      */\r
58706     baseCls : 'x-fieldset',\r
58707     /**\r
58708      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).\r
58709      */\r
58710     layout : 'form',\r
58711     /**\r
58712      * @cfg {Boolean} animCollapse\r
58713      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the\r
58714      * animation (defaults to <tt>false</tt>).\r
58715      */\r
58716     animCollapse : false,\r
58717 \r
58718     // private\r
58719     onRender : function(ct, position){\r
58720         if(!this.el){\r
58721             this.el = document.createElement('fieldset');\r
58722             this.el.id = this.id;\r
58723             if (this.title || this.header || this.checkboxToggle) {\r
58724                 this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';\r
58725             }\r
58726         }\r
58727 \r
58728         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);\r
58729 \r
58730         if(this.checkboxToggle){\r
58731             var o = typeof this.checkboxToggle == 'object' ?\r
58732                     this.checkboxToggle :\r
58733                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};\r
58734             this.checkbox = this.header.insertFirst(o);\r
58735             this.checkbox.dom.checked = !this.collapsed;\r
58736             this.mon(this.checkbox, 'click', this.onCheckClick, this);\r
58737         }\r
58738     },\r
58739 \r
58740     // private\r
58741     onCollapse : function(doAnim, animArg){\r
58742         if(this.checkbox){\r
58743             this.checkbox.dom.checked = false;\r
58744         }\r
58745         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);\r
58746 \r
58747     },\r
58748 \r
58749     // private\r
58750     onExpand : function(doAnim, animArg){\r
58751         if(this.checkbox){\r
58752             this.checkbox.dom.checked = true;\r
58753         }\r
58754         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);\r
58755     },\r
58756 \r
58757     /**\r
58758      * This function is called by the fieldset's checkbox when it is toggled (only applies when\r
58759      * checkboxToggle = true).  This method should never be called externally, but can be\r
58760      * overridden to provide custom behavior when the checkbox is toggled if needed.\r
58761      */\r
58762     onCheckClick : function(){\r
58763         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();\r
58764     }\r
58765 \r
58766     /**\r
58767      * @cfg {String/Number} activeItem\r
58768      * @hide\r
58769      */\r
58770     /**\r
58771      * @cfg {Mixed} applyTo\r
58772      * @hide\r
58773      */\r
58774     /**\r
58775      * @cfg {Boolean} bodyBorder\r
58776      * @hide\r
58777      */\r
58778     /**\r
58779      * @cfg {Boolean} border\r
58780      * @hide\r
58781      */\r
58782     /**\r
58783      * @cfg {Boolean/Number} bufferResize\r
58784      * @hide\r
58785      */\r
58786     /**\r
58787      * @cfg {Boolean} collapseFirst\r
58788      * @hide\r
58789      */\r
58790     /**\r
58791      * @cfg {String} defaultType\r
58792      * @hide\r
58793      */\r
58794     /**\r
58795      * @cfg {String} disabledClass\r
58796      * @hide\r
58797      */\r
58798     /**\r
58799      * @cfg {String} elements\r
58800      * @hide\r
58801      */\r
58802     /**\r
58803      * @cfg {Boolean} floating\r
58804      * @hide\r
58805      */\r
58806     /**\r
58807      * @cfg {Boolean} footer\r
58808      * @hide\r
58809      */\r
58810     /**\r
58811      * @cfg {Boolean} frame\r
58812      * @hide\r
58813      */\r
58814     /**\r
58815      * @cfg {Boolean} header\r
58816      * @hide\r
58817      */\r
58818     /**\r
58819      * @cfg {Boolean} headerAsText\r
58820      * @hide\r
58821      */\r
58822     /**\r
58823      * @cfg {Boolean} hideCollapseTool\r
58824      * @hide\r
58825      */\r
58826     /**\r
58827      * @cfg {String} iconCls\r
58828      * @hide\r
58829      */\r
58830     /**\r
58831      * @cfg {Boolean/String} shadow\r
58832      * @hide\r
58833      */\r
58834     /**\r
58835      * @cfg {Number} shadowOffset\r
58836      * @hide\r
58837      */\r
58838     /**\r
58839      * @cfg {Boolean} shim\r
58840      * @hide\r
58841      */\r
58842     /**\r
58843      * @cfg {Object/Array} tbar\r
58844      * @hide\r
58845      */\r
58846     /**\r
58847      * @cfg {Array} tools\r
58848      * @hide\r
58849      */\r
58850     /**\r
58851      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate\r
58852      * @hide\r
58853      */\r
58854     /**\r
58855      * @cfg {String} xtype\r
58856      * @hide\r
58857      */\r
58858     /**\r
58859      * @property header\r
58860      * @hide\r
58861      */\r
58862     /**\r
58863      * @property footer\r
58864      * @hide\r
58865      */\r
58866     /**\r
58867      * @method focus\r
58868      * @hide\r
58869      */\r
58870     /**\r
58871      * @method getBottomToolbar\r
58872      * @hide\r
58873      */\r
58874     /**\r
58875      * @method getTopToolbar\r
58876      * @hide\r
58877      */\r
58878     /**\r
58879      * @method setIconClass\r
58880      * @hide\r
58881      */\r
58882     /**\r
58883      * @event activate\r
58884      * @hide\r
58885      */\r
58886     /**\r
58887      * @event beforeclose\r
58888      * @hide\r
58889      */\r
58890     /**\r
58891      * @event bodyresize\r
58892      * @hide\r
58893      */\r
58894     /**\r
58895      * @event close\r
58896      * @hide\r
58897      */\r
58898     /**\r
58899      * @event deactivate\r
58900      * @hide\r
58901      */\r
58902 });\r
58903 Ext.reg('fieldset', Ext.form.FieldSet);\r
58904 /**\r
58905  * @class Ext.form.HtmlEditor\r
58906  * @extends Ext.form.Field\r
58907  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be \r
58908  * automatically hidden when needed.  These are noted in the config options where appropriate.\r
58909  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not \r
58910  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.\r
58911  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT\r
58912  * supported by this editor.</b>\r
58913  * <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
58914  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.\r
58915  * <br><br>Example usage:\r
58916  * <pre><code>\r
58917 // Simple example rendered with default options:\r
58918 Ext.QuickTips.init();  // enable tooltips\r
58919 new Ext.form.HtmlEditor({\r
58920     renderTo: Ext.getBody(),\r
58921     width: 800,\r
58922     height: 300\r
58923 });\r
58924 \r
58925 // Passed via xtype into a container and with custom options:\r
58926 Ext.QuickTips.init();  // enable tooltips\r
58927 new Ext.Panel({\r
58928     title: 'HTML Editor',\r
58929     renderTo: Ext.getBody(),\r
58930     width: 600,\r
58931     height: 300,\r
58932     frame: true,\r
58933     layout: 'fit',\r
58934     items: {\r
58935         xtype: 'htmleditor',\r
58936         enableColors: false,\r
58937         enableAlignments: false\r
58938     }\r
58939 });\r
58940 </code></pre>\r
58941  * @constructor\r
58942  * Create a new HtmlEditor\r
58943  * @param {Object} config\r
58944  * @xtype htmleditor\r
58945  */\r
58946 \r
58947 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {\r
58948     /**\r
58949      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)\r
58950      */\r
58951     enableFormat : true,\r
58952     /**\r
58953      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)\r
58954      */\r
58955     enableFontSize : true,\r
58956     /**\r
58957      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)\r
58958      */\r
58959     enableColors : true,\r
58960     /**\r
58961      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)\r
58962      */\r
58963     enableAlignments : true,\r
58964     /**\r
58965      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)\r
58966      */\r
58967     enableLists : true,\r
58968     /**\r
58969      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)\r
58970      */\r
58971     enableSourceEdit : true,\r
58972     /**\r
58973      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)\r
58974      */\r
58975     enableLinks : true,\r
58976     /**\r
58977      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)\r
58978      */\r
58979     enableFont : true,\r
58980     /**\r
58981      * @cfg {String} createLinkText The default text for the create link prompt\r
58982      */\r
58983     createLinkText : 'Please enter the URL for the link:',\r
58984     /**\r
58985      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)\r
58986      */\r
58987     defaultLinkValue : 'http:/'+'/',\r
58988     /**\r
58989      * @cfg {Array} fontFamilies An array of available font families\r
58990      */\r
58991     fontFamilies : [\r
58992         'Arial',\r
58993         'Courier New',\r
58994         'Tahoma',\r
58995         'Times New Roman',\r
58996         'Verdana'\r
58997     ],\r
58998     defaultFont: 'tahoma',\r
58999     /**\r
59000      * @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
59001      */\r
59002     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',\r
59003 \r
59004     // private properties\r
59005     actionMode: 'wrap',\r
59006     validationEvent : false,\r
59007     deferHeight: true,\r
59008     initialized : false,\r
59009     activated : false,\r
59010     sourceEditMode : false,\r
59011     onFocus : Ext.emptyFn,\r
59012     iframePad:3,\r
59013     hideMode:'offsets',\r
59014     defaultAutoCreate : {\r
59015         tag: "textarea",\r
59016         style:"width:500px;height:300px;",\r
59017         autocomplete: "off"\r
59018     },\r
59019 \r
59020     // private\r
59021     initComponent : function(){\r
59022         this.addEvents(\r
59023             /**\r
59024              * @event initialize\r
59025              * Fires when the editor is fully initialized (including the iframe)\r
59026              * @param {HtmlEditor} this\r
59027              */\r
59028             'initialize',\r
59029             /**\r
59030              * @event activate\r
59031              * Fires when the editor is first receives the focus. Any insertion must wait\r
59032              * until after this event.\r
59033              * @param {HtmlEditor} this\r
59034              */\r
59035             'activate',\r
59036              /**\r
59037              * @event beforesync\r
59038              * Fires before the textarea is updated with content from the editor iframe. Return false\r
59039              * to cancel the sync.\r
59040              * @param {HtmlEditor} this\r
59041              * @param {String} html\r
59042              */\r
59043             'beforesync',\r
59044              /**\r
59045              * @event beforepush\r
59046              * Fires before the iframe editor is updated with content from the textarea. Return false\r
59047              * to cancel the push.\r
59048              * @param {HtmlEditor} this\r
59049              * @param {String} html\r
59050              */\r
59051             'beforepush',\r
59052              /**\r
59053              * @event sync\r
59054              * Fires when the textarea is updated with content from the editor iframe.\r
59055              * @param {HtmlEditor} this\r
59056              * @param {String} html\r
59057              */\r
59058             'sync',\r
59059              /**\r
59060              * @event push\r
59061              * Fires when the iframe editor is updated with content from the textarea.\r
59062              * @param {HtmlEditor} this\r
59063              * @param {String} html\r
59064              */\r
59065             'push',\r
59066              /**\r
59067              * @event editmodechange\r
59068              * Fires when the editor switches edit modes\r
59069              * @param {HtmlEditor} this\r
59070              * @param {Boolean} sourceEdit True if source edit, false if standard editing.\r
59071              */\r
59072             'editmodechange'\r
59073         )\r
59074     },\r
59075 \r
59076     // private\r
59077     createFontOptions : function(){\r
59078         var buf = [], fs = this.fontFamilies, ff, lc;\r
59079         for(var i = 0, len = fs.length; i< len; i++){\r
59080             ff = fs[i];\r
59081             lc = ff.toLowerCase();\r
59082             buf.push(\r
59083                 '<option value="',lc,'" style="font-family:',ff,';"',\r
59084                     (this.defaultFont == lc ? ' selected="true">' : '>'),\r
59085                     ff,\r
59086                 '</option>'\r
59087             );\r
59088         }\r
59089         return buf.join('');\r
59090     },\r
59091     \r
59092     /*\r
59093      * Protected method that will not generally be called directly. It\r
59094      * is called when the editor creates its toolbar. Override this method if you need to\r
59095      * add custom toolbar buttons.\r
59096      * @param {HtmlEditor} editor\r
59097      */\r
59098     createToolbar : function(editor){\r
59099         \r
59100         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();\r
59101         \r
59102         function btn(id, toggle, handler){\r
59103             return {\r
59104                 itemId : id,\r
59105                 cls : 'x-btn-icon',\r
59106                 iconCls: 'x-edit-'+id,\r
59107                 enableToggle:toggle !== false,\r
59108                 scope: editor,\r
59109                 handler:handler||editor.relayBtnCmd,\r
59110                 clickEvent:'mousedown',\r
59111                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,\r
59112                 overflowText: editor.buttonTips[id].title || undefined,\r
59113                 tabIndex:-1\r
59114             };\r
59115         }\r
59116 \r
59117         // build the toolbar\r
59118         var tb = new Ext.Toolbar({\r
59119             renderTo:this.wrap.dom.firstChild\r
59120         });\r
59121 \r
59122         // stop form submits\r
59123         this.mon(tb.el, 'click', function(e){\r
59124             e.preventDefault();\r
59125         });\r
59126 \r
59127         if(this.enableFont && !Ext.isSafari2){\r
59128             this.fontSelect = tb.el.createChild({\r
59129                 tag:'select',\r
59130                 cls:'x-font-select',\r
59131                 html: this.createFontOptions()\r
59132             });\r
59133             this.mon(this.fontSelect, 'change', function(){\r
59134                 var font = this.fontSelect.dom.value;\r
59135                 this.relayCmd('fontname', font);\r
59136                 this.deferFocus();\r
59137             }, this);\r
59138 \r
59139             tb.add(\r
59140                 this.fontSelect.dom,\r
59141                 '-'\r
59142             );\r
59143         }\r
59144 \r
59145         if(this.enableFormat){\r
59146             tb.add(\r
59147                 btn('bold'),\r
59148                 btn('italic'),\r
59149                 btn('underline')\r
59150             );\r
59151         }\r
59152 \r
59153         if(this.enableFontSize){\r
59154             tb.add(\r
59155                 '-',\r
59156                 btn('increasefontsize', false, this.adjustFont),\r
59157                 btn('decreasefontsize', false, this.adjustFont)\r
59158             );\r
59159         }\r
59160 \r
59161         if(this.enableColors){\r
59162             tb.add(\r
59163                 '-', {\r
59164                     itemId:'forecolor',\r
59165                     cls:'x-btn-icon',\r
59166                     iconCls: 'x-edit-forecolor',\r
59167                     clickEvent:'mousedown',\r
59168                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,\r
59169                     tabIndex:-1,\r
59170                     menu : new Ext.menu.ColorMenu({\r
59171                         allowReselect: true,\r
59172                         focus: Ext.emptyFn,\r
59173                         value:'000000',\r
59174                         plain:true,\r
59175                         listeners: {\r
59176                             scope: this,\r
59177                             select: function(cp, color){\r
59178                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);\r
59179                                 this.deferFocus();\r
59180                             }\r
59181                         },\r
59182                         clickEvent:'mousedown'\r
59183                     })\r
59184                 }, {\r
59185                     itemId:'backcolor',\r
59186                     cls:'x-btn-icon',\r
59187                     iconCls: 'x-edit-backcolor',\r
59188                     clickEvent:'mousedown',\r
59189                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,\r
59190                     tabIndex:-1,\r
59191                     menu : new Ext.menu.ColorMenu({\r
59192                         focus: Ext.emptyFn,\r
59193                         value:'FFFFFF',\r
59194                         plain:true,\r
59195                         allowReselect: true,\r
59196                         listeners: {\r
59197                             scope: this,\r
59198                             select: function(cp, color){\r
59199                                 if(Ext.isGecko){\r
59200                                     this.execCmd('useCSS', false);\r
59201                                     this.execCmd('hilitecolor', color);\r
59202                                     this.execCmd('useCSS', true);\r
59203                                     this.deferFocus();\r
59204                                 }else{\r
59205                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);\r
59206                                     this.deferFocus();\r
59207                                 }\r
59208                             }\r
59209                         },\r
59210                         clickEvent:'mousedown'\r
59211                     })\r
59212                 }\r
59213             );\r
59214         }\r
59215 \r
59216         if(this.enableAlignments){\r
59217             tb.add(\r
59218                 '-',\r
59219                 btn('justifyleft'),\r
59220                 btn('justifycenter'),\r
59221                 btn('justifyright')\r
59222             );\r
59223         }\r
59224 \r
59225         if(!Ext.isSafari2){\r
59226             if(this.enableLinks){\r
59227                 tb.add(\r
59228                     '-',\r
59229                     btn('createlink', false, this.createLink)\r
59230                 );\r
59231             }\r
59232 \r
59233             if(this.enableLists){\r
59234                 tb.add(\r
59235                     '-',\r
59236                     btn('insertorderedlist'),\r
59237                     btn('insertunorderedlist')\r
59238                 );\r
59239             }\r
59240             if(this.enableSourceEdit){\r
59241                 tb.add(\r
59242                     '-',\r
59243                     btn('sourceedit', true, function(btn){\r
59244                         this.toggleSourceEdit(!this.sourceEditMode);\r
59245                     })\r
59246                 );\r
59247             }\r
59248         }\r
59249 \r
59250         this.tb = tb;\r
59251     },\r
59252 \r
59253     /**\r
59254      * Protected method that will not generally be called directly. It\r
59255      * is called when the editor initializes the iframe with HTML contents. Override this method if you\r
59256      * want to change the initialization markup of the iframe (e.g. to add stylesheets).\r
59257      */\r
59258     getDocMarkup : function(){\r
59259         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';\r
59260     },\r
59261 \r
59262     // private\r
59263     getEditorBody : function(){\r
59264         return this.doc.body || this.doc.documentElement;\r
59265     },\r
59266 \r
59267     // private\r
59268     getDoc : function(){\r
59269         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);\r
59270     },\r
59271 \r
59272     // private\r
59273     getWin : function(){\r
59274         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];\r
59275     },\r
59276 \r
59277     // private\r
59278     onRender : function(ct, position){\r
59279         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);\r
59280         this.el.dom.style.border = '0 none';\r
59281         this.el.dom.setAttribute('tabIndex', -1);\r
59282         this.el.addClass('x-hidden');\r
59283         if(Ext.isIE){ // fix IE 1px bogus margin\r
59284             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')\r
59285         }\r
59286         this.wrap = this.el.wrap({\r
59287             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}\r
59288         });\r
59289 \r
59290         this.createToolbar(this);\r
59291 \r
59292         this.disableItems(true);\r
59293         // is this needed?\r
59294         // this.tb.doLayout();\r
59295 \r
59296         this.createIFrame();\r
59297 \r
59298         if(!this.width){\r
59299             var sz = this.el.getSize();\r
59300             this.setSize(sz.width, this.height || sz.height);\r
59301         }\r
59302         this.resizeEl = this.positionEl = this.wrap;\r
59303     },\r
59304 \r
59305     createIFrame: function(){\r
59306         var iframe = document.createElement('iframe');\r
59307         iframe.name = Ext.id();\r
59308         iframe.frameBorder = '0';\r
59309         iframe.src = Ext.SSL_SECURE_URL;\r
59310         this.wrap.dom.appendChild(iframe);\r
59311 \r
59312         this.iframe = iframe;\r
59313 \r
59314         this.monitorTask = Ext.TaskMgr.start({\r
59315             run: this.checkDesignMode,\r
59316             scope: this,\r
59317             interval:100\r
59318         });\r
59319     },\r
59320 \r
59321     initFrame : function(){\r
59322         Ext.TaskMgr.stop(this.monitorTask);\r
59323         this.doc = this.getDoc();\r
59324         this.win = this.getWin();\r
59325 \r
59326         this.doc.open();\r
59327         this.doc.write(this.getDocMarkup());\r
59328         this.doc.close();\r
59329 \r
59330         var task = { // must defer to wait for browser to be ready\r
59331             run : function(){\r
59332                 if(this.doc.body || this.doc.readyState == 'complete'){\r
59333                     Ext.TaskMgr.stop(task);\r
59334                     this.doc.designMode="on";\r
59335                     this.initEditor.defer(10, this);\r
59336                 }\r
59337             },\r
59338             interval : 10,\r
59339             duration:10000,\r
59340             scope: this\r
59341         };\r
59342         Ext.TaskMgr.start(task);\r
59343     },\r
59344 \r
59345 \r
59346     checkDesignMode : function(){\r
59347         if(this.wrap && this.wrap.dom.offsetWidth){\r
59348             var doc = this.getDoc();\r
59349             if(!doc){\r
59350                 return;\r
59351             }\r
59352             if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){\r
59353                 this.initFrame();\r
59354             }\r
59355         }\r
59356     },\r
59357     \r
59358     disableItems: function(disabled){\r
59359         if(this.fontSelect){\r
59360             this.fontSelect.dom.disabled = disabled;\r
59361         }\r
59362         this.tb.items.each(function(item){\r
59363             if(item.getItemId() != 'sourceedit'){\r
59364                 item.setDisabled(disabled);\r
59365             }\r
59366         });\r
59367     },\r
59368 \r
59369     // private\r
59370     onResize : function(w, h){\r
59371         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);\r
59372         if(this.el && this.iframe){\r
59373             if(Ext.isNumber(w)){\r
59374                 var aw = w - this.wrap.getFrameWidth('lr');\r
59375                 this.el.setWidth(aw);\r
59376                 this.tb.setWidth(aw);\r
59377                 this.iframe.style.width = Math.max(aw, 0) + 'px';\r
59378             }\r
59379             if(Ext.isNumber(h)){\r
59380                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();\r
59381                 this.el.setHeight(ah);\r
59382                 this.iframe.style.height = Math.max(ah, 0) + 'px';\r
59383                 if(this.doc){\r
59384                     this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';\r
59385                 }\r
59386             }\r
59387         }\r
59388     },\r
59389 \r
59390     /**\r
59391      * Toggles the editor between standard and source edit mode.\r
59392      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard\r
59393      */\r
59394     toggleSourceEdit : function(sourceEditMode){\r
59395         if(sourceEditMode === undefined){\r
59396             sourceEditMode = !this.sourceEditMode;\r
59397         }\r
59398         this.sourceEditMode = sourceEditMode === true;\r
59399         var btn = this.tb.items.get('sourceedit');\r
59400         if(btn.pressed !== this.sourceEditMode){\r
59401             btn.toggle(this.sourceEditMode);\r
59402             if(!btn.xtbHidden){\r
59403                 return;\r
59404             }\r
59405         }\r
59406         if(this.sourceEditMode){\r
59407             this.disableItems(true);\r
59408             this.syncValue();\r
59409             this.iframe.className = 'x-hidden';\r
59410             this.el.removeClass('x-hidden');\r
59411             this.el.dom.removeAttribute('tabIndex');\r
59412             this.el.focus();\r
59413         }else{\r
59414             if(this.initialized){\r
59415                 this.disableItems(false);\r
59416             }\r
59417             this.pushValue();\r
59418             this.iframe.className = '';\r
59419             this.el.addClass('x-hidden');\r
59420             this.el.dom.setAttribute('tabIndex', -1);\r
59421             this.deferFocus();\r
59422         }\r
59423         var lastSize = this.lastSize;\r
59424         if(lastSize){\r
59425             delete this.lastSize;\r
59426             this.setSize(lastSize);\r
59427         }\r
59428         this.fireEvent('editmodechange', this, this.sourceEditMode);\r
59429     },\r
59430 \r
59431     // private used internally\r
59432     createLink : function(){\r
59433         var url = prompt(this.createLinkText, this.defaultLinkValue);\r
59434         if(url && url != 'http:/'+'/'){\r
59435             this.relayCmd('createlink', url);\r
59436         }\r
59437     },\r
59438 \r
59439     // private\r
59440     initEvents : function(){\r
59441         this.originalValue = this.getValue();\r
59442     },\r
59443 \r
59444     /**\r
59445      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide\r
59446      * @method\r
59447      */\r
59448     markInvalid : Ext.emptyFn,\r
59449     \r
59450     /**\r
59451      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide\r
59452      * @method\r
59453      */\r
59454     clearInvalid : Ext.emptyFn,\r
59455 \r
59456     // docs inherit from Field\r
59457     setValue : function(v){\r
59458         Ext.form.HtmlEditor.superclass.setValue.call(this, v);\r
59459         this.pushValue();\r
59460         return this;\r
59461     },\r
59462 \r
59463     /**\r
59464      * Protected method that will not generally be called directly. If you need/want\r
59465      * custom HTML cleanup, this is the method you should override.\r
59466      * @param {String} html The HTML to be cleaned\r
59467      * @return {String} The cleaned HTML\r
59468      */\r
59469     cleanHtml: function(html) {\r
59470         html = String(html);\r
59471         if(Ext.isWebKit){ // strip safari nonsense\r
59472             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');\r
59473         }\r
59474         \r
59475         /*\r
59476          * Neat little hack. Strips out all the non-digit characters from the default\r
59477          * value and compares it to the character code of the first character in the string\r
59478          * because it can cause encoding issues when posted to the server.\r
59479          */\r
59480         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){\r
59481             html = html.substring(1);\r
59482         }\r
59483         return html;\r
59484     },\r
59485 \r
59486     /**\r
59487      * Protected method that will not generally be called directly. Syncs the contents\r
59488      * of the editor iframe with the textarea.\r
59489      */\r
59490     syncValue : function(){\r
59491         if(this.initialized){\r
59492             var bd = this.getEditorBody();\r
59493             var html = bd.innerHTML;\r
59494             if(Ext.isWebKit){\r
59495                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!\r
59496                 var m = bs.match(/text-align:(.*?);/i);\r
59497                 if(m && m[1]){\r
59498                     html = '<div style="'+m[0]+'">' + html + '</div>';\r
59499                 }\r
59500             }\r
59501             html = this.cleanHtml(html);\r
59502             if(this.fireEvent('beforesync', this, html) !== false){\r
59503                 this.el.dom.value = html;\r
59504                 this.fireEvent('sync', this, html);\r
59505             }\r
59506         }\r
59507     },\r
59508     \r
59509     //docs inherit from Field\r
59510     getValue : function() {\r
59511         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();\r
59512         return Ext.form.HtmlEditor.superclass.getValue.call(this);\r
59513     },\r
59514 \r
59515     /**\r
59516      * Protected method that will not generally be called directly. Pushes the value of the textarea\r
59517      * into the iframe editor.\r
59518      */\r
59519     pushValue : function(){\r
59520         if(this.initialized){\r
59521             var v = this.el.dom.value;\r
59522             if(!this.activated && v.length < 1){\r
59523                 v = this.defaultValue;\r
59524             }\r
59525             if(this.fireEvent('beforepush', this, v) !== false){\r
59526                 this.getEditorBody().innerHTML = v;\r
59527                 if(Ext.isGecko){\r
59528                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8\r
59529                     var d = this.doc,\r
59530                         mode = d.designMode.toLowerCase();\r
59531                     \r
59532                     d.designMode = mode.toggle('on', 'off');\r
59533                     d.designMode = mode;\r
59534                 }\r
59535                 this.fireEvent('push', this, v);\r
59536             }\r
59537         }\r
59538     },\r
59539 \r
59540     // private\r
59541     deferFocus : function(){\r
59542         this.focus.defer(10, this);\r
59543     },\r
59544 \r
59545     // docs inherit from Field\r
59546     focus : function(){\r
59547         if(this.win && !this.sourceEditMode){\r
59548             this.win.focus();\r
59549         }else{\r
59550             this.el.focus();\r
59551         }\r
59552     },\r
59553 \r
59554     // private\r
59555     initEditor : function(){\r
59556         //Destroying the component during/before initEditor can cause issues.\r
59557         try{\r
59558             var dbody = this.getEditorBody();\r
59559             var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');\r
59560             ss['background-attachment'] = 'fixed'; // w3c\r
59561             dbody.bgProperties = 'fixed'; // ie\r
59562 \r
59563             Ext.DomHelper.applyStyles(dbody, ss);\r
59564 \r
59565             if(this.doc){\r
59566                 try{\r
59567                     Ext.EventManager.removeAll(this.doc);\r
59568                 }catch(e){}\r
59569             }\r
59570 \r
59571             this.doc = this.getDoc();\r
59572 \r
59573             Ext.EventManager.on(this.doc, {\r
59574                 'mousedown': this.onEditorEvent,\r
59575                 'dblclick': this.onEditorEvent,\r
59576                 'click': this.onEditorEvent,\r
59577                 'keyup': this.onEditorEvent,\r
59578                 buffer:100,\r
59579                 scope: this\r
59580             });\r
59581 \r
59582             if(Ext.isGecko){\r
59583                 Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this);\r
59584             }\r
59585             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){\r
59586                 Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this);\r
59587             }\r
59588             this.initialized = true;\r
59589             this.fireEvent('initialize', this);\r
59590             this.doc.editorInitialized = true;\r
59591             this.pushValue();\r
59592         }catch(e){}\r
59593     },\r
59594 \r
59595     // private\r
59596     onDestroy : function(){\r
59597         if(this.monitorTask){\r
59598             Ext.TaskMgr.stop(this.monitorTask);\r
59599         }\r
59600         if(this.rendered){\r
59601             Ext.destroy(this.tb);\r
59602             if(this.wrap){\r
59603                 this.wrap.dom.innerHTML = '';\r
59604                 this.wrap.remove();\r
59605             }\r
59606         }\r
59607         if(this.el){\r
59608             this.el.removeAllListeners();\r
59609             this.el.remove();\r
59610         }\r
59611  \r
59612         if(this.doc){\r
59613             try{\r
59614                 Ext.EventManager.removeAll(this.doc);\r
59615                 for (var prop in this.doc){\r
59616                    delete this.doc[prop];\r
59617                 }\r
59618             }catch(e){}\r
59619         }\r
59620         this.purgeListeners();\r
59621     },\r
59622 \r
59623     // private\r
59624     onFirstFocus : function(){\r
59625         this.activated = true;\r
59626         this.disableItems(false);\r
59627         if(Ext.isGecko){ // prevent silly gecko errors\r
59628             this.win.focus();\r
59629             var s = this.win.getSelection();\r
59630             if(!s.focusNode || s.focusNode.nodeType != 3){\r
59631                 var r = s.getRangeAt(0);\r
59632                 r.selectNodeContents(this.getEditorBody());\r
59633                 r.collapse(true);\r
59634                 this.deferFocus();\r
59635             }\r
59636             try{\r
59637                 this.execCmd('useCSS', true);\r
59638                 this.execCmd('styleWithCSS', false);\r
59639             }catch(e){}\r
59640         }\r
59641         this.fireEvent('activate', this);\r
59642     },\r
59643 \r
59644     // private\r
59645     adjustFont: function(btn){\r
59646         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1;\r
59647 \r
59648         var v = parseInt(this.doc.queryCommandValue('FontSize') || 2, 10);\r
59649         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){\r
59650             // Safari 3 values\r
59651             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px\r
59652             if(v <= 10){\r
59653                 v = 1 + adjust;\r
59654             }else if(v <= 13){\r
59655                 v = 2 + adjust;\r
59656             }else if(v <= 16){\r
59657                 v = 3 + adjust;\r
59658             }else if(v <= 18){\r
59659                 v = 4 + adjust;\r
59660             }else if(v <= 24){\r
59661                 v = 5 + adjust;\r
59662             }else {\r
59663                 v = 6 + adjust;\r
59664             }\r
59665             v = v.constrain(1, 6);\r
59666         }else{\r
59667             if(Ext.isSafari){ // safari\r
59668                 adjust *= 2;\r
59669             }\r
59670             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);\r
59671         }\r
59672         this.execCmd('FontSize', v);\r
59673     },\r
59674 \r
59675     // private\r
59676     onEditorEvent : function(e){\r
59677         this.updateToolbar();\r
59678     },\r
59679 \r
59680 \r
59681     /**\r
59682      * Protected method that will not generally be called directly. It triggers\r
59683      * a toolbar update by reading the markup state of the current selection in the editor.\r
59684      */\r
59685     updateToolbar: function(){\r
59686 \r
59687         if(!this.activated){\r
59688             this.onFirstFocus();\r
59689             return;\r
59690         }\r
59691 \r
59692         var btns = this.tb.items.map, doc = this.doc;\r
59693 \r
59694         if(this.enableFont && !Ext.isSafari2){\r
59695             var name = (this.doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();\r
59696             if(name != this.fontSelect.dom.value){\r
59697                 this.fontSelect.dom.value = name;\r
59698             }\r
59699         }\r
59700         if(this.enableFormat){\r
59701             btns.bold.toggle(doc.queryCommandState('bold'));\r
59702             btns.italic.toggle(doc.queryCommandState('italic'));\r
59703             btns.underline.toggle(doc.queryCommandState('underline'));\r
59704         }\r
59705         if(this.enableAlignments){\r
59706             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));\r
59707             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));\r
59708             btns.justifyright.toggle(doc.queryCommandState('justifyright'));\r
59709         }\r
59710         if(!Ext.isSafari2 && this.enableLists){\r
59711             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));\r
59712             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));\r
59713         }\r
59714         \r
59715         Ext.menu.MenuMgr.hideAll();\r
59716 \r
59717         this.syncValue();\r
59718     },\r
59719 \r
59720     // private\r
59721     relayBtnCmd : function(btn){\r
59722         this.relayCmd(btn.getItemId());\r
59723     },\r
59724 \r
59725     /**\r
59726      * Executes a Midas editor command on the editor document and performs necessary focus and\r
59727      * toolbar updates. <b>This should only be called after the editor is initialized.</b>\r
59728      * @param {String} cmd The Midas command\r
59729      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)\r
59730      */\r
59731     relayCmd : function(cmd, value){\r
59732         (function(){\r
59733             this.focus();\r
59734             this.execCmd(cmd, value);\r
59735             this.updateToolbar();\r
59736         }).defer(10, this);\r
59737     },\r
59738 \r
59739     /**\r
59740      * Executes a Midas editor command directly on the editor document.\r
59741      * For visual commands, you should use {@link #relayCmd} instead.\r
59742      * <b>This should only be called after the editor is initialized.</b>\r
59743      * @param {String} cmd The Midas command\r
59744      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)\r
59745      */\r
59746     execCmd : function(cmd, value){\r
59747         this.doc.execCommand(cmd, false, value === undefined ? null : value);\r
59748         this.syncValue();\r
59749     },\r
59750 \r
59751     // private\r
59752     applyCommand : function(e){\r
59753         if(e.ctrlKey){\r
59754             var c = e.getCharCode(), cmd;\r
59755             if(c > 0){\r
59756                 c = String.fromCharCode(c);\r
59757                 switch(c){\r
59758                     case 'b':\r
59759                         cmd = 'bold';\r
59760                     break;\r
59761                     case 'i':\r
59762                         cmd = 'italic';\r
59763                     break;\r
59764                     case 'u':\r
59765                         cmd = 'underline';\r
59766                     break;\r
59767                 }\r
59768                 if(cmd){\r
59769                     this.win.focus();\r
59770                     this.execCmd(cmd);\r
59771                     this.deferFocus();\r
59772                     e.preventDefault();\r
59773                 }\r
59774             }\r
59775         }\r
59776     },\r
59777 \r
59778     /**\r
59779      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated\r
59780      * to insert text.\r
59781      * @param {String} text\r
59782      */\r
59783     insertAtCursor : function(text){\r
59784         if(!this.activated){\r
59785             return;\r
59786         }\r
59787         if(Ext.isIE){\r
59788             this.win.focus();\r
59789             var r = this.doc.selection.createRange();\r
59790             if(r){\r
59791                 r.collapse(true);\r
59792                 r.pasteHTML(text);\r
59793                 this.syncValue();\r
59794                 this.deferFocus();\r
59795             }\r
59796         }else{\r
59797             this.win.focus();\r
59798             this.execCmd('InsertHTML', text);\r
59799             this.deferFocus();\r
59800         }\r
59801     },\r
59802 \r
59803     // private\r
59804     fixKeys : function(){ // load time branching for fastest keydown performance\r
59805         if(Ext.isIE){\r
59806             return function(e){\r
59807                 var k = e.getKey(), r;\r
59808                 if(k == e.TAB){\r
59809                     e.stopEvent();\r
59810                     r = this.doc.selection.createRange();\r
59811                     if(r){\r
59812                         r.collapse(true);\r
59813                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');\r
59814                         this.deferFocus();\r
59815                     }\r
59816                 }else if(k == e.ENTER){\r
59817                     r = this.doc.selection.createRange();\r
59818                     if(r){\r
59819                         var target = r.parentElement();\r
59820                         if(!target || target.tagName.toLowerCase() != 'li'){\r
59821                             e.stopEvent();\r
59822                             r.pasteHTML('<br />');\r
59823                             r.collapse(false);\r
59824                             r.select();\r
59825                         }\r
59826                     }\r
59827                 }\r
59828             };\r
59829         }else if(Ext.isOpera){\r
59830             return function(e){\r
59831                 var k = e.getKey();\r
59832                 if(k == e.TAB){\r
59833                     e.stopEvent();\r
59834                     this.win.focus();\r
59835                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');\r
59836                     this.deferFocus();\r
59837                 }\r
59838             };\r
59839         }else if(Ext.isWebKit){\r
59840             return function(e){\r
59841                 var k = e.getKey();\r
59842                 if(k == e.TAB){\r
59843                     e.stopEvent();\r
59844                     this.execCmd('InsertText','\t');\r
59845                     this.deferFocus();\r
59846                 }else if(k == e.ENTER){\r
59847                     e.stopEvent();\r
59848                     this.execCmd('InsertHtml','<br /><br />');\r
59849                     this.deferFocus();\r
59850                 }\r
59851              };\r
59852         }\r
59853     }(),\r
59854 \r
59855     /**\r
59856      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>\r
59857      * @return {Ext.Toolbar}\r
59858      */\r
59859     getToolbar : function(){\r
59860         return this.tb;\r
59861     },\r
59862 \r
59863     /**\r
59864      * Object collection of toolbar tooltips for the buttons in the editor. The key\r
59865      * is the command id associated with that button and the value is a valid QuickTips object.\r
59866      * For example:\r
59867 <pre><code>\r
59868 {\r
59869     bold : {\r
59870         title: 'Bold (Ctrl+B)',\r
59871         text: 'Make the selected text bold.',\r
59872         cls: 'x-html-editor-tip'\r
59873     },\r
59874     italic : {\r
59875         title: 'Italic (Ctrl+I)',\r
59876         text: 'Make the selected text italic.',\r
59877         cls: 'x-html-editor-tip'\r
59878     },\r
59879     ...\r
59880 </code></pre>\r
59881     * @type Object\r
59882      */\r
59883     buttonTips : {\r
59884         bold : {\r
59885             title: 'Bold (Ctrl+B)',\r
59886             text: 'Make the selected text bold.',\r
59887             cls: 'x-html-editor-tip'\r
59888         },\r
59889         italic : {\r
59890             title: 'Italic (Ctrl+I)',\r
59891             text: 'Make the selected text italic.',\r
59892             cls: 'x-html-editor-tip'\r
59893         },\r
59894         underline : {\r
59895             title: 'Underline (Ctrl+U)',\r
59896             text: 'Underline the selected text.',\r
59897             cls: 'x-html-editor-tip'\r
59898         },\r
59899         increasefontsize : {\r
59900             title: 'Grow Text',\r
59901             text: 'Increase the font size.',\r
59902             cls: 'x-html-editor-tip'\r
59903         },\r
59904         decreasefontsize : {\r
59905             title: 'Shrink Text',\r
59906             text: 'Decrease the font size.',\r
59907             cls: 'x-html-editor-tip'\r
59908         },\r
59909         backcolor : {\r
59910             title: 'Text Highlight Color',\r
59911             text: 'Change the background color of the selected text.',\r
59912             cls: 'x-html-editor-tip'\r
59913         },\r
59914         forecolor : {\r
59915             title: 'Font Color',\r
59916             text: 'Change the color of the selected text.',\r
59917             cls: 'x-html-editor-tip'\r
59918         },\r
59919         justifyleft : {\r
59920             title: 'Align Text Left',\r
59921             text: 'Align text to the left.',\r
59922             cls: 'x-html-editor-tip'\r
59923         },\r
59924         justifycenter : {\r
59925             title: 'Center Text',\r
59926             text: 'Center text in the editor.',\r
59927             cls: 'x-html-editor-tip'\r
59928         },\r
59929         justifyright : {\r
59930             title: 'Align Text Right',\r
59931             text: 'Align text to the right.',\r
59932             cls: 'x-html-editor-tip'\r
59933         },\r
59934         insertunorderedlist : {\r
59935             title: 'Bullet List',\r
59936             text: 'Start a bulleted list.',\r
59937             cls: 'x-html-editor-tip'\r
59938         },\r
59939         insertorderedlist : {\r
59940             title: 'Numbered List',\r
59941             text: 'Start a numbered list.',\r
59942             cls: 'x-html-editor-tip'\r
59943         },\r
59944         createlink : {\r
59945             title: 'Hyperlink',\r
59946             text: 'Make the selected text a hyperlink.',\r
59947             cls: 'x-html-editor-tip'\r
59948         },\r
59949         sourceedit : {\r
59950             title: 'Source Edit',\r
59951             text: 'Switch to source editing mode.',\r
59952             cls: 'x-html-editor-tip'\r
59953         }\r
59954     }\r
59955 \r
59956     // hide stuff that is not compatible\r
59957     /**\r
59958      * @event blur\r
59959      * @hide\r
59960      */\r
59961     /**\r
59962      * @event change\r
59963      * @hide\r
59964      */\r
59965     /**\r
59966      * @event focus\r
59967      * @hide\r
59968      */\r
59969     /**\r
59970      * @event specialkey\r
59971      * @hide\r
59972      */\r
59973     /**\r
59974      * @cfg {String} fieldClass @hide\r
59975      */\r
59976     /**\r
59977      * @cfg {String} focusClass @hide\r
59978      */\r
59979     /**\r
59980      * @cfg {String} autoCreate @hide\r
59981      */\r
59982     /**\r
59983      * @cfg {String} inputType @hide\r
59984      */\r
59985     /**\r
59986      * @cfg {String} invalidClass @hide\r
59987      */\r
59988     /**\r
59989      * @cfg {String} invalidText @hide\r
59990      */\r
59991     /**\r
59992      * @cfg {String} msgFx @hide\r
59993      */\r
59994     /**\r
59995      * @cfg {String} validateOnBlur @hide\r
59996      */\r
59997     /**\r
59998      * @cfg {Boolean} allowDomMove  @hide\r
59999      */\r
60000     /**\r
60001      * @cfg {String} applyTo @hide\r
60002      */\r
60003     /**\r
60004      * @cfg {String} autoHeight  @hide\r
60005      */\r
60006     /**\r
60007      * @cfg {String} autoWidth  @hide\r
60008      */\r
60009     /**\r
60010      * @cfg {String} cls  @hide\r
60011      */\r
60012     /**\r
60013      * @cfg {String} disabled  @hide\r
60014      */\r
60015     /**\r
60016      * @cfg {String} disabledClass  @hide\r
60017      */\r
60018     /**\r
60019      * @cfg {String} msgTarget  @hide\r
60020      */\r
60021     /**\r
60022      * @cfg {String} readOnly  @hide\r
60023      */\r
60024     /**\r
60025      * @cfg {String} style  @hide\r
60026      */\r
60027     /**\r
60028      * @cfg {String} validationDelay  @hide\r
60029      */\r
60030     /**\r
60031      * @cfg {String} validationEvent  @hide\r
60032      */\r
60033     /**\r
60034      * @cfg {String} tabIndex  @hide\r
60035      */\r
60036     /**\r
60037      * @property disabled\r
60038      * @hide\r
60039      */\r
60040     /**\r
60041      * @method applyToMarkup\r
60042      * @hide\r
60043      */\r
60044     /**\r
60045      * @method disable\r
60046      * @hide\r
60047      */\r
60048     /**\r
60049      * @method enable\r
60050      * @hide\r
60051      */\r
60052     /**\r
60053      * @method validate\r
60054      * @hide\r
60055      */\r
60056     /**\r
60057      * @event valid\r
60058      * @hide\r
60059      */\r
60060     /**\r
60061      * @method setDisabled\r
60062      * @hide\r
60063      */\r
60064     /**\r
60065      * @cfg keys\r
60066      * @hide\r
60067      */\r
60068 });\r
60069 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**\r
60070  * @class Ext.form.TimeField\r
60071  * @extends Ext.form.ComboBox\r
60072  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:\r
60073  * <pre><code>\r
60074 new Ext.form.TimeField({\r
60075     minValue: '9:00 AM',\r
60076     maxValue: '6:00 PM',\r
60077     increment: 30\r
60078 });\r
60079 </code></pre>\r
60080  * @constructor\r
60081  * Create a new TimeField\r
60082  * @param {Object} config\r
60083  * @xtype timefield\r
60084  */\r
60085 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {\r
60086     /**\r
60087      * @cfg {Date/String} minValue\r
60088      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string \r
60089      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).\r
60090      */\r
60091     minValue : null,\r
60092     /**\r
60093      * @cfg {Date/String} maxValue\r
60094      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string \r
60095      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).\r
60096      */\r
60097     maxValue : null,\r
60098     /**\r
60099      * @cfg {String} minText\r
60100      * The error text to display when the date in the cell is before minValue (defaults to\r
60101      * 'The time in this field must be equal to or after {0}').\r
60102      */\r
60103     minText : "The time in this field must be equal to or after {0}",\r
60104     /**\r
60105      * @cfg {String} maxText\r
60106      * The error text to display when the time is after maxValue (defaults to\r
60107      * 'The time in this field must be equal to or before {0}').\r
60108      */\r
60109     maxText : "The time in this field must be equal to or before {0}",\r
60110     /**\r
60111      * @cfg {String} invalidText\r
60112      * The error text to display when the time in the field is invalid (defaults to\r
60113      * '{value} is not a valid time').\r
60114      */\r
60115     invalidText : "{0} is not a valid time",\r
60116     /**\r
60117      * @cfg {String} format\r
60118      * The default time format string which can be overriden for localization support.  The format must be\r
60119      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time\r
60120      * format try 'H:i' instead.\r
60121      */\r
60122     format : "g:i A",\r
60123     /**\r
60124      * @cfg {String} altFormats\r
60125      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined\r
60126      * 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
60127      */\r
60128     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
60129     /**\r
60130      * @cfg {Number} increment\r
60131      * The number of minutes between each time value in the list (defaults to 15).\r
60132      */\r
60133     increment: 15,\r
60134 \r
60135     // private override\r
60136     mode: 'local',\r
60137     // private override\r
60138     triggerAction: 'all',\r
60139     // private override\r
60140     typeAhead: false,\r
60141     \r
60142     // private - This is the date to use when generating time values in the absence of either minValue\r
60143     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an \r
60144     // arbitrary "safe" date that can be any date aside from DST boundary dates.\r
60145     initDate: '1/1/2008',\r
60146 \r
60147     // private\r
60148     initComponent : function(){\r
60149         if(typeof this.minValue == "string"){\r
60150             this.minValue = this.parseDate(this.minValue);\r
60151         }\r
60152         if(typeof this.maxValue == "string"){\r
60153             this.maxValue = this.parseDate(this.maxValue);\r
60154         }\r
60155 \r
60156         if(!this.store){\r
60157             var min = this.parseDate(this.minValue) || new Date(this.initDate).clearTime();\r
60158             var max = this.parseDate(this.maxValue) || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1);\r
60159             var times = [];\r
60160             while(min <= max){\r
60161                 times.push(min.dateFormat(this.format));\r
60162                 min = min.add('mi', this.increment);\r
60163             }\r
60164             this.store = times;\r
60165         }\r
60166         Ext.form.TimeField.superclass.initComponent.call(this);\r
60167     },\r
60168 \r
60169     // inherited docs\r
60170     getValue : function(){\r
60171         var v = Ext.form.TimeField.superclass.getValue.call(this);\r
60172         return this.formatDate(this.parseDate(v)) || '';\r
60173     },\r
60174 \r
60175     // inherited docs\r
60176     setValue : function(value){\r
60177         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));\r
60178     },\r
60179 \r
60180     // private overrides\r
60181     validateValue : Ext.form.DateField.prototype.validateValue,\r
60182     parseDate : Ext.form.DateField.prototype.parseDate,\r
60183     formatDate : Ext.form.DateField.prototype.formatDate,\r
60184 \r
60185     // private\r
60186     beforeBlur : function(){\r
60187         var v = this.parseDate(this.getRawValue());\r
60188         if(v){\r
60189             this.setValue(v.dateFormat(this.format));\r
60190         }\r
60191         Ext.form.TimeField.superclass.beforeBlur.call(this);\r
60192     }\r
60193 \r
60194     /**\r
60195      * @cfg {Boolean} grow @hide\r
60196      */\r
60197     /**\r
60198      * @cfg {Number} growMin @hide\r
60199      */\r
60200     /**\r
60201      * @cfg {Number} growMax @hide\r
60202      */\r
60203     /**\r
60204      * @hide\r
60205      * @method autoSize\r
60206      */\r
60207 });\r
60208 Ext.reg('timefield', Ext.form.TimeField);/**
60209  * @class Ext.form.Label
60210  * @extends Ext.BoxComponent
60211  * Basic Label field.
60212  * @constructor
60213  * Creates a new Label
60214  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
60215  * 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
60216  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
60217  * @xtype label
60218  */
60219 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
60220     /**
60221      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
60222      * tags within the label's innerHTML, use the {@link #html} config instead.
60223      */
60224     /**
60225      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
60226      * attribute. If not specified, the attribute will not be added to the label.
60227      */
60228     /**
60229      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
60230      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
60231      */
60232
60233     // private
60234     onRender : function(ct, position){
60235         if(!this.el){
60236             this.el = document.createElement('label');
60237             this.el.id = this.getId();
60238             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
60239             if(this.forId){
60240                 this.el.setAttribute('for', this.forId);
60241             }
60242         }
60243         Ext.form.Label.superclass.onRender.call(this, ct, position);
60244     },
60245
60246     /**
60247      * Updates the label's innerHTML with the specified string.
60248      * @param {String} text The new label text
60249      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
60250      * to the label (defaults to true which encodes the value). This might be useful if you want to include
60251      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
60252      * @return {Label} this
60253      */
60254     setText : function(t, encode){
60255         var e = encode === false;
60256         this[!e ? 'text' : 'html'] = t;
60257         delete this[e ? 'text' : 'html'];
60258         if(this.rendered){
60259             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
60260         }
60261         return this;
60262     }
60263 });
60264
60265 Ext.reg('label', Ext.form.Label);/**\r
60266  * @class Ext.form.Action\r
60267  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>\r
60268  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when\r
60269  * the Form needs to perform an action such as submit or load. The Configuration options\r
60270  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},\r
60271  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>\r
60272  * <p>The instance of Action which performed the action is passed to the success\r
60273  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},\r
60274  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),\r
60275  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and\r
60276  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>\r
60277  */\r
60278 Ext.form.Action = function(form, options){\r
60279     this.form = form;\r
60280     this.options = options || {};\r
60281 };\r
60282 \r
60283 /**\r
60284  * Failure type returned when client side validation of the Form fails\r
60285  * thus aborting a submit action. Client side validation is performed unless\r
60286  * {@link #clientValidation} is explicitly set to <tt>false</tt>.\r
60287  * @type {String}\r
60288  * @static\r
60289  */\r
60290 Ext.form.Action.CLIENT_INVALID = 'client';\r
60291 /**\r
60292  * <p>Failure type returned when server side processing fails and the {@link #result}'s\r
60293  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>\r
60294  * <p>In the case of a form submission, field-specific error messages may be returned in the\r
60295  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>\r
60296  * @type {String}\r
60297  * @static\r
60298  */\r
60299 Ext.form.Action.SERVER_INVALID = 'server';\r
60300 /**\r
60301  * Failure type returned when a communication error happens when attempting\r
60302  * to send a request to the remote server. The {@link #response} may be examined to\r
60303  * provide further information.\r
60304  * @type {String}\r
60305  * @static\r
60306  */\r
60307 Ext.form.Action.CONNECT_FAILURE = 'connect';\r
60308 /**\r
60309  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>\r
60310  * property is set to <tt>false</tt>, or no field values are returned in the response's\r
60311  * <tt style="font-weight:bold">data</tt> property.\r
60312  * @type {String}\r
60313  * @static\r
60314  */\r
60315 Ext.form.Action.LOAD_FAILURE = 'load';\r
60316 \r
60317 Ext.form.Action.prototype = {\r
60318 /**\r
60319  * @cfg {String} url The URL that the Action is to invoke.\r
60320  */\r
60321 /**\r
60322  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be\r
60323  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens\r
60324  * <b>before</b> the {@link #success} callback is called and before the Form's\r
60325  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.\r
60326  */\r
60327 /**\r
60328  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the\r
60329  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.\r
60330  */\r
60331 /**\r
60332  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's\r
60333  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's\r
60334  * input fields.</p>\r
60335  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>\r
60336  */\r
60337 /**\r
60338  * @cfg {Number} timeout The number of seconds to wait for a server response before\r
60339  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,\r
60340  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the\r
60341  * {@link Ext.form.BasicForm form}.\r
60342  */\r
60343 /**\r
60344  * @cfg {Function} success The function to call when a valid success return packet is recieved.\r
60345  * The function is passed the following parameters:<ul class="mdetail-params">\r
60346  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>\r
60347  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}\r
60348  * property of this object may be examined to perform custom postprocessing.</div></li>\r
60349  * </ul>\r
60350  */\r
60351 /**\r
60352  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an\r
60353  * error ocurred in the Ajax communication.\r
60354  * The function is passed the following parameters:<ul class="mdetail-params">\r
60355  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>\r
60356  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax\r
60357  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}\r
60358  * property of this object may be examined to perform custom postprocessing.</div></li>\r
60359  * </ul>\r
60360  */\r
60361 /**\r
60362  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference\r
60363  * for the callback functions).\r
60364  */\r
60365 /**\r
60366  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}\r
60367  * during the time the action is being processed.\r
60368  */\r
60369 /**\r
60370  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}\r
60371  * during the time the action is being processed.\r
60372  */\r
60373 \r
60374 /**\r
60375  * The type of action this Action instance performs.\r
60376  * Currently only "submit" and "load" are supported.\r
60377  * @type {String}\r
60378  */\r
60379     type : 'default',\r
60380 /**\r
60381  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},\r
60382  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:\r
60383  * <pre><code>\r
60384 var fp = new Ext.form.FormPanel({\r
60385 ...\r
60386 buttons: [{\r
60387     text: 'Save',\r
60388     formBind: true,\r
60389     handler: function(){\r
60390         if(fp.getForm().isValid()){\r
60391             fp.getForm().submit({\r
60392                 url: 'form-submit.php',\r
60393                 waitMsg: 'Submitting your data...',\r
60394                 success: function(form, action){\r
60395                     // server responded with success = true\r
60396                     var result = action.{@link #result};\r
60397                 },\r
60398                 failure: function(form, action){\r
60399                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {\r
60400                         Ext.Msg.alert('Error',\r
60401                             'Status:'+action.{@link #response}.status+': '+\r
60402                             action.{@link #response}.statusText);\r
60403                     }\r
60404                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){\r
60405                         // server responded with success = false\r
60406                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);\r
60407                     }\r
60408                 }\r
60409             });\r
60410         }\r
60411     }\r
60412 },{\r
60413     text: 'Reset',\r
60414     handler: function(){\r
60415         fp.getForm().reset();\r
60416     }\r
60417 }]\r
60418  * </code></pre>\r
60419  * @property failureType\r
60420  * @type {String}\r
60421  */\r
60422  /**\r
60423  * The XMLHttpRequest object used to perform the action.\r
60424  * @property response\r
60425  * @type {Object}\r
60426  */\r
60427  /**\r
60428  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and\r
60429  * other, action-specific properties.\r
60430  * @property result\r
60431  * @type {Object}\r
60432  */\r
60433 \r
60434     // interface method\r
60435     run : function(options){\r
60436 \r
60437     },\r
60438 \r
60439     // interface method\r
60440     success : function(response){\r
60441 \r
60442     },\r
60443 \r
60444     // interface method\r
60445     handleResponse : function(response){\r
60446 \r
60447     },\r
60448 \r
60449     // default connection failure\r
60450     failure : function(response){\r
60451         this.response = response;\r
60452         this.failureType = Ext.form.Action.CONNECT_FAILURE;\r
60453         this.form.afterAction(this, false);\r
60454     },\r
60455 \r
60456     // private\r
60457     // shared code among all Actions to validate that there was a response\r
60458     // with either responseText or responseXml\r
60459     processResponse : function(response){\r
60460         this.response = response;\r
60461         if(!response.responseText && !response.responseXML){\r
60462             return true;\r
60463         }\r
60464         this.result = this.handleResponse(response);\r
60465         return this.result;\r
60466     },\r
60467 \r
60468     // utility functions used internally\r
60469     getUrl : function(appendParams){\r
60470         var url = this.options.url || this.form.url || this.form.el.dom.action;\r
60471         if(appendParams){\r
60472             var p = this.getParams();\r
60473             if(p){\r
60474                 url = Ext.urlAppend(url, p);\r
60475             }\r
60476         }\r
60477         return url;\r
60478     },\r
60479 \r
60480     // private\r
60481     getMethod : function(){\r
60482         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();\r
60483     },\r
60484 \r
60485     // private\r
60486     getParams : function(){\r
60487         var bp = this.form.baseParams;\r
60488         var p = this.options.params;\r
60489         if(p){\r
60490             if(typeof p == "object"){\r
60491                 p = Ext.urlEncode(Ext.applyIf(p, bp));\r
60492             }else if(typeof p == 'string' && bp){\r
60493                 p += '&' + Ext.urlEncode(bp);\r
60494             }\r
60495         }else if(bp){\r
60496             p = Ext.urlEncode(bp);\r
60497         }\r
60498         return p;\r
60499     },\r
60500 \r
60501     // private\r
60502     createCallback : function(opts){\r
60503         var opts = opts || {};\r
60504         return {\r
60505             success: this.success,\r
60506             failure: this.failure,\r
60507             scope: this,\r
60508             timeout: (opts.timeout*1000) || (this.form.timeout*1000),\r
60509             upload: this.form.fileUpload ? this.success : undefined\r
60510         };\r
60511     }\r
60512 };\r
60513 \r
60514 /**\r
60515  * @class Ext.form.Action.Submit\r
60516  * @extends Ext.form.Action\r
60517  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s\r
60518  * and processes the returned response.</p>\r
60519  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when\r
60520  * {@link Ext.form.BasicForm#submit submit}ting.</p>\r
60521  * <p><u><b>Response Packet Criteria</b></u></p>\r
60522  * <p>A response packet may contain:\r
60523  * <div class="mdetail-params"><ul>\r
60524  * <li><b><code>success</code></b> property : Boolean\r
60525  * <div class="sub-desc">The <code>success</code> property is required.</div></li>\r
60526  * <li><b><code>errors</code></b> property : Object\r
60527  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,\r
60528  * which is optional, contains error messages for invalid fields.</div></li>\r
60529  * </ul></div>\r
60530  * <p><u><b>JSON Packets</b></u></p>\r
60531  * <p>By default, response packets are assumed to be JSON, so a typical response\r
60532  * packet may look like this:</p><pre><code>\r
60533 {\r
60534     success: false,\r
60535     errors: {\r
60536         clientCode: "Client not found",\r
60537         portOfLoading: "This field must not be null"\r
60538     }\r
60539 }</code></pre>\r
60540  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback\r
60541  * or event handler methods. The object decoded from this JSON is available in the\r
60542  * {@link Ext.form.Action#result result} property.</p>\r
60543  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>\r
60544     errorReader: new Ext.data.XmlReader({\r
60545             record : 'field',\r
60546             success: '@success'\r
60547         }, [\r
60548             'id', 'msg'\r
60549         ]\r
60550     )\r
60551 </code></pre>\r
60552  * <p>then the results may be sent back in XML format:</p><pre><code>\r
60553 &lt;?xml version="1.0" encoding="UTF-8"?&gt;\r
60554 &lt;message success="false"&gt;\r
60555 &lt;errors&gt;\r
60556     &lt;field&gt;\r
60557         &lt;id&gt;clientCode&lt;/id&gt;\r
60558         &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;\r
60559     &lt;/field&gt;\r
60560     &lt;field&gt;\r
60561         &lt;id&gt;portOfLoading&lt;/id&gt;\r
60562         &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;\r
60563     &lt;/field&gt;\r
60564 &lt;/errors&gt;\r
60565 &lt;/message&gt;\r
60566 </code></pre>\r
60567  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback\r
60568  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>\r
60569  */\r
60570 Ext.form.Action.Submit = function(form, options){\r
60571     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);\r
60572 };\r
60573 \r
60574 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {\r
60575     /**\r
60576      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with\r
60577      * no need for an errorReader.</b></p>\r
60578      * <p>A Reader which reads a single record from the returned data. The DataReader's\r
60579      * <b>success</b> property specifies how submission success is determined. The Record's\r
60580      * data provides the error messages to apply to any invalid form Fields.</p>\r
60581      */\r
60582     /**\r
60583      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated\r
60584      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.\r
60585      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation\r
60586      * is performed.\r
60587      */\r
60588     type : 'submit',\r
60589 \r
60590     // private\r
60591     run : function(){\r
60592         var o = this.options;\r
60593         var method = this.getMethod();\r
60594         var isGet = method == 'GET';\r
60595         if(o.clientValidation === false || this.form.isValid()){\r
60596             Ext.Ajax.request(Ext.apply(this.createCallback(o), {\r
60597                 form:this.form.el.dom,\r
60598                 url:this.getUrl(isGet),\r
60599                 method: method,\r
60600                 headers: o.headers,\r
60601                 params:!isGet ? this.getParams() : null,\r
60602                 isUpload: this.form.fileUpload\r
60603             }));\r
60604         }else if (o.clientValidation !== false){ // client validation failed\r
60605             this.failureType = Ext.form.Action.CLIENT_INVALID;\r
60606             this.form.afterAction(this, false);\r
60607         }\r
60608     },\r
60609 \r
60610     // private\r
60611     success : function(response){\r
60612         var result = this.processResponse(response);\r
60613         if(result === true || result.success){\r
60614             this.form.afterAction(this, true);\r
60615             return;\r
60616         }\r
60617         if(result.errors){\r
60618             this.form.markInvalid(result.errors);\r
60619         }\r
60620         this.failureType = Ext.form.Action.SERVER_INVALID;\r
60621         this.form.afterAction(this, false);\r
60622     },\r
60623 \r
60624     // private\r
60625     handleResponse : function(response){\r
60626         if(this.form.errorReader){\r
60627             var rs = this.form.errorReader.read(response);\r
60628             var errors = [];\r
60629             if(rs.records){\r
60630                 for(var i = 0, len = rs.records.length; i < len; i++) {\r
60631                     var r = rs.records[i];\r
60632                     errors[i] = r.data;\r
60633                 }\r
60634             }\r
60635             if(errors.length < 1){\r
60636                 errors = null;\r
60637             }\r
60638             return {\r
60639                 success : rs.success,\r
60640                 errors : errors\r
60641             };\r
60642         }\r
60643         return Ext.decode(response.responseText);\r
60644     }\r
60645 });\r
60646 \r
60647 \r
60648 /**\r
60649  * @class Ext.form.Action.Load\r
60650  * @extends Ext.form.Action\r
60651  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>\r
60652  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when\r
60653  * {@link Ext.form.BasicForm#load load}ing.</p>\r
60654  * <p><u><b>Response Packet Criteria</b></u></p>\r
60655  * <p>A response packet <b>must</b> contain:\r
60656  * <div class="mdetail-params"><ul>\r
60657  * <li><b><code>success</code></b> property : Boolean</li>\r
60658  * <li><b><code>data</code></b> property : Object</li>\r
60659  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.\r
60660  * The individual value object for each Field is passed to the Field's\r
60661  * {@link Ext.form.Field#setValue setValue} method.</div></li>\r
60662  * </ul></div>\r
60663  * <p><u><b>JSON Packets</b></u></p>\r
60664  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>\r
60665 var myFormPanel = new Ext.form.FormPanel({\r
60666     title: 'Client and routing info',\r
60667     items: [{\r
60668         fieldLabel: 'Client',\r
60669         name: 'clientName'\r
60670     }, {\r
60671         fieldLabel: 'Port of loading',\r
60672         name: 'portOfLoading'\r
60673     }, {\r
60674         fieldLabel: 'Port of discharge',\r
60675         name: 'portOfDischarge'\r
60676     }]\r
60677 });\r
60678 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({\r
60679     url: '/getRoutingInfo.php',\r
60680     params: {\r
60681         consignmentRef: myConsignmentRef\r
60682     },\r
60683     failure: function(form, action) {\r
60684         Ext.Msg.alert("Load failed", action.result.errorMessage);\r
60685     }\r
60686 });\r
60687 </code></pre>\r
60688  * a <b>success response</b> packet may look like this:</p><pre><code>\r
60689 {\r
60690     success: true,\r
60691     data: {\r
60692         clientName: "Fred. Olsen Lines",\r
60693         portOfLoading: "FXT",\r
60694         portOfDischarge: "OSL"\r
60695     }\r
60696 }</code></pre>\r
60697  * while a <b>failure response</b> packet may look like this:</p><pre><code>\r
60698 {\r
60699     success: false,\r
60700     errorMessage: "Consignment reference not found"\r
60701 }</code></pre>\r
60702  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s\r
60703  * callback or event handler methods. The object decoded from this JSON is available in the\r
60704  * {@link Ext.form.Action#result result} property.</p>\r
60705  */\r
60706 Ext.form.Action.Load = function(form, options){\r
60707     Ext.form.Action.Load.superclass.constructor.call(this, form, options);\r
60708     this.reader = this.form.reader;\r
60709 };\r
60710 \r
60711 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {\r
60712     // private\r
60713     type : 'load',\r
60714 \r
60715     // private\r
60716     run : function(){\r
60717         Ext.Ajax.request(Ext.apply(\r
60718                 this.createCallback(this.options), {\r
60719                     method:this.getMethod(),\r
60720                     url:this.getUrl(false),\r
60721                     headers: this.options.headers,\r
60722                     params:this.getParams()\r
60723         }));\r
60724     },\r
60725 \r
60726     // private\r
60727     success : function(response){\r
60728         var result = this.processResponse(response);\r
60729         if(result === true || !result.success || !result.data){\r
60730             this.failureType = Ext.form.Action.LOAD_FAILURE;\r
60731             this.form.afterAction(this, false);\r
60732             return;\r
60733         }\r
60734         this.form.clearInvalid();\r
60735         this.form.setValues(result.data);\r
60736         this.form.afterAction(this, true);\r
60737     },\r
60738 \r
60739     // private\r
60740     handleResponse : function(response){\r
60741         if(this.form.reader){\r
60742             var rs = this.form.reader.read(response);\r
60743             var data = rs.records && rs.records[0] ? rs.records[0].data : null;\r
60744             return {\r
60745                 success : rs.success,\r
60746                 data : data\r
60747             };\r
60748         }\r
60749         return Ext.decode(response.responseText);\r
60750     }\r
60751 });\r
60752 \r
60753 \r
60754 \r
60755 /**\r
60756  * @class Ext.form.Action.DirectLoad\r
60757  * @extends Ext.form.Action.Load\r
60758  * <p>Provides Ext.direct support for loading form data.</p>\r
60759  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>\r
60760  * <pre><code>\r
60761 var myFormPanel = new Ext.form.FormPanel({\r
60762     // configs for FormPanel\r
60763     title: 'Basic Information',\r
60764     renderTo: document.body,\r
60765     width: 300, height: 160,\r
60766     padding: 10,\r
60767 \r
60768     // configs apply to child items\r
60769     defaults: {anchor: '100%'},\r
60770     defaultType: 'textfield',\r
60771     items: [{\r
60772         fieldLabel: 'Name',\r
60773         name: 'name'\r
60774     },{\r
60775         fieldLabel: 'Email',\r
60776         name: 'email'\r
60777     },{\r
60778         fieldLabel: 'Company',\r
60779         name: 'company'\r
60780     }],\r
60781 \r
60782     // configs for BasicForm\r
60783     api: {\r
60784         // The server-side method to call for load() requests\r
60785         load: Profile.getBasicInfo,\r
60786         // The server-side must mark the submit handler as a 'formHandler'\r
60787         submit: Profile.updateBasicInfo\r
60788     },\r
60789     // specify the order for the passed params\r
60790     paramOrder: ['uid', 'foo']\r
60791 });\r
60792 \r
60793 // load the form\r
60794 myFormPanel.getForm().load({\r
60795     // pass 2 arguments to server side getBasicInfo method (len=2)\r
60796     params: {\r
60797         foo: 'bar',\r
60798         uid: 34\r
60799     }\r
60800 });\r
60801  * </code></pre>\r
60802  * The data packet sent to the server will resemble something like:\r
60803  * <pre><code>\r
60804 [\r
60805     {\r
60806         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,\r
60807         "data":[34,"bar"] // note the order of the params\r
60808     }\r
60809 ]\r
60810  * </code></pre>\r
60811  * The form will process a data packet returned by the server that is similar\r
60812  * to the following format:\r
60813  * <pre><code>\r
60814 [\r
60815     {\r
60816         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,\r
60817         "result":{\r
60818             "success":true,\r
60819             "data":{\r
60820                 "name":"Fred Flintstone",\r
60821                 "company":"Slate Rock and Gravel",\r
60822                 "email":"fred.flintstone@slaterg.com"\r
60823             }\r
60824         }\r
60825     }\r
60826 ]\r
60827  * </code></pre>\r
60828  */\r
60829 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {\r
60830     constructor: function(form, opts) {\r
60831         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);\r
60832     },\r
60833     type : 'directload',\r
60834 \r
60835     run : function(){\r
60836         var args = this.getParams();\r
60837         args.push(this.success, this);\r
60838         this.form.api.load.apply(window, args);\r
60839     },\r
60840 \r
60841     getParams : function() {\r
60842         var buf = [], o = {};\r
60843         var bp = this.form.baseParams;\r
60844         var p = this.options.params;\r
60845         Ext.apply(o, p, bp);\r
60846         var paramOrder = this.form.paramOrder;\r
60847         if(paramOrder){\r
60848             for(var i = 0, len = paramOrder.length; i < len; i++){\r
60849                 buf.push(o[paramOrder[i]]);\r
60850             }\r
60851         }else if(this.form.paramsAsHash){\r
60852             buf.push(o);\r
60853         }\r
60854         return buf;\r
60855     },\r
60856     // Direct actions have already been processed and therefore\r
60857     // we can directly set the result; Direct Actions do not have\r
60858     // a this.response property.\r
60859     processResponse : function(result) {\r
60860         this.result = result;\r
60861         return result;\r
60862     },\r
60863     \r
60864     success : function(response, trans){\r
60865         if(trans.type == Ext.Direct.exceptions.SERVER){\r
60866             response = {};\r
60867         }\r
60868         Ext.form.Action.DirectLoad.superclass.success.call(this, response);\r
60869     }\r
60870 });\r
60871 \r
60872 /**\r
60873  * @class Ext.form.Action.DirectSubmit\r
60874  * @extends Ext.form.Action.Submit\r
60875  * <p>Provides Ext.direct support for submitting form data.</p>\r
60876  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>\r
60877  * <pre><code>\r
60878 var myFormPanel = new Ext.form.FormPanel({\r
60879     // configs for FormPanel\r
60880     title: 'Basic Information',\r
60881     renderTo: document.body,\r
60882     width: 300, height: 160,\r
60883     padding: 10,\r
60884     buttons:[{\r
60885         text: 'Submit',\r
60886         handler: function(){\r
60887             myFormPanel.getForm().submit({\r
60888                 params: {\r
60889                     foo: 'bar',\r
60890                     uid: 34\r
60891                 }\r
60892             });\r
60893         }\r
60894     }],\r
60895 \r
60896     // configs apply to child items\r
60897     defaults: {anchor: '100%'},\r
60898     defaultType: 'textfield',\r
60899     items: [{\r
60900         fieldLabel: 'Name',\r
60901         name: 'name'\r
60902     },{\r
60903         fieldLabel: 'Email',\r
60904         name: 'email'\r
60905     },{\r
60906         fieldLabel: 'Company',\r
60907         name: 'company'\r
60908     }],\r
60909 \r
60910     // configs for BasicForm\r
60911     api: {\r
60912         // The server-side method to call for load() requests\r
60913         load: Profile.getBasicInfo,\r
60914         // The server-side must mark the submit handler as a 'formHandler'\r
60915         submit: Profile.updateBasicInfo\r
60916     },\r
60917     // specify the order for the passed params\r
60918     paramOrder: ['uid', 'foo']\r
60919 });\r
60920  * </code></pre>\r
60921  * The data packet sent to the server will resemble something like:\r
60922  * <pre><code>\r
60923 {\r
60924     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",\r
60925     "result":{\r
60926         "success":true,\r
60927         "id":{\r
60928             "extAction":"Profile","extMethod":"updateBasicInfo",\r
60929             "extType":"rpc","extTID":"6","extUpload":"false",\r
60930             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"\r
60931         }\r
60932     }\r
60933 }\r
60934  * </code></pre>\r
60935  * The form will process a data packet returned by the server that is similar\r
60936  * to the following:\r
60937  * <pre><code>\r
60938 // sample success packet (batched requests)\r
60939 [\r
60940     {\r
60941         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,\r
60942         "result":{\r
60943             "success":true\r
60944         }\r
60945     }\r
60946 ]\r
60947 \r
60948 // sample failure packet (one request)\r
60949 {\r
60950         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",\r
60951         "result":{\r
60952             "errors":{\r
60953                 "email":"already taken"\r
60954             },\r
60955             "success":false,\r
60956             "foo":"bar"\r
60957         }\r
60958 }\r
60959  * </code></pre>\r
60960  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.\r
60961  */\r
60962 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {\r
60963     constructor : function(form, opts) {\r
60964         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);\r
60965     },\r
60966     type : 'directsubmit',\r
60967     // override of Submit\r
60968     run : function(){\r
60969         var o = this.options;\r
60970         if(o.clientValidation === false || this.form.isValid()){\r
60971             // tag on any additional params to be posted in the\r
60972             // form scope\r
60973             this.success.params = this.getParams();\r
60974             this.form.api.submit(this.form.el.dom, this.success, this);\r
60975         }else if (o.clientValidation !== false){ // client validation failed\r
60976             this.failureType = Ext.form.Action.CLIENT_INVALID;\r
60977             this.form.afterAction(this, false);\r
60978         }\r
60979     },\r
60980 \r
60981     getParams : function() {\r
60982         var o = {};\r
60983         var bp = this.form.baseParams;\r
60984         var p = this.options.params;\r
60985         Ext.apply(o, p, bp);\r
60986         return o;\r
60987     },\r
60988     // Direct actions have already been processed and therefore\r
60989     // we can directly set the result; Direct Actions do not have\r
60990     // a this.response property.\r
60991     processResponse : function(result) {\r
60992         this.result = result;\r
60993         return result;\r
60994     },\r
60995     \r
60996     success : function(response, trans){\r
60997         if(trans.type == Ext.Direct.exceptions.SERVER){\r
60998             response = {};\r
60999         }\r
61000         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);\r
61001     }\r
61002 });\r
61003 \r
61004 Ext.form.Action.ACTION_TYPES = {\r
61005     'load' : Ext.form.Action.Load,\r
61006     'submit' : Ext.form.Action.Submit,\r
61007     'directload' : Ext.form.Action.DirectLoad,\r
61008     'directsubmit' : Ext.form.Action.DirectSubmit\r
61009 };\r
61010 /**
61011  * @class Ext.form.VTypes
61012  * <p>This is a singleton object which contains a set of commonly used field validation functions.
61013  * The validations provided are basic and intended to be easily customizable and extended.</p>
61014  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
61015  * test function, and optionally specify any corresponding error text to display and any keystroke
61016  * filtering mask to apply. For example:</p>
61017  * <pre><code>
61018 // custom Vtype for vtype:'time'
61019 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
61020 Ext.apply(Ext.form.VTypes, {
61021     //  vtype validation function
61022     time: function(val, field) {
61023         return timeTest.test(val);
61024     },
61025     // vtype Text property: The error text to display when the validation function returns false
61026     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
61027     // vtype Mask property: The keystroke filter mask
61028     timeMask: /[\d\s:amp]/i
61029 });
61030  * </code></pre>
61031  * Another example: 
61032  * <pre><code>
61033 // custom Vtype for vtype:'IPAddress'
61034 Ext.apply(Ext.form.VTypes, {
61035     IPAddress:  function(v) {
61036         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
61037     },
61038     IPAddressText: 'Must be a numeric IP address',
61039     IPAddressMask: /[\d\.]/i
61040 });
61041  * </code></pre>
61042  * @singleton
61043  */
61044 Ext.form.VTypes = function(){
61045     // closure these in so they are only created once.
61046     var alpha = /^[a-zA-Z_]+$/,
61047         alphanum = /^[a-zA-Z0-9_]+$/,
61048         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,4}$/,
61049         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
61050
61051     // All these messages and functions are configurable
61052     return {
61053         /**
61054          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
61055          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
61056          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
61057          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a> 
61058          * for additional information.  This implementation is intended to validate the following emails:<tt>
61059          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
61060          * </tt>.
61061          * @param {String} value The email address
61062          * @return {Boolean} true if the RegExp test passed, and false if not.
61063          */
61064         'email' : function(v){
61065             return email.test(v);
61066         },
61067         /**
61068          * The error text to display when the email validation function returns false.  Defaults to:
61069          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
61070          * @type String
61071          */
61072         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
61073         /**
61074          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for 
61075          * information about more complex email validation. Defaults to:
61076          * <tt>/[a-z0-9_\.\-@]/i</tt>
61077          * @type RegExp
61078          */
61079         'emailMask' : /[a-z0-9_\.\-@]/i,
61080
61081         /**
61082          * The function used to validate URLs
61083          * @param {String} value The URL
61084          * @return {Boolean} true if the RegExp test passed, and false if not.
61085          */
61086         'url' : function(v){
61087             return url.test(v);
61088         },
61089         /**
61090          * The error text to display when the url validation function returns false.  Defaults to:
61091          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
61092          * @type String
61093          */
61094         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
61095         
61096         /**
61097          * The function used to validate alpha values
61098          * @param {String} value The value
61099          * @return {Boolean} true if the RegExp test passed, and false if not.
61100          */
61101         'alpha' : function(v){
61102             return alpha.test(v);
61103         },
61104         /**
61105          * The error text to display when the alpha validation function returns false.  Defaults to:
61106          * <tt>'This field should only contain letters and _'</tt>
61107          * @type String
61108          */
61109         'alphaText' : 'This field should only contain letters and _',
61110         /**
61111          * The keystroke filter mask to be applied on alpha input.  Defaults to:
61112          * <tt>/[a-z_]/i</tt>
61113          * @type RegExp
61114          */
61115         'alphaMask' : /[a-z_]/i,
61116
61117         /**
61118          * The function used to validate alphanumeric values
61119          * @param {String} value The value
61120          * @return {Boolean} true if the RegExp test passed, and false if not.
61121          */
61122         'alphanum' : function(v){
61123             return alphanum.test(v);
61124         },
61125         /**
61126          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
61127          * <tt>'This field should only contain letters, numbers and _'</tt>
61128          * @type String
61129          */
61130         'alphanumText' : 'This field should only contain letters, numbers and _',
61131         /**
61132          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
61133          * <tt>/[a-z0-9_]/i</tt>
61134          * @type RegExp
61135          */
61136         'alphanumMask' : /[a-z0-9_]/i
61137     };
61138 }();/**\r
61139  * @class Ext.grid.GridPanel\r
61140  * @extends Ext.Panel\r
61141  * <p>This class represents the primary interface of a component based grid control to represent data\r
61142  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>\r
61143  * <div class="mdetail-params"><ul>\r
61144  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)\r
61145  * <div class="sub-desc"></div></li>\r
61146  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup\r
61147  * <div class="sub-desc"></div></li>\r
61148  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface \r
61149  * <div class="sub-desc"></div></li>\r
61150  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior \r
61151  * <div class="sub-desc"></div></li>\r
61152  * </ul></div>\r
61153  * <p>Example usage:</p>\r
61154  * <pre><code>\r
61155 var grid = new Ext.grid.GridPanel({\r
61156     {@link #store}: new {@link Ext.data.Store}({\r
61157         {@link Ext.data.Store#autoDestroy autoDestroy}: true,\r
61158         {@link Ext.data.Store#reader reader}: reader,\r
61159         {@link Ext.data.Store#data data}: xg.dummyData\r
61160     }),\r
61161     {@link #colModel}: new {@link Ext.grid.ColumnModel}({\r
61162         {@link Ext.grid.ColumnModel#defaults defaults}: {\r
61163             width: 120,\r
61164             sortable: true\r
61165         },\r
61166         {@link Ext.grid.ColumnModel#columns columns}: [\r
61167             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},\r
61168             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},\r
61169             {header: 'Change', dataIndex: 'change'},\r
61170             {header: '% Change', dataIndex: 'pctChange'},\r
61171             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype\r
61172             {\r
61173                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',\r
61174                 xtype: 'datecolumn', format: 'M d, Y'\r
61175             }\r
61176         ],\r
61177     }),\r
61178     {@link #viewConfig}: {\r
61179         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
61180 \r
61181 //      Return CSS class to apply to rows depending upon data values\r
61182         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {\r
61183             var c = record.{@link Ext.data.Record#get get}('change');\r
61184             if (c < 0) {\r
61185                 return 'price-fall';\r
61186             } else if (c > 0) {\r
61187                 return 'price-rise';\r
61188             }\r
61189         }\r
61190     },\r
61191     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),\r
61192     width: 600,\r
61193     height: 300,\r
61194     frame: true,\r
61195     title: 'Framed with Row Selection and Horizontal Scrolling',\r
61196     iconCls: 'icon-grid'\r
61197 });\r
61198  * </code></pre>\r
61199  * <p><b><u>Notes:</u></b></p>\r
61200  * <div class="mdetail-params"><ul>\r
61201  * <li>Although this class inherits many configuration options from base classes, some of them\r
61202  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will\r
61203  * have no effect.</li>\r
61204  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to\r
61205  * scroll its rows. These dimensions can either be set explicitly through the\r
61206  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>\r
61207  * configuration options or implicitly set by using the grid as a child item of a\r
61208  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}\r
61209  * provide the sizing of its child items (for example the Container of the Grid may specify\r
61210  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>\r
61211  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated\r
61212  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>\r
61213  * </ul></div>\r
61214  * @constructor\r
61215  * @param {Object} config The config object\r
61216  * @xtype grid\r
61217  */\r
61218 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {\r
61219     /**\r
61220      * @cfg {String} autoExpandColumn\r
61221      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in\r
61222      * this grid that should expand to fill unused space. This value specified here can not\r
61223      * be <tt>0</tt>.</p>\r
61224      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with\r
61225      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>\r
61226      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>\r
61227      * for additional details.</p>\r
61228      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>\r
61229      */\r
61230     autoExpandColumn : false,\r
61231     /**\r
61232      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>\r
61233      * can have (if enabled). Defaults to <tt>1000</tt>.\r
61234      */\r
61235     autoExpandMax : 1000,\r
61236     /**\r
61237      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>\r
61238      * can have (if enabled). Defaults to <tt>50</tt>.\r
61239      */\r
61240     autoExpandMin : 50,\r
61241     /**\r
61242      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.\r
61243      * Default is <tt>false</tt>.\r
61244      */\r
61245     columnLines : false,\r
61246     /**\r
61247      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.\r
61248      */\r
61249     /**\r
61250      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).\r
61251      */\r
61252     /**\r
61253      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a\r
61254      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the\r
61255      * <tt>{@link #colModel}</tt> configuration property.\r
61256      */\r
61257     /**\r
61258      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.\r
61259      */\r
61260     /**\r
61261      * @cfg {String} ddText\r
61262      * Configures the text in the drag proxy.  Defaults to:\r
61263      * <pre><code>\r
61264      * ddText : '{0} selected row{1}'\r
61265      * </code></pre>\r
61266      * <tt>{0}</tt> is replaced with the number of selected rows.\r
61267      */\r
61268     ddText : '{0} selected row{1}',\r
61269     /**\r
61270      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>\r
61271      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row\r
61272      * structure deferred so that layouts with GridPanels appear more quickly.</p>\r
61273      */\r
61274     deferRowRender : true,\r
61275     /**\r
61276      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>\r
61277      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>\r
61278      */\r
61279     /**\r
61280      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.\r
61281      */\r
61282     /**\r
61283      * @cfg {Boolean} enableColumnHide\r
61284      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}\r
61285      * with the {@link #enableHdMenu header menu}.\r
61286      */\r
61287     enableColumnHide : true,\r
61288     /**\r
61289      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>\r
61290      * to turn off column reordering via drag drop.\r
61291      */\r
61292     enableColumnMove : true,\r
61293     /**\r
61294      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>\r
61295      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to\r
61296      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>\r
61297      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>\r
61298      * property.</p>\r
61299      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of\r
61300      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},\r
61301      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able\r
61302      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>\r
61303      */\r
61304     enableDragDrop : false,\r
61305     /**\r
61306      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.\r
61307      */\r
61308     enableHdMenu : true,\r
61309     /**\r
61310      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.\r
61311      */\r
61312     /**\r
61313      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while\r
61314      * loading. Defaults to <code>false</code>.\r
61315      */\r
61316     loadMask : false,\r
61317     /**\r
61318      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.\r
61319      */\r
61320     /**\r
61321      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.\r
61322      */\r
61323     minColumnWidth : 25,\r
61324     /**\r
61325      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.\r
61326      */\r
61327     /**\r
61328      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide\r
61329      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).\r
61330      */\r
61331     /**\r
61332      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).\r
61333      */\r
61334     /**\r
61335      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.\r
61336      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of\r
61337      * the grid. A default CSS rule is provided which sets a background colour, but you can override this\r
61338      * with a rule which either overrides the <b>background-color</b> style using the '!important'\r
61339      * modifier, or which uses a CSS selector of higher specificity.</p>\r
61340      */\r
61341     stripeRows : false,\r
61342     /**\r
61343      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>\r
61344      * for GridPanel, but <tt>false</tt> for EditorGridPanel.\r
61345      */\r
61346     trackMouseOver : true,\r
61347     /**\r
61348      * @cfg {Array} stateEvents\r
61349      * An array of events that, when fired, should trigger this component to save its state.\r
61350      * Defaults to:<pre><code>\r
61351      * stateEvents: ['columnmove', 'columnresize', 'sortchange']\r
61352      * </code></pre>\r
61353      * <p>These can be any types of events supported by this component, including browser or\r
61354      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>\r
61355      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring\r
61356      * Component state.</p>\r
61357      */\r
61358     stateEvents : ['columnmove', 'columnresize', 'sortchange'],\r
61359     /**\r
61360      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set\r
61361      * before a call to {@link Ext.Component#render render()}.\r
61362      */\r
61363     view : null,\r
61364     \r
61365     /**\r
61366      * @cfg {Array} bubbleEvents\r
61367      * <p>An array of events that, when fired, should be bubbled to any parent container.\r
61368      * Defaults to <tt>[]</tt>.\r
61369      */\r
61370     bubbleEvents: [],\r
61371     \r
61372     /**\r
61373      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of\r
61374      * the config options available for {@link Ext.grid.GridView} can be specified here. This option\r
61375      * is ignored if <tt>{@link #view}</tt> is specified.\r
61376      */\r
61377 \r
61378     // private\r
61379     rendered : false,\r
61380     // private\r
61381     viewReady : false,\r
61382 \r
61383     // private\r
61384     initComponent : function(){\r
61385         Ext.grid.GridPanel.superclass.initComponent.call(this);\r
61386 \r
61387         if(this.columnLines){\r
61388             this.cls = (this.cls || '') + ' x-grid-with-col-lines';\r
61389         }\r
61390         // override any provided value since it isn't valid\r
61391         // and is causing too many bug reports ;)\r
61392         this.autoScroll = false;\r
61393         this.autoWidth = false;\r
61394 \r
61395         if(Ext.isArray(this.columns)){\r
61396             this.colModel = new Ext.grid.ColumnModel(this.columns);\r
61397             delete this.columns;\r
61398         }\r
61399 \r
61400         // check and correct shorthanded configs\r
61401         if(this.ds){\r
61402             this.store = this.ds;\r
61403             delete this.ds;\r
61404         }\r
61405         if(this.cm){\r
61406             this.colModel = this.cm;\r
61407             delete this.cm;\r
61408         }\r
61409         if(this.sm){\r
61410             this.selModel = this.sm;\r
61411             delete this.sm;\r
61412         }\r
61413         this.store = Ext.StoreMgr.lookup(this.store);\r
61414 \r
61415         this.addEvents(\r
61416             // raw events\r
61417             /**\r
61418              * @event click\r
61419              * The raw click event for the entire grid.\r
61420              * @param {Ext.EventObject} e\r
61421              */\r
61422             'click',\r
61423             /**\r
61424              * @event dblclick\r
61425              * The raw dblclick event for the entire grid.\r
61426              * @param {Ext.EventObject} e\r
61427              */\r
61428             'dblclick',\r
61429             /**\r
61430              * @event contextmenu\r
61431              * The raw contextmenu event for the entire grid.\r
61432              * @param {Ext.EventObject} e\r
61433              */\r
61434             'contextmenu',\r
61435             /**\r
61436              * @event mousedown\r
61437              * The raw mousedown event for the entire grid.\r
61438              * @param {Ext.EventObject} e\r
61439              */\r
61440             'mousedown',\r
61441             /**\r
61442              * @event mouseup\r
61443              * The raw mouseup event for the entire grid.\r
61444              * @param {Ext.EventObject} e\r
61445              */\r
61446             'mouseup',\r
61447             /**\r
61448              * @event mouseover\r
61449              * The raw mouseover event for the entire grid.\r
61450              * @param {Ext.EventObject} e\r
61451              */\r
61452             'mouseover',\r
61453             /**\r
61454              * @event mouseout\r
61455              * The raw mouseout event for the entire grid.\r
61456              * @param {Ext.EventObject} e\r
61457              */\r
61458             'mouseout',\r
61459             /**\r
61460              * @event keypress\r
61461              * The raw keypress event for the entire grid.\r
61462              * @param {Ext.EventObject} e\r
61463              */\r
61464             'keypress',\r
61465             /**\r
61466              * @event keydown\r
61467              * The raw keydown event for the entire grid.\r
61468              * @param {Ext.EventObject} e\r
61469              */\r
61470             'keydown',\r
61471 \r
61472             // custom events\r
61473             /**\r
61474              * @event cellmousedown\r
61475              * Fires before a cell is clicked\r
61476              * @param {Grid} this\r
61477              * @param {Number} rowIndex\r
61478              * @param {Number} columnIndex\r
61479              * @param {Ext.EventObject} e\r
61480              */\r
61481             'cellmousedown',\r
61482             /**\r
61483              * @event rowmousedown\r
61484              * Fires before a row is clicked\r
61485              * @param {Grid} this\r
61486              * @param {Number} rowIndex\r
61487              * @param {Ext.EventObject} e\r
61488              */\r
61489             'rowmousedown',\r
61490             /**\r
61491              * @event headermousedown\r
61492              * Fires before a header is clicked\r
61493              * @param {Grid} this\r
61494              * @param {Number} columnIndex\r
61495              * @param {Ext.EventObject} e\r
61496              */\r
61497             'headermousedown',\r
61498 \r
61499             /**\r
61500              * @event cellclick\r
61501              * Fires when a cell is clicked.\r
61502              * The data for the cell is drawn from the {@link Ext.data.Record Record}\r
61503              * for this row. To access the data in the listener function use the\r
61504              * following technique:\r
61505              * <pre><code>\r
61506 function(grid, rowIndex, columnIndex, e) {\r
61507     var record = grid.getStore().getAt(rowIndex);  // Get the Record\r
61508     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name\r
61509     var data = record.get(fieldName);\r
61510 }\r
61511 </code></pre>\r
61512              * @param {Grid} this\r
61513              * @param {Number} rowIndex\r
61514              * @param {Number} columnIndex\r
61515              * @param {Ext.EventObject} e\r
61516              */\r
61517             'cellclick',\r
61518             /**\r
61519              * @event celldblclick\r
61520              * Fires when a cell is double clicked\r
61521              * @param {Grid} this\r
61522              * @param {Number} rowIndex\r
61523              * @param {Number} columnIndex\r
61524              * @param {Ext.EventObject} e\r
61525              */\r
61526             'celldblclick',\r
61527             /**\r
61528              * @event rowclick\r
61529              * Fires when a row is clicked\r
61530              * @param {Grid} this\r
61531              * @param {Number} rowIndex\r
61532              * @param {Ext.EventObject} e\r
61533              */\r
61534             'rowclick',\r
61535             /**\r
61536              * @event rowdblclick\r
61537              * Fires when a row is double clicked\r
61538              * @param {Grid} this\r
61539              * @param {Number} rowIndex\r
61540              * @param {Ext.EventObject} e\r
61541              */\r
61542             'rowdblclick',\r
61543             /**\r
61544              * @event headerclick\r
61545              * Fires when a header is clicked\r
61546              * @param {Grid} this\r
61547              * @param {Number} columnIndex\r
61548              * @param {Ext.EventObject} e\r
61549              */\r
61550             'headerclick',\r
61551             /**\r
61552              * @event headerdblclick\r
61553              * Fires when a header cell is double clicked\r
61554              * @param {Grid} this\r
61555              * @param {Number} columnIndex\r
61556              * @param {Ext.EventObject} e\r
61557              */\r
61558             'headerdblclick',\r
61559             /**\r
61560              * @event rowcontextmenu\r
61561              * Fires when a row is right clicked\r
61562              * @param {Grid} this\r
61563              * @param {Number} rowIndex\r
61564              * @param {Ext.EventObject} e\r
61565              */\r
61566             'rowcontextmenu',\r
61567             /**\r
61568              * @event cellcontextmenu\r
61569              * Fires when a cell is right clicked\r
61570              * @param {Grid} this\r
61571              * @param {Number} rowIndex\r
61572              * @param {Number} cellIndex\r
61573              * @param {Ext.EventObject} e\r
61574              */\r
61575             'cellcontextmenu',\r
61576             /**\r
61577              * @event headercontextmenu\r
61578              * Fires when a header is right clicked\r
61579              * @param {Grid} this\r
61580              * @param {Number} columnIndex\r
61581              * @param {Ext.EventObject} e\r
61582              */\r
61583             'headercontextmenu',\r
61584             /**\r
61585              * @event bodyscroll\r
61586              * Fires when the body element is scrolled\r
61587              * @param {Number} scrollLeft\r
61588              * @param {Number} scrollTop\r
61589              */\r
61590             'bodyscroll',\r
61591             /**\r
61592              * @event columnresize\r
61593              * Fires when the user resizes a column\r
61594              * @param {Number} columnIndex\r
61595              * @param {Number} newSize\r
61596              */\r
61597             'columnresize',\r
61598             /**\r
61599              * @event columnmove\r
61600              * Fires when the user moves a column\r
61601              * @param {Number} oldIndex\r
61602              * @param {Number} newIndex\r
61603              */\r
61604             'columnmove',\r
61605             /**\r
61606              * @event sortchange\r
61607              * Fires when the grid's store sort changes\r
61608              * @param {Grid} this\r
61609              * @param {Object} sortInfo An object with the keys field and direction\r
61610              */\r
61611             'sortchange',\r
61612             /**\r
61613              * @event reconfigure\r
61614              * Fires when the grid is reconfigured with a new store and/or column model.\r
61615              * @param {Grid} this\r
61616              * @param {Ext.data.Store} store The new store\r
61617              * @param {Ext.grid.ColumnModel} colModel The new column model\r
61618              */\r
61619             'reconfigure'\r
61620         );\r
61621     },\r
61622 \r
61623     // private\r
61624     onRender : function(ct, position){\r
61625         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);\r
61626 \r
61627         var c = this.getGridEl();\r
61628 \r
61629         this.el.addClass('x-grid-panel');\r
61630 \r
61631         this.mon(c, {\r
61632             scope: this,\r
61633             mousedown: this.onMouseDown,\r
61634             click: this.onClick,\r
61635             dblclick: this.onDblClick,\r
61636             contextmenu: this.onContextMenu\r
61637         });\r
61638 \r
61639         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);\r
61640 \r
61641         var view = this.getView();\r
61642         view.init(this);\r
61643         view.render();\r
61644         this.getSelectionModel().init(this);\r
61645     },\r
61646 \r
61647     // private\r
61648     initEvents : function(){\r
61649         Ext.grid.GridPanel.superclass.initEvents.call(this);\r
61650 \r
61651         if(this.loadMask){\r
61652             this.loadMask = new Ext.LoadMask(this.bwrap,\r
61653                     Ext.apply({store:this.store}, this.loadMask));\r
61654         }\r
61655     },\r
61656 \r
61657     initStateEvents : function(){\r
61658         Ext.grid.GridPanel.superclass.initStateEvents.call(this);\r
61659         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});\r
61660     },\r
61661 \r
61662     applyState : function(state){\r
61663         var cm = this.colModel,\r
61664             cs = state.columns;\r
61665         if(cs){\r
61666             for(var i = 0, len = cs.length; i < len; i++){\r
61667                 var s = cs[i];\r
61668                 var c = cm.getColumnById(s.id);\r
61669                 if(c){\r
61670                     c.hidden = s.hidden;\r
61671                     c.width = s.width;\r
61672                     var oldIndex = cm.getIndexById(s.id);\r
61673                     if(oldIndex != i){\r
61674                         cm.moveColumn(oldIndex, i);\r
61675                     }\r
61676                 }\r
61677             }\r
61678         }\r
61679         if(state.sort && this.store){\r
61680             this.store[this.store.remoteSort ? 'setDefaultSort' : 'sort'](state.sort.field, state.sort.direction);\r
61681         }\r
61682         var o = Ext.apply({}, state);\r
61683         delete o.columns;\r
61684         delete o.sort;\r
61685         Ext.grid.GridPanel.superclass.applyState.call(this, o);\r
61686     },\r
61687 \r
61688     getState : function(){\r
61689         var o = {columns: []};\r
61690         for(var i = 0, c; (c = this.colModel.config[i]); i++){\r
61691             o.columns[i] = {\r
61692                 id: c.id,\r
61693                 width: c.width\r
61694             };\r
61695             if(c.hidden){\r
61696                 o.columns[i].hidden = true;\r
61697             }\r
61698         }\r
61699         if(this.store){\r
61700             var ss = this.store.getSortState();\r
61701             if(ss){\r
61702                 o.sort = ss;\r
61703             }\r
61704         }\r
61705         return o;\r
61706     },\r
61707 \r
61708     // private\r
61709     afterRender : function(){\r
61710         Ext.grid.GridPanel.superclass.afterRender.call(this);\r
61711         var v = this.view;\r
61712         this.on('bodyresize', v.layout, v);\r
61713         v.layout();\r
61714         if(this.deferRowRender){\r
61715             v.afterRender.defer(10, this.view);\r
61716         }else{\r
61717             v.afterRender();\r
61718         }\r
61719         this.viewReady = true;\r
61720     },\r
61721 \r
61722     /**\r
61723      * <p>Reconfigures the grid to use a different Store and Column Model\r
61724      * and fires the 'reconfigure' event. The View will be bound to the new\r
61725      * objects and refreshed.</p>\r
61726      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become\r
61727      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the\r
61728      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound\r
61729      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring\r
61730      * with the new data.</p>\r
61731      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object\r
61732      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object\r
61733      */\r
61734     reconfigure : function(store, colModel){\r
61735         if(this.loadMask){\r
61736             this.loadMask.destroy();\r
61737             this.loadMask = new Ext.LoadMask(this.bwrap,\r
61738                     Ext.apply({}, {store:store}, this.initialConfig.loadMask));\r
61739         }\r
61740         this.view.initData(store, colModel);\r
61741         this.store = store;\r
61742         this.colModel = colModel;\r
61743         if(this.rendered){\r
61744             this.view.refresh(true);\r
61745         }\r
61746         this.fireEvent('reconfigure', this, store, colModel);\r
61747     },\r
61748 \r
61749     // private\r
61750     onDestroy : function(){\r
61751         if(this.rendered){\r
61752             var c = this.body;\r
61753             c.removeAllListeners();\r
61754             c.update('');\r
61755             Ext.destroy(this.view, this.loadMask);\r
61756         }else if(this.store && this.store.autoDestroy){\r
61757             this.store.destroy();\r
61758         }\r
61759         Ext.destroy(this.colModel, this.selModel);\r
61760         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;\r
61761         Ext.grid.GridPanel.superclass.onDestroy.call(this);\r
61762     },\r
61763 \r
61764     // private\r
61765     processEvent : function(name, e){\r
61766         this.fireEvent(name, e);\r
61767         var t = e.getTarget();\r
61768         var v = this.view;\r
61769         var header = v.findHeaderIndex(t);\r
61770         if(header !== false){\r
61771             this.fireEvent('header' + name, this, header, e);\r
61772         }else{\r
61773             var row = v.findRowIndex(t);\r
61774             var cell = v.findCellIndex(t);\r
61775             if(row !== false){\r
61776                 this.fireEvent('row' + name, this, row, e);\r
61777                 if(cell !== false){\r
61778                     this.fireEvent('cell' + name, this, row, cell, e);\r
61779                 }\r
61780             }\r
61781         }\r
61782     },\r
61783 \r
61784     // private\r
61785     onClick : function(e){\r
61786         this.processEvent('click', e);\r
61787     },\r
61788 \r
61789     // private\r
61790     onMouseDown : function(e){\r
61791         this.processEvent('mousedown', e);\r
61792     },\r
61793 \r
61794     // private\r
61795     onContextMenu : function(e, t){\r
61796         this.processEvent('contextmenu', e);\r
61797     },\r
61798 \r
61799     // private\r
61800     onDblClick : function(e){\r
61801         this.processEvent('dblclick', e);\r
61802     },\r
61803 \r
61804     // private\r
61805     walkCells : function(row, col, step, fn, scope){\r
61806         var cm = this.colModel, clen = cm.getColumnCount();\r
61807         var ds = this.store, rlen = ds.getCount(), first = true;\r
61808         if(step < 0){\r
61809             if(col < 0){\r
61810                 row--;\r
61811                 first = false;\r
61812             }\r
61813             while(row >= 0){\r
61814                 if(!first){\r
61815                     col = clen-1;\r
61816                 }\r
61817                 first = false;\r
61818                 while(col >= 0){\r
61819                     if(fn.call(scope || this, row, col, cm) === true){\r
61820                         return [row, col];\r
61821                     }\r
61822                     col--;\r
61823                 }\r
61824                 row--;\r
61825             }\r
61826         } else {\r
61827             if(col >= clen){\r
61828                 row++;\r
61829                 first = false;\r
61830             }\r
61831             while(row < rlen){\r
61832                 if(!first){\r
61833                     col = 0;\r
61834                 }\r
61835                 first = false;\r
61836                 while(col < clen){\r
61837                     if(fn.call(scope || this, row, col, cm) === true){\r
61838                         return [row, col];\r
61839                     }\r
61840                     col++;\r
61841                 }\r
61842                 row++;\r
61843             }\r
61844         }\r
61845         return null;\r
61846     },\r
61847 \r
61848     // private\r
61849     onResize : function(){\r
61850         Ext.grid.GridPanel.superclass.onResize.apply(this, arguments);\r
61851         if(this.viewReady){\r
61852             this.view.layout();\r
61853         }\r
61854     },\r
61855 \r
61856     /**\r
61857      * Returns the grid's underlying element.\r
61858      * @return {Element} The element\r
61859      */\r
61860     getGridEl : function(){\r
61861         return this.body;\r
61862     },\r
61863 \r
61864     // private for compatibility, overridden by editor grid\r
61865     stopEditing : Ext.emptyFn,\r
61866 \r
61867     /**\r
61868      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>\r
61869      * configuration option. If no selection model was configured, this will create\r
61870      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.\r
61871      * @return {SelectionModel}\r
61872      */\r
61873     getSelectionModel : function(){\r
61874         if(!this.selModel){\r
61875             this.selModel = new Ext.grid.RowSelectionModel(\r
61876                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);\r
61877         }\r
61878         return this.selModel;\r
61879     },\r
61880 \r
61881     /**\r
61882      * Returns the grid's data store.\r
61883      * @return {Ext.data.Store} The store\r
61884      */\r
61885     getStore : function(){\r
61886         return this.store;\r
61887     },\r
61888 \r
61889     /**\r
61890      * Returns the grid's ColumnModel.\r
61891      * @return {Ext.grid.ColumnModel} The column model\r
61892      */\r
61893     getColumnModel : function(){\r
61894         return this.colModel;\r
61895     },\r
61896 \r
61897     /**\r
61898      * Returns the grid's GridView object.\r
61899      * @return {Ext.grid.GridView} The grid view\r
61900      */\r
61901     getView : function(){\r
61902         if(!this.view){\r
61903             this.view = new Ext.grid.GridView(this.viewConfig);\r
61904         }\r
61905         return this.view;\r
61906     },\r
61907     /**\r
61908      * Called to get grid's drag proxy text, by default returns this.ddText.\r
61909      * @return {String} The text\r
61910      */\r
61911     getDragDropText : function(){\r
61912         var count = this.selModel.getCount();\r
61913         return String.format(this.ddText, count, count == 1 ? '' : 's');\r
61914     }\r
61915 \r
61916     /** \r
61917      * @cfg {String/Number} activeItem \r
61918      * @hide \r
61919      */\r
61920     /** \r
61921      * @cfg {Boolean} autoDestroy \r
61922      * @hide \r
61923      */\r
61924     /** \r
61925      * @cfg {Object/String/Function} autoLoad \r
61926      * @hide \r
61927      */\r
61928     /** \r
61929      * @cfg {Boolean} autoWidth \r
61930      * @hide \r
61931      */\r
61932     /** \r
61933      * @cfg {Boolean/Number} bufferResize \r
61934      * @hide \r
61935      */\r
61936     /** \r
61937      * @cfg {String} defaultType \r
61938      * @hide \r
61939      */\r
61940     /** \r
61941      * @cfg {Object} defaults \r
61942      * @hide \r
61943      */\r
61944     /** \r
61945      * @cfg {Boolean} hideBorders \r
61946      * @hide \r
61947      */\r
61948     /** \r
61949      * @cfg {Mixed} items \r
61950      * @hide \r
61951      */\r
61952     /** \r
61953      * @cfg {String} layout \r
61954      * @hide \r
61955      */\r
61956     /** \r
61957      * @cfg {Object} layoutConfig \r
61958      * @hide \r
61959      */\r
61960     /** \r
61961      * @cfg {Boolean} monitorResize \r
61962      * @hide \r
61963      */\r
61964     /** \r
61965      * @property items \r
61966      * @hide \r
61967      */\r
61968     /** \r
61969      * @method add \r
61970      * @hide \r
61971      */\r
61972     /** \r
61973      * @method cascade \r
61974      * @hide \r
61975      */\r
61976     /** \r
61977      * @method doLayout \r
61978      * @hide \r
61979      */\r
61980     /** \r
61981      * @method find \r
61982      * @hide \r
61983      */\r
61984     /** \r
61985      * @method findBy \r
61986      * @hide \r
61987      */\r
61988     /** \r
61989      * @method findById \r
61990      * @hide \r
61991      */\r
61992     /** \r
61993      * @method findByType \r
61994      * @hide \r
61995      */\r
61996     /** \r
61997      * @method getComponent \r
61998      * @hide \r
61999      */\r
62000     /** \r
62001      * @method getLayout \r
62002      * @hide \r
62003      */\r
62004     /** \r
62005      * @method getUpdater \r
62006      * @hide \r
62007      */\r
62008     /** \r
62009      * @method insert \r
62010      * @hide \r
62011      */\r
62012     /** \r
62013      * @method load \r
62014      * @hide \r
62015      */\r
62016     /** \r
62017      * @method remove \r
62018      * @hide \r
62019      */\r
62020     /** \r
62021      * @event add \r
62022      * @hide \r
62023      */\r
62024     /** \r
62025      * @event afterLayout \r
62026      * @hide \r
62027      */\r
62028     /** \r
62029      * @event beforeadd \r
62030      * @hide \r
62031      */\r
62032     /** \r
62033      * @event beforeremove \r
62034      * @hide \r
62035      */\r
62036     /** \r
62037      * @event remove \r
62038      * @hide \r
62039      */\r
62040 \r
62041 \r
62042 \r
62043     /**\r
62044      * @cfg {String} allowDomMove  @hide\r
62045      */\r
62046     /**\r
62047      * @cfg {String} autoEl @hide\r
62048      */\r
62049     /**\r
62050      * @cfg {String} applyTo  @hide\r
62051      */\r
62052     /**\r
62053      * @cfg {String} autoScroll  @hide\r
62054      */\r
62055     /**\r
62056      * @cfg {String} bodyBorder  @hide\r
62057      */\r
62058     /**\r
62059      * @cfg {String} bodyStyle  @hide\r
62060      */\r
62061     /**\r
62062      * @cfg {String} contentEl  @hide\r
62063      */\r
62064     /**\r
62065      * @cfg {String} disabledClass  @hide\r
62066      */\r
62067     /**\r
62068      * @cfg {String} elements  @hide\r
62069      */\r
62070     /**\r
62071      * @cfg {String} html  @hide\r
62072      */\r
62073     /**\r
62074      * @cfg {Boolean} preventBodyReset\r
62075      * @hide\r
62076      */\r
62077     /**\r
62078      * @property disabled\r
62079      * @hide\r
62080      */\r
62081     /**\r
62082      * @method applyToMarkup\r
62083      * @hide\r
62084      */\r
62085     /**\r
62086      * @method enable\r
62087      * @hide\r
62088      */\r
62089     /**\r
62090      * @method disable\r
62091      * @hide\r
62092      */\r
62093     /**\r
62094      * @method setDisabled\r
62095      * @hide\r
62096      */\r
62097 });\r
62098 Ext.reg('grid', Ext.grid.GridPanel);/**
62099  * @class Ext.grid.GridView
62100  * @extends Ext.util.Observable
62101  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
62102  * Methods of this class may be used to access user interface elements to enable
62103  * special display effects. Do not change the DOM structure of the user interface.</p>
62104  * <p>This class does not provide ways to manipulate the underlying data. The data
62105  * model of a Grid is held in an {@link Ext.data.Store}.</p>
62106  * @constructor
62107  * @param {Object} config
62108  */
62109 Ext.grid.GridView = function(config){
62110     Ext.apply(this, config);
62111     // These events are only used internally by the grid components
62112     this.addEvents(
62113         /**
62114          * @event beforerowremoved
62115          * Internal UI Event. Fired before a row is removed.
62116          * @param {Ext.grid.GridView} view
62117          * @param {Number} rowIndex The index of the row to be removed.
62118          * @param {Ext.data.Record} record The Record to be removed
62119          */
62120         'beforerowremoved',
62121         /**
62122          * @event beforerowsinserted
62123          * Internal UI Event. Fired before rows are inserted.
62124          * @param {Ext.grid.GridView} view
62125          * @param {Number} firstRow The index of the first row to be inserted.
62126          * @param {Number} lastRow The index of the last row to be inserted.
62127          */
62128         'beforerowsinserted',
62129         /**
62130          * @event beforerefresh
62131          * Internal UI Event. Fired before the view is refreshed.
62132          * @param {Ext.grid.GridView} view
62133          */
62134         'beforerefresh',
62135         /**
62136          * @event rowremoved
62137          * Internal UI Event. Fired after a row is removed.
62138          * @param {Ext.grid.GridView} view
62139          * @param {Number} rowIndex The index of the row that was removed.
62140          * @param {Ext.data.Record} record The Record that was removed
62141          */
62142         'rowremoved',
62143         /**
62144          * @event rowsinserted
62145          * Internal UI Event. Fired after rows are inserted.
62146          * @param {Ext.grid.GridView} view
62147          * @param {Number} firstRow The index of the first inserted.
62148          * @param {Number} lastRow The index of the last row inserted.
62149          */
62150         'rowsinserted',
62151         /**
62152          * @event rowupdated
62153          * Internal UI Event. Fired after a row has been updated.
62154          * @param {Ext.grid.GridView} view
62155          * @param {Number} firstRow The index of the row updated.
62156          * @param {Ext.data.record} record The Record backing the row updated.
62157          */
62158         'rowupdated',
62159         /**
62160          * @event refresh
62161          * Internal UI Event. Fired after the GridView's body has been refreshed.
62162          * @param {Ext.grid.GridView} view
62163          */
62164         'refresh'
62165     );
62166     Ext.grid.GridView.superclass.constructor.call(this);
62167 };
62168
62169 Ext.extend(Ext.grid.GridView, Ext.util.Observable, {
62170     /**
62171      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
62172      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
62173      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
62174      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
62175      * (e.g., 'my-class another-class'). Example usage:
62176     <pre><code>
62177 viewConfig: {
62178     forceFit: true,
62179     showPreview: true, // custom property
62180     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
62181     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
62182         if(this.showPreview){
62183             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
62184             return 'x-grid3-row-expanded';
62185         }
62186         return 'x-grid3-row-collapsed';
62187     }
62188 },     
62189     </code></pre>
62190      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
62191      * @param {Number} index The row index.
62192      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
62193      * customization of various aspects of a grid row.
62194      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
62195      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
62196      * <ul>
62197      * <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>
62198      * <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>
62199      * </ul>
62200      * The following property will be passed in, and may be appended to:
62201      * <ul>
62202      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
62203      * both the standard grid row, and any expansion row.</div></li>
62204      * </ul>
62205      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
62206      * @method getRowClass
62207      * @return {String} a CSS class name to add to the row.
62208      */
62209     /**
62210      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
62211      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
62212      */
62213     /**
62214      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
62215      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
62216     <pre><code>
62217     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
62218     </code></pre>
62219      */
62220     /**
62221      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>). 
62222      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
62223      * config to disable the <i>menu</i> for individual columns.  While this config is true the
62224      * following will be disabled:<div class="mdetail-params"><ul>
62225      * <li>clicking on header to sort</li>
62226      * <li>the trigger to reveal the menu.</li>
62227      * </ul></div>
62228      */
62229     /**
62230      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
62231      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
62232      * See {@link Ext.grid.GridDragZone} for details.</p>
62233      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
62234      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
62235      * <li><i>after</i> the owning GridPanel has been rendered.</li>
62236      * </ul></div>
62237      * @property dragZone
62238      * @type {Ext.grid.GridDragZone}
62239      */
62240     /**
62241      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
62242      * first load (defaults to <tt>true</tt>).
62243      */
62244     deferEmptyText : true,
62245     /**
62246      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
62247      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
62248      * calculated.
62249      */
62250     scrollOffset : undefined,
62251     /**
62252      * @cfg {Boolean} autoFill
62253      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
62254      * when the grid is <b>initially rendered</b>.  The 
62255      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
62256      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
62257      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
62258      * See <tt>{@link #forceFit}</tt> also.
62259      */
62260     autoFill : false,
62261     /**
62262      * @cfg {Boolean} forceFit
62263      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
62264      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
62265      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
62266      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
62267      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
62268      */
62269     forceFit : false,
62270     /**
62271      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
62272      */
62273     sortClasses : ['sort-asc', 'sort-desc'],
62274     /**
62275      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
62276      */
62277     sortAscText : 'Sort Ascending',
62278     /**
62279      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
62280      */
62281     sortDescText : 'Sort Descending',
62282     /**
62283      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
62284      */
62285     columnsText : 'Columns',
62286
62287     /**
62288      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
62289      * example overriding the default styling:
62290     <pre><code>
62291     .x-grid3-row-selected {background-color: yellow;}
62292     </code></pre>
62293      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
62294      * facets (like text) use something like:
62295     <pre><code>
62296     .x-grid3-row-selected .x-grid3-cell-inner {
62297         color: #FFCC00;
62298     }
62299     </code></pre>
62300      * @type String
62301      */
62302     selectedRowClass : 'x-grid3-row-selected',
62303
62304     // private
62305     borderWidth : 2,
62306     tdClass : 'x-grid3-cell',
62307     hdCls : 'x-grid3-hd',
62308     markDirty : true,
62309
62310     /**
62311      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
62312      */
62313     cellSelectorDepth : 4,
62314     /**
62315      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
62316      */
62317     rowSelectorDepth : 10,
62318
62319     /**
62320      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
62321      */
62322     cellSelector : 'td.x-grid3-cell',
62323     /**
62324      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
62325      */
62326     rowSelector : 'div.x-grid3-row',
62327     
62328     // private
62329     firstRowCls: 'x-grid3-row-first',
62330     lastRowCls: 'x-grid3-row-last',
62331     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
62332
62333     /* -------------------------------- UI Specific ----------------------------- */
62334
62335     // private
62336     initTemplates : function(){
62337         var ts = this.templates || {};
62338         if(!ts.master){
62339             ts.master = new Ext.Template(
62340                     '<div class="x-grid3" hidefocus="true">',
62341                         '<div class="x-grid3-viewport">',
62342                             '<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>',
62343                             '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
62344                         '</div>',
62345                         '<div class="x-grid3-resize-marker">&#160;</div>',
62346                         '<div class="x-grid3-resize-proxy">&#160;</div>',
62347                     '</div>'
62348                     );
62349         }
62350
62351         if(!ts.header){
62352             ts.header = new Ext.Template(
62353                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
62354                     '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
62355                     '</table>'
62356                     );
62357         }
62358
62359         if(!ts.hcell){
62360             ts.hcell = new Ext.Template(
62361                     '<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>' : '',
62362                     '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
62363                     '</div></td>'
62364                     );
62365         }
62366
62367         if(!ts.body){
62368             ts.body = new Ext.Template('{rows}');
62369         }
62370
62371         if(!ts.row){
62372             ts.row = new Ext.Template(
62373                     '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
62374                     '<tbody><tr>{cells}</tr>',
62375                     (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>' : ''),
62376                     '</tbody></table></div>'
62377                     );
62378         }
62379
62380         if(!ts.cell){
62381             ts.cell = new Ext.Template(
62382                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
62383                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
62384                     '</td>'
62385                     );
62386         }
62387
62388         for(var k in ts){
62389             var t = ts[k];
62390             if(t && typeof t.compile == 'function' && !t.compiled){
62391                 t.disableFormats = true;
62392                 t.compile();
62393             }
62394         }
62395
62396         this.templates = ts;
62397         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
62398     },
62399
62400     // private
62401     fly : function(el){
62402         if(!this._flyweight){
62403             this._flyweight = new Ext.Element.Flyweight(document.body);
62404         }
62405         this._flyweight.dom = el;
62406         return this._flyweight;
62407     },
62408
62409     // private
62410     getEditorParent : function(){
62411         return this.scroller.dom;
62412     },
62413
62414     // private
62415     initElements : function(){
62416         var E = Ext.Element;
62417
62418         var el = this.grid.getGridEl().dom.firstChild;
62419         var cs = el.childNodes;
62420
62421         this.el = new E(el);
62422
62423         this.mainWrap = new E(cs[0]);
62424         this.mainHd = new E(this.mainWrap.dom.firstChild);
62425
62426         if(this.grid.hideHeaders){
62427             this.mainHd.setDisplayed(false);
62428         }
62429
62430         this.innerHd = this.mainHd.dom.firstChild;
62431         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
62432         if(this.forceFit){
62433             this.scroller.setStyle('overflow-x', 'hidden');
62434         }
62435         /**
62436          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
62437          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
62438          * @type Ext.Element
62439          * @property mainBody
62440          */
62441         this.mainBody = new E(this.scroller.dom.firstChild);
62442
62443         this.focusEl = new E(this.scroller.dom.childNodes[1]);
62444         this.focusEl.swallowEvent('click', true);
62445
62446         this.resizeMarker = new E(cs[1]);
62447         this.resizeProxy = new E(cs[2]);
62448     },
62449
62450     // private
62451     getRows : function(){
62452         return this.hasRows() ? this.mainBody.dom.childNodes : [];
62453     },
62454
62455     // finder methods, used with delegation
62456
62457     // private
62458     findCell : function(el){
62459         if(!el){
62460             return false;
62461         }
62462         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
62463     },
62464
62465     /**
62466      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
62467      * See also {@link #findRowIndex}
62468      * @param {HTMLElement} el The target element
62469      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
62470      */
62471     findCellIndex : function(el, requiredCls){
62472         var cell = this.findCell(el);
62473         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
62474             return this.getCellIndex(cell);
62475         }
62476         return false;
62477     },
62478
62479     // private
62480     getCellIndex : function(el){
62481         if(el){
62482             var m = el.className.match(this.colRe);
62483             if(m && m[1]){
62484                 return this.cm.getIndexById(m[1]);
62485             }
62486         }
62487         return false;
62488     },
62489
62490     // private
62491     findHeaderCell : function(el){
62492         var cell = this.findCell(el);
62493         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
62494     },
62495
62496     // private
62497     findHeaderIndex : function(el){
62498         return this.findCellIndex(el, this.hdCls);
62499     },
62500
62501     /**
62502      * Return the HtmlElement representing the grid row which contains the passed element.
62503      * @param {HTMLElement} el The target HTMLElement
62504      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
62505      */
62506     findRow : function(el){
62507         if(!el){
62508             return false;
62509         }
62510         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
62511     },
62512
62513     /**
62514      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
62515      * See also {@link #findCellIndex}
62516      * @param {HTMLElement} el The target HTMLElement
62517      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
62518      */
62519     findRowIndex : function(el){
62520         var r = this.findRow(el);
62521         return r ? r.rowIndex : false;
62522     },
62523
62524     // getter methods for fetching elements dynamically in the grid
62525
62526     /**
62527      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
62528      * @param {Number} index The row index
62529      * @return {HtmlElement} The div element.
62530      */
62531     getRow : function(row){
62532         return this.getRows()[row];
62533     },
62534
62535     /**
62536      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
62537      * @param {Number} row The row index in which to find the cell.
62538      * @param {Number} col The column index of the cell.
62539      * @return {HtmlElement} The td at the specified coordinates.
62540      */
62541     getCell : function(row, col){
62542         return this.getRow(row).getElementsByTagName('td')[col];
62543     },
62544
62545     /**
62546      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
62547      * @param {Number} index The column index
62548      * @return {HtmlElement} The td element.
62549      */
62550     getHeaderCell : function(index){
62551       return this.mainHd.dom.getElementsByTagName('td')[index];
62552     },
62553
62554     // manipulating elements
62555
62556     // private - use getRowClass to apply custom row classes
62557     addRowClass : function(row, cls){
62558         var r = this.getRow(row);
62559         if(r){
62560             this.fly(r).addClass(cls);
62561         }
62562     },
62563
62564     // private
62565     removeRowClass : function(row, cls){
62566         var r = this.getRow(row);
62567         if(r){
62568             this.fly(r).removeClass(cls);
62569         }
62570     },
62571
62572     // private
62573     removeRow : function(row){
62574         Ext.removeNode(this.getRow(row));
62575         this.syncFocusEl(row);
62576     },
62577     
62578     // private
62579     removeRows : function(firstRow, lastRow){
62580         var bd = this.mainBody.dom;
62581         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
62582             Ext.removeNode(bd.childNodes[firstRow]);
62583         }
62584         this.syncFocusEl(firstRow);
62585     },
62586
62587     // scrolling stuff
62588
62589     // private
62590     getScrollState : function(){
62591         var sb = this.scroller.dom;
62592         return {left: sb.scrollLeft, top: sb.scrollTop};
62593     },
62594
62595     // private
62596     restoreScroll : function(state){
62597         var sb = this.scroller.dom;
62598         sb.scrollLeft = state.left;
62599         sb.scrollTop = state.top;
62600     },
62601
62602     /**
62603      * Scrolls the grid to the top
62604      */
62605     scrollToTop : function(){
62606         this.scroller.dom.scrollTop = 0;
62607         this.scroller.dom.scrollLeft = 0;
62608     },
62609
62610     // private
62611     syncScroll : function(){
62612       this.syncHeaderScroll();
62613       var mb = this.scroller.dom;
62614         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
62615     },
62616
62617     // private
62618     syncHeaderScroll : function(){
62619         var mb = this.scroller.dom;
62620         this.innerHd.scrollLeft = mb.scrollLeft;
62621         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
62622     },
62623
62624     // private
62625     updateSortIcon : function(col, dir){
62626         var sc = this.sortClasses;
62627         var hds = this.mainHd.select('td').removeClass(sc);
62628         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
62629     },
62630
62631     // private
62632     updateAllColumnWidths : function(){
62633         var tw = this.getTotalWidth(),
62634             clen = this.cm.getColumnCount(),
62635             ws = [],
62636             len,
62637             i;
62638         for(i = 0; i < clen; i++){
62639             ws[i] = this.getColumnWidth(i);
62640         }
62641         this.innerHd.firstChild.style.width = this.getOffsetWidth();
62642         this.innerHd.firstChild.firstChild.style.width = tw;
62643         this.mainBody.dom.style.width = tw;
62644         for(i = 0; i < clen; i++){
62645             var hd = this.getHeaderCell(i);
62646             hd.style.width = ws[i];
62647         }
62648
62649         var ns = this.getRows(), row, trow;
62650         for(i = 0, len = ns.length; i < len; i++){
62651             row = ns[i];
62652             row.style.width = tw;
62653             if(row.firstChild){
62654                 row.firstChild.style.width = tw;
62655                 trow = row.firstChild.rows[0];
62656                 for (var j = 0; j < clen; j++) {
62657                    trow.childNodes[j].style.width = ws[j];
62658                 }
62659             }
62660         }
62661
62662         this.onAllColumnWidthsUpdated(ws, tw);
62663     },
62664
62665     // private
62666     updateColumnWidth : function(col, width){
62667         var w = this.getColumnWidth(col);
62668         var tw = this.getTotalWidth();
62669         this.innerHd.firstChild.style.width = this.getOffsetWidth();
62670         this.innerHd.firstChild.firstChild.style.width = tw;
62671         this.mainBody.dom.style.width = tw;
62672         var hd = this.getHeaderCell(col);
62673         hd.style.width = w;
62674
62675         var ns = this.getRows(), row;
62676         for(var i = 0, len = ns.length; i < len; i++){
62677             row = ns[i];
62678             row.style.width = tw;
62679             if(row.firstChild){
62680                 row.firstChild.style.width = tw;
62681                 row.firstChild.rows[0].childNodes[col].style.width = w;
62682             }
62683         }
62684
62685         this.onColumnWidthUpdated(col, w, tw);
62686     },
62687
62688     // private
62689     updateColumnHidden : function(col, hidden){
62690         var tw = this.getTotalWidth();
62691         this.innerHd.firstChild.style.width = this.getOffsetWidth();
62692         this.innerHd.firstChild.firstChild.style.width = tw;
62693         this.mainBody.dom.style.width = tw;
62694         var display = hidden ? 'none' : '';
62695
62696         var hd = this.getHeaderCell(col);
62697         hd.style.display = display;
62698
62699         var ns = this.getRows(), row;
62700         for(var i = 0, len = ns.length; i < len; i++){
62701             row = ns[i];
62702             row.style.width = tw;
62703             if(row.firstChild){
62704                 row.firstChild.style.width = tw;
62705                 row.firstChild.rows[0].childNodes[col].style.display = display;
62706             }
62707         }
62708
62709         this.onColumnHiddenUpdated(col, hidden, tw);
62710         delete this.lastViewWidth; // force recalc
62711         this.layout();
62712     },
62713
62714     // private
62715     doRender : function(cs, rs, ds, startRow, colCount, stripe){
62716         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
62717         var tstyle = 'width:'+this.getTotalWidth()+';';
62718         // buffers
62719         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
62720         for(var j = 0, len = rs.length; j < len; j++){
62721             r = rs[j]; cb = [];
62722             var rowIndex = (j+startRow);
62723             for(var i = 0; i < colCount; i++){
62724                 c = cs[i];
62725                 p.id = c.id;
62726                 p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
62727                 p.attr = p.cellAttr = '';
62728                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
62729                 p.style = c.style;
62730                 if(Ext.isEmpty(p.value)){
62731                     p.value = '&#160;';
62732                 }
62733                 if(this.markDirty && r.dirty && typeof r.modified[c.name] !== 'undefined'){
62734                     p.css += ' x-grid3-dirty-cell';
62735                 }
62736                 cb[cb.length] = ct.apply(p);
62737             }
62738             var alt = [];
62739             if(stripe && ((rowIndex+1) % 2 === 0)){
62740                 alt[0] = 'x-grid3-row-alt';
62741             }
62742             if(r.dirty){
62743                 alt[1] = ' x-grid3-dirty-row';
62744             }
62745             rp.cols = colCount;
62746             if(this.getRowClass){
62747                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
62748             }
62749             rp.alt = alt.join(' ');
62750             rp.cells = cb.join('');
62751             buf[buf.length] =  rt.apply(rp);
62752         }
62753         return buf.join('');
62754     },
62755
62756     // private
62757     processRows : function(startRow, skipStripe){
62758         if(!this.ds || this.ds.getCount() < 1){
62759             return;
62760         }
62761         var rows = this.getRows();
62762         skipStripe = skipStripe || !this.grid.stripeRows;
62763         startRow = startRow || 0;
62764         Ext.each(rows, function(row, idx){
62765             row.rowIndex = idx;
62766             if(!skipStripe){
62767                 row.className = row.className.replace(this.rowClsRe, ' ');
62768                 if ((idx + 1) % 2 === 0){
62769                     row.className += ' x-grid3-row-alt';
62770                 }
62771             }
62772         }, this);
62773         // add first/last-row classes
62774         if(startRow === 0){
62775             Ext.fly(rows[0]).addClass(this.firstRowCls);
62776         }
62777         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
62778     },
62779
62780     afterRender : function(){
62781         if(!this.ds || !this.cm){
62782             return;
62783         }
62784         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
62785         this.processRows(0, true);
62786
62787         if(this.deferEmptyText !== true){
62788             this.applyEmptyText();
62789         }
62790     },
62791
62792     // private
62793     renderUI : function(){
62794
62795         var header = this.renderHeaders();
62796         var body = this.templates.body.apply({rows:'&#160;'});
62797
62798
62799         var html = this.templates.master.apply({
62800             body: body,
62801             header: header,
62802             ostyle: 'width:'+this.getOffsetWidth()+';',
62803             bstyle: 'width:'+this.getTotalWidth()+';'
62804         });
62805
62806         var g = this.grid;
62807
62808         g.getGridEl().dom.innerHTML = html;
62809
62810         this.initElements();
62811
62812         // get mousedowns early
62813         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
62814         this.mainHd.on({
62815             scope: this,
62816             mouseover: this.handleHdOver,
62817             mouseout: this.handleHdOut,
62818             mousemove: this.handleHdMove
62819         });
62820
62821         this.scroller.on('scroll', this.syncScroll,  this);
62822         if(g.enableColumnResize !== false){
62823             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
62824         }
62825
62826         if(g.enableColumnMove){
62827             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
62828             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
62829         }
62830
62831         if(g.enableHdMenu !== false){
62832             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
62833             this.hmenu.add(
62834                 {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
62835                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
62836             );
62837             if(g.enableColumnHide !== false){
62838                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
62839                 this.colMenu.on({
62840                     scope: this,
62841                     beforeshow: this.beforeColMenuShow,
62842                     itemclick: this.handleHdMenuClick
62843                 });
62844                 this.hmenu.add('-', {
62845                     itemId:'columns',
62846                     hideOnClick: false,
62847                     text: this.columnsText,
62848                     menu: this.colMenu,
62849                     iconCls: 'x-cols-icon'
62850                 });
62851             }
62852             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
62853         }
62854
62855         if(g.trackMouseOver){
62856             this.mainBody.on({
62857                 scope: this,
62858                 mouseover: this.onRowOver,
62859                 mouseout: this.onRowOut
62860             });
62861         }
62862
62863         if(g.enableDragDrop || g.enableDrag){
62864             this.dragZone = new Ext.grid.GridDragZone(g, {
62865                 ddGroup : g.ddGroup || 'GridDD'
62866             });
62867         }
62868
62869         this.updateHeaderSortState();
62870
62871     },
62872
62873     // private
62874     layout : function(){
62875         if(!this.mainBody){
62876             return; // not rendered
62877         }
62878         var g = this.grid;
62879         var c = g.getGridEl();
62880         var csize = c.getSize(true);
62881         var vw = csize.width;
62882
62883         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
62884             return;
62885         }
62886         
62887         if(g.autoHeight){
62888             this.scroller.dom.style.overflow = 'visible';
62889             if(Ext.isWebKit){
62890                 this.scroller.dom.style.position = 'static';
62891             }
62892         }else{
62893             this.el.setSize(csize.width, csize.height);
62894
62895             var hdHeight = this.mainHd.getHeight();
62896             var vh = csize.height - (hdHeight);
62897
62898             this.scroller.setSize(vw, vh);
62899             if(this.innerHd){
62900                 this.innerHd.style.width = (vw)+'px';
62901             }
62902         }
62903         if(this.forceFit){
62904             if(this.lastViewWidth != vw){
62905                 this.fitColumns(false, false);
62906                 this.lastViewWidth = vw;
62907             }
62908         }else {
62909             this.autoExpand();
62910             this.syncHeaderScroll();
62911         }
62912         this.onLayout(vw, vh);
62913     },
62914
62915     // template functions for subclasses and plugins
62916     // these functions include precalculated values
62917     onLayout : function(vw, vh){
62918         // do nothing
62919     },
62920
62921     onColumnWidthUpdated : function(col, w, tw){
62922         //template method
62923     },
62924
62925     onAllColumnWidthsUpdated : function(ws, tw){
62926         //template method
62927     },
62928
62929     onColumnHiddenUpdated : function(col, hidden, tw){
62930         // template method
62931     },
62932
62933     updateColumnText : function(col, text){
62934         // template method
62935     },
62936
62937     afterMove : function(colIndex){
62938         // template method
62939     },
62940
62941     /* ----------------------------------- Core Specific -------------------------------------------*/
62942     // private
62943     init : function(grid){
62944         this.grid = grid;
62945
62946         this.initTemplates();
62947         this.initData(grid.store, grid.colModel);
62948         this.initUI(grid);
62949     },
62950
62951     // private
62952     getColumnId : function(index){
62953       return this.cm.getColumnId(index);
62954     },
62955     
62956     // private 
62957     getOffsetWidth : function() {
62958         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
62959     },
62960     
62961     getScrollOffset: function(){
62962         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
62963     },
62964
62965     // private
62966     renderHeaders : function(){
62967         var cm = this.cm, 
62968             ts = this.templates,
62969             ct = ts.hcell,
62970             cb = [], 
62971             p = {},
62972             len = cm.getColumnCount(),
62973             last = len - 1;
62974             
62975         for(var i = 0; i < len; i++){
62976             p.id = cm.getColumnId(i);
62977             p.value = cm.getColumnHeader(i) || '';
62978             p.style = this.getColumnStyle(i, true);
62979             p.tooltip = this.getColumnTooltip(i);
62980             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
62981             if(cm.config[i].align == 'right'){
62982                 p.istyle = 'padding-right:16px';
62983             } else {
62984                 delete p.istyle;
62985             }
62986             cb[cb.length] = ct.apply(p);
62987         }
62988         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
62989     },
62990
62991     // private
62992     getColumnTooltip : function(i){
62993         var tt = this.cm.getColumnTooltip(i);
62994         if(tt){
62995             if(Ext.QuickTips.isEnabled()){
62996                 return 'ext:qtip="'+tt+'"';
62997             }else{
62998                 return 'title="'+tt+'"';
62999             }
63000         }
63001         return '';
63002     },
63003
63004     // private
63005     beforeUpdate : function(){
63006         this.grid.stopEditing(true);
63007     },
63008
63009     // private
63010     updateHeaders : function(){
63011         this.innerHd.firstChild.innerHTML = this.renderHeaders();
63012         this.innerHd.firstChild.style.width = this.getOffsetWidth();
63013         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
63014     },
63015
63016     /**
63017      * Focuses the specified row.
63018      * @param {Number} row The row index
63019      */
63020     focusRow : function(row){
63021         this.focusCell(row, 0, false);
63022     },
63023
63024     /**
63025      * Focuses the specified cell.
63026      * @param {Number} row The row index
63027      * @param {Number} col The column index
63028      */
63029     focusCell : function(row, col, hscroll){
63030         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
63031         if(Ext.isGecko){
63032             this.focusEl.focus();
63033         }else{
63034             this.focusEl.focus.defer(1, this.focusEl);
63035         }
63036     },
63037
63038     resolveCell : function(row, col, hscroll){
63039         if(typeof row != "number"){
63040             row = row.rowIndex;
63041         }
63042         if(!this.ds){
63043             return null;
63044         }
63045         if(row < 0 || row >= this.ds.getCount()){
63046             return null;
63047         }
63048         col = (col !== undefined ? col : 0);
63049
63050         var rowEl = this.getRow(row),
63051             cm = this.cm,
63052             colCount = cm.getColumnCount(),
63053             cellEl;
63054         if(!(hscroll === false && col === 0)){
63055             while(col < colCount && cm.isHidden(col)){
63056                 col++;
63057             }
63058             cellEl = this.getCell(row, col);
63059         }
63060
63061         return {row: rowEl, cell: cellEl};
63062     },
63063
63064     getResolvedXY : function(resolved){
63065         if(!resolved){
63066             return null;
63067         }
63068         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
63069         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
63070     },
63071
63072     syncFocusEl : function(row, col, hscroll){
63073         var xy = row;
63074         if(!Ext.isArray(xy)){
63075             row = Math.min(row, Math.max(0, this.getRows().length-1));
63076             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
63077         }
63078         this.focusEl.setXY(xy||this.scroller.getXY());
63079     },
63080
63081     ensureVisible : function(row, col, hscroll){
63082         var resolved = this.resolveCell(row, col, hscroll);
63083         if(!resolved || !resolved.row){
63084             return;
63085         }
63086
63087         var rowEl = resolved.row, 
63088             cellEl = resolved.cell,
63089             c = this.scroller.dom,
63090             ctop = 0,
63091             p = rowEl, 
63092             stop = this.el.dom;
63093             
63094         while(p && p != stop){
63095             ctop += p.offsetTop;
63096             p = p.offsetParent;
63097         }
63098         
63099         ctop -= this.mainHd.dom.offsetHeight;
63100         stop = parseInt(c.scrollTop, 10);
63101         
63102         var cbot = ctop + rowEl.offsetHeight,
63103             ch = c.clientHeight,
63104             sbot = stop + ch;
63105         
63106
63107         if(ctop < stop){
63108           c.scrollTop = ctop;
63109         }else if(cbot > sbot){
63110             c.scrollTop = cbot-ch;
63111         }
63112
63113         if(hscroll !== false){
63114             var cleft = parseInt(cellEl.offsetLeft, 10);
63115             var cright = cleft + cellEl.offsetWidth;
63116
63117             var sleft = parseInt(c.scrollLeft, 10);
63118             var sright = sleft + c.clientWidth;
63119             if(cleft < sleft){
63120                 c.scrollLeft = cleft;
63121             }else if(cright > sright){
63122                 c.scrollLeft = cright-c.clientWidth;
63123             }
63124         }
63125         return this.getResolvedXY(resolved);
63126     },
63127
63128     // private
63129     insertRows : function(dm, firstRow, lastRow, isUpdate){
63130         var last = dm.getCount() - 1;
63131         if(!isUpdate && firstRow === 0 && lastRow >= last){
63132             this.refresh();
63133         }else{
63134             if(!isUpdate){
63135                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
63136             }
63137             var html = this.renderRows(firstRow, lastRow),
63138                 before = this.getRow(firstRow);
63139             if(before){
63140                 if(firstRow === 0){
63141                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
63142                 }
63143                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
63144             }else{
63145                 var r = this.getRow(last - 1);
63146                 if(r){
63147                     Ext.fly(r).removeClass(this.lastRowCls);
63148                 }
63149                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
63150             }
63151             if(!isUpdate){
63152                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
63153                 this.processRows(firstRow);
63154             }else if(firstRow === 0 || firstRow >= last){
63155                 //ensure first/last row is kept after an update.
63156                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
63157             }
63158         }
63159         this.syncFocusEl(firstRow);
63160     },
63161
63162     // private
63163     deleteRows : function(dm, firstRow, lastRow){
63164         if(dm.getRowCount()<1){
63165             this.refresh();
63166         }else{
63167             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
63168
63169             this.removeRows(firstRow, lastRow);
63170
63171             this.processRows(firstRow);
63172             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
63173         }
63174     },
63175
63176     // private
63177     getColumnStyle : function(col, isHeader){
63178         var style = !isHeader ? (this.cm.config[col].css || '') : '';
63179         style += 'width:'+this.getColumnWidth(col)+';';
63180         if(this.cm.isHidden(col)){
63181             style += 'display:none;';
63182         }
63183         var align = this.cm.config[col].align;
63184         if(align){
63185             style += 'text-align:'+align+';';
63186         }
63187         return style;
63188     },
63189
63190     // private
63191     getColumnWidth : function(col){
63192         var w = this.cm.getColumnWidth(col);
63193         if(typeof w == 'number'){
63194             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
63195         }
63196         return w;
63197     },
63198
63199     // private
63200     getTotalWidth : function(){
63201         return this.cm.getTotalWidth()+'px';
63202     },
63203
63204     // private
63205     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
63206         var cm = this.cm, i;
63207         var tw = cm.getTotalWidth(false);
63208         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
63209
63210         if(aw < 20){ // not initialized, so don't screw up the default widths
63211             return;
63212         }
63213         var extra = aw - tw;
63214
63215         if(extra === 0){
63216             return false;
63217         }
63218
63219         var vc = cm.getColumnCount(true);
63220         var ac = vc-(typeof omitColumn == 'number' ? 1 : 0);
63221         if(ac === 0){
63222             ac = 1;
63223             omitColumn = undefined;
63224         }
63225         var colCount = cm.getColumnCount();
63226         var cols = [];
63227         var extraCol = 0;
63228         var width = 0;
63229         var w;
63230         for (i = 0; i < colCount; i++){
63231             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
63232                 w = cm.getColumnWidth(i);
63233                 cols.push(i);
63234                 extraCol = i;
63235                 cols.push(w);
63236                 width += w;
63237             }
63238         }
63239         var frac = (aw - cm.getTotalWidth())/width;
63240         while (cols.length){
63241             w = cols.pop();
63242             i = cols.pop();
63243             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
63244         }
63245
63246         if((tw = cm.getTotalWidth(false)) > aw){
63247             var adjustCol = ac != vc ? omitColumn : extraCol;
63248              cm.setColumnWidth(adjustCol, Math.max(1,
63249                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
63250         }
63251
63252         if(preventRefresh !== true){
63253             this.updateAllColumnWidths();
63254         }
63255
63256
63257         return true;
63258     },
63259
63260     // private
63261     autoExpand : function(preventUpdate){
63262         var g = this.grid, cm = this.cm;
63263         if(!this.userResized && g.autoExpandColumn){
63264             var tw = cm.getTotalWidth(false);
63265             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
63266             if(tw != aw){
63267                 var ci = cm.getIndexById(g.autoExpandColumn);
63268                 var currentWidth = cm.getColumnWidth(ci);
63269                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
63270                 if(cw != currentWidth){
63271                     cm.setColumnWidth(ci, cw, true);
63272                     if(preventUpdate !== true){
63273                         this.updateColumnWidth(ci, cw);
63274                     }
63275                 }
63276             }
63277         }
63278     },
63279
63280     // private
63281     getColumnData : function(){
63282         // build a map for all the columns
63283         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
63284         for(var i = 0; i < colCount; i++){
63285             var name = cm.getDataIndex(i);
63286             cs[i] = {
63287                 name : (typeof name == 'undefined' ? this.ds.fields.get(i).name : name),
63288                 renderer : cm.getRenderer(i),
63289                 id : cm.getColumnId(i),
63290                 style : this.getColumnStyle(i)
63291             };
63292         }
63293         return cs;
63294     },
63295
63296     // private
63297     renderRows : function(startRow, endRow){
63298         // pull in all the crap needed to render rows
63299         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
63300         var colCount = cm.getColumnCount();
63301
63302         if(ds.getCount() < 1){
63303             return '';
63304         }
63305
63306         var cs = this.getColumnData();
63307
63308         startRow = startRow || 0;
63309         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63310
63311         // records to render
63312         var rs = ds.getRange(startRow, endRow);
63313
63314         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63315     },
63316
63317     // private
63318     renderBody : function(){
63319         var markup = this.renderRows() || '&#160;';
63320         return this.templates.body.apply({rows: markup});
63321     },
63322
63323     // private
63324     refreshRow : function(record){
63325         var ds = this.ds, index;
63326         if(typeof record == 'number'){
63327             index = record;
63328             record = ds.getAt(index);
63329             if(!record){
63330                 return;
63331             }
63332         }else{
63333             index = ds.indexOf(record);
63334             if(index < 0){
63335                 return;
63336             }
63337         }
63338         this.insertRows(ds, index, index, true);
63339         this.getRow(index).rowIndex = index;
63340         this.onRemove(ds, record, index+1, true);
63341         this.fireEvent('rowupdated', this, index, record);
63342     },
63343
63344     /**
63345      * Refreshs the grid UI
63346      * @param {Boolean} headersToo (optional) True to also refresh the headers
63347      */
63348     refresh : function(headersToo){
63349         this.fireEvent('beforerefresh', this);
63350         this.grid.stopEditing(true);
63351
63352         var result = this.renderBody();
63353         this.mainBody.update(result).setWidth(this.getTotalWidth());
63354         if(headersToo === true){
63355             this.updateHeaders();
63356             this.updateHeaderSortState();
63357         }
63358         this.processRows(0, true);
63359         this.layout();
63360         this.applyEmptyText();
63361         this.fireEvent('refresh', this);
63362     },
63363
63364     // private
63365     applyEmptyText : function(){
63366         if(this.emptyText && !this.hasRows()){
63367             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
63368         }
63369     },
63370
63371     // private
63372     updateHeaderSortState : function(){
63373         var state = this.ds.getSortState();
63374         if(!state){
63375             return;
63376         }
63377         if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){
63378             this.grid.fireEvent('sortchange', this.grid, state);
63379         }
63380         this.sortState = state;
63381         var sortColumn = this.cm.findColumnIndex(state.field);
63382         if(sortColumn != -1){
63383             var sortDir = state.direction;
63384             this.updateSortIcon(sortColumn, sortDir);
63385         }
63386     },
63387
63388     // private
63389     destroy : function(){
63390         if(this.colMenu){
63391             Ext.menu.MenuMgr.unregister(this.colMenu);
63392             this.colMenu.destroy();
63393             delete this.colMenu;
63394         }
63395         if(this.hmenu){
63396             Ext.menu.MenuMgr.unregister(this.hmenu);
63397             this.hmenu.destroy();
63398             delete this.hmenu;
63399         }
63400
63401         this.initData(null, null);
63402         this.purgeListeners();
63403         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
63404
63405         if(this.grid.enableColumnMove){
63406             Ext.destroy(
63407                 this.columnDrag.el,
63408                 this.columnDrag.proxy.ghost,
63409                 this.columnDrag.proxy.el,
63410                 this.columnDrop.el,
63411                 this.columnDrop.proxyTop,
63412                 this.columnDrop.proxyBottom,
63413                 this.columnDrag.dragData.ddel,
63414                 this.columnDrag.dragData.header
63415             );
63416             if (this.columnDrag.proxy.anim) {
63417                 Ext.destroy(this.columnDrag.proxy.anim);
63418             }
63419             delete this.columnDrag.proxy.ghost;
63420             delete this.columnDrag.dragData.ddel;
63421             delete this.columnDrag.dragData.header;
63422             this.columnDrag.destroy();
63423             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
63424             delete this.columnDrag._domRef;
63425
63426             delete this.columnDrop.proxyTop;
63427             delete this.columnDrop.proxyBottom;
63428             this.columnDrop.destroy();
63429             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
63430             delete this.columnDrop._domRef;
63431             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
63432         }
63433
63434         if (this.splitone){ // enableColumnResize
63435             this.splitone.destroy();
63436             delete this.splitone._domRef;
63437             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
63438         }
63439
63440         Ext.fly(this.innerHd).removeAllListeners();
63441         Ext.removeNode(this.innerHd);
63442         delete this.innerHd;
63443
63444         Ext.destroy(
63445             this.el,
63446             this.mainWrap,
63447             this.mainHd,
63448             this.scroller,
63449             this.mainBody,
63450             this.focusEl,
63451             this.resizeMarker,
63452             this.resizeProxy,
63453             this.activeHdBtn,
63454             this.dragZone,
63455             this.splitZone,
63456             this._flyweight
63457         );
63458
63459         delete this.grid.container;
63460
63461         if(this.dragZone){
63462             this.dragZone.destroy();
63463         }
63464
63465         Ext.dd.DDM.currentTarget = null;
63466         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
63467
63468         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
63469     },
63470
63471     // private
63472     onDenyColumnHide : function(){
63473
63474     },
63475
63476     // private
63477     render : function(){
63478         if(this.autoFill){
63479             var ct = this.grid.ownerCt;
63480             if (ct && ct.getLayout()){
63481                 ct.on('afterlayout', function(){ 
63482                     this.fitColumns(true, true);
63483                     this.updateHeaders(); 
63484                 }, this, {single: true}); 
63485             }else{ 
63486                 this.fitColumns(true, true); 
63487             }
63488         }else if(this.forceFit){
63489             this.fitColumns(true, false);
63490         }else if(this.grid.autoExpandColumn){
63491             this.autoExpand(true);
63492         }
63493
63494         this.renderUI();
63495     },
63496
63497     /* --------------------------------- Model Events and Handlers --------------------------------*/
63498     // private
63499     initData : function(ds, cm){
63500         if(this.ds){
63501             this.ds.un('load', this.onLoad, this);
63502             this.ds.un('datachanged', this.onDataChange, this);
63503             this.ds.un('add', this.onAdd, this);
63504             this.ds.un('remove', this.onRemove, this);
63505             this.ds.un('update', this.onUpdate, this);
63506             this.ds.un('clear', this.onClear, this);
63507             if(this.ds !== ds && this.ds.autoDestroy){
63508                 this.ds.destroy();
63509             }
63510         }
63511         if(ds){
63512             ds.on({
63513                 scope: this,
63514                 load: this.onLoad,
63515                 datachanged: this.onDataChange,
63516                 add: this.onAdd,
63517                 remove: this.onRemove,
63518                 update: this.onUpdate,
63519                 clear: this.onClear
63520             });
63521         }
63522         this.ds = ds;
63523
63524         if(this.cm){
63525             this.cm.un('configchange', this.onColConfigChange, this);
63526             this.cm.un('widthchange', this.onColWidthChange, this);
63527             this.cm.un('headerchange', this.onHeaderChange, this);
63528             this.cm.un('hiddenchange', this.onHiddenChange, this);
63529             this.cm.un('columnmoved', this.onColumnMove, this);
63530         }
63531         if(cm){
63532             delete this.lastViewWidth;
63533             cm.on({
63534                 scope: this,
63535                 configchange: this.onColConfigChange,
63536                 widthchange: this.onColWidthChange,
63537                 headerchange: this.onHeaderChange,
63538                 hiddenchange: this.onHiddenChange,
63539                 columnmoved: this.onColumnMove
63540             });
63541         }
63542         this.cm = cm;
63543     },
63544
63545     // private
63546     onDataChange : function(){
63547         this.refresh();
63548         this.updateHeaderSortState();
63549         this.syncFocusEl(0);
63550     },
63551
63552     // private
63553     onClear : function(){
63554         this.refresh();
63555         this.syncFocusEl(0);
63556     },
63557
63558     // private
63559     onUpdate : function(ds, record){
63560         this.refreshRow(record);
63561     },
63562
63563     // private
63564     onAdd : function(ds, records, index){
63565         this.insertRows(ds, index, index + (records.length-1));
63566     },
63567
63568     // private
63569     onRemove : function(ds, record, index, isUpdate){
63570         if(isUpdate !== true){
63571             this.fireEvent('beforerowremoved', this, index, record);
63572         }
63573         this.removeRow(index);
63574         if(isUpdate !== true){
63575             this.processRows(index);
63576             this.applyEmptyText();
63577             this.fireEvent('rowremoved', this, index, record);
63578         }
63579     },
63580
63581     // private
63582     onLoad : function(){
63583         this.scrollToTop.defer(Ext.isGecko ? 1 : 0, this);
63584     },
63585
63586     // private
63587     onColWidthChange : function(cm, col, width){
63588         this.updateColumnWidth(col, width);
63589     },
63590
63591     // private
63592     onHeaderChange : function(cm, col, text){
63593         this.updateHeaders();
63594     },
63595
63596     // private
63597     onHiddenChange : function(cm, col, hidden){
63598         this.updateColumnHidden(col, hidden);
63599     },
63600
63601     // private
63602     onColumnMove : function(cm, oldIndex, newIndex){
63603         this.indexMap = null;
63604         var s = this.getScrollState();
63605         this.refresh(true);
63606         this.restoreScroll(s);
63607         this.afterMove(newIndex);
63608         this.grid.fireEvent('columnmove', oldIndex, newIndex);
63609     },
63610
63611     // private
63612     onColConfigChange : function(){
63613         delete this.lastViewWidth;
63614         this.indexMap = null;
63615         this.refresh(true);
63616     },
63617
63618     /* -------------------- UI Events and Handlers ------------------------------ */
63619     // private
63620     initUI : function(grid){
63621         grid.on('headerclick', this.onHeaderClick, this);
63622     },
63623
63624     // private
63625     initEvents : function(){
63626     },
63627
63628     // private
63629     onHeaderClick : function(g, index){
63630         if(this.headersDisabled || !this.cm.isSortable(index)){
63631             return;
63632         }
63633         g.stopEditing(true);
63634         g.store.sort(this.cm.getDataIndex(index));
63635     },
63636
63637     // private
63638     onRowOver : function(e, t){
63639         var row;
63640         if((row = this.findRowIndex(t)) !== false){
63641             this.addRowClass(row, 'x-grid3-row-over');
63642         }
63643     },
63644
63645     // private
63646     onRowOut : function(e, t){
63647         var row;
63648         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
63649             this.removeRowClass(row, 'x-grid3-row-over');
63650         }
63651     },
63652
63653     // private
63654     handleWheel : function(e){
63655         e.stopPropagation();
63656     },
63657
63658     // private
63659     onRowSelect : function(row){
63660         this.addRowClass(row, this.selectedRowClass);
63661     },
63662
63663     // private
63664     onRowDeselect : function(row){
63665         this.removeRowClass(row, this.selectedRowClass);
63666     },
63667
63668     // private
63669     onCellSelect : function(row, col){
63670         var cell = this.getCell(row, col);
63671         if(cell){
63672             this.fly(cell).addClass('x-grid3-cell-selected');
63673         }
63674     },
63675
63676     // private
63677     onCellDeselect : function(row, col){
63678         var cell = this.getCell(row, col);
63679         if(cell){
63680             this.fly(cell).removeClass('x-grid3-cell-selected');
63681         }
63682     },
63683
63684     // private
63685     onColumnSplitterMoved : function(i, w){
63686         this.userResized = true;
63687         var cm = this.grid.colModel;
63688         cm.setColumnWidth(i, w, true);
63689
63690         if(this.forceFit){
63691             this.fitColumns(true, false, i);
63692             this.updateAllColumnWidths();
63693         }else{
63694             this.updateColumnWidth(i, w);
63695             this.syncHeaderScroll();
63696         }
63697
63698         this.grid.fireEvent('columnresize', i, w);
63699     },
63700
63701     // private
63702     handleHdMenuClick : function(item){
63703         var index = this.hdCtxIndex,
63704             cm = this.cm, 
63705             ds = this.ds,
63706             id = item.getItemId();
63707         switch(id){
63708             case 'asc':
63709                 ds.sort(cm.getDataIndex(index), 'ASC');
63710                 break;
63711             case 'desc':
63712                 ds.sort(cm.getDataIndex(index), 'DESC');
63713                 break;
63714             default:
63715                 index = cm.getIndexById(id.substr(4));
63716                 if(index != -1){
63717                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
63718                         this.onDenyColumnHide();
63719                         return false;
63720                     }
63721                     cm.setHidden(index, item.checked);
63722                 }
63723         }
63724         return true;
63725     },
63726
63727     // private
63728     isHideableColumn : function(c){
63729         return !c.hidden && !c.fixed;
63730     },
63731
63732     // private
63733     beforeColMenuShow : function(){
63734         var cm = this.cm,  colCount = cm.getColumnCount();
63735         this.colMenu.removeAll();
63736         for(var i = 0; i < colCount; i++){
63737             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
63738                 this.colMenu.add(new Ext.menu.CheckItem({
63739                     itemId: 'col-'+cm.getColumnId(i),
63740                     text: cm.getColumnHeader(i),
63741                     checked: !cm.isHidden(i),
63742                     hideOnClick:false,
63743                     disabled: cm.config[i].hideable === false
63744                 }));
63745             }
63746         }
63747     },
63748
63749     // private
63750     handleHdDown : function(e, t){
63751         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
63752             e.stopEvent();
63753             var hd = this.findHeaderCell(t);
63754             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
63755             var index = this.getCellIndex(hd);
63756             this.hdCtxIndex = index;
63757             var ms = this.hmenu.items, cm = this.cm;
63758             ms.get('asc').setDisabled(!cm.isSortable(index));
63759             ms.get('desc').setDisabled(!cm.isSortable(index));
63760             this.hmenu.on('hide', function(){
63761                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
63762             }, this, {single:true});
63763             this.hmenu.show(t, 'tl-bl?');
63764         }
63765     },
63766
63767     // private
63768     handleHdOver : function(e, t){
63769         var hd = this.findHeaderCell(t);
63770         if(hd && !this.headersDisabled){
63771             this.activeHdRef = t;
63772             this.activeHdIndex = this.getCellIndex(hd);
63773             var fly = this.fly(hd);
63774             this.activeHdRegion = fly.getRegion();
63775             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
63776                 fly.addClass('x-grid3-hd-over');
63777                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
63778                 if(this.activeHdBtn){
63779                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
63780                 }
63781             }
63782         }
63783     },
63784
63785     // private
63786     handleHdMove : function(e, t){
63787         var hd = this.findHeaderCell(this.activeHdRef);
63788         if(hd && !this.headersDisabled){
63789             var hw = this.splitHandleWidth || 5,
63790                 r = this.activeHdRegion,
63791                 x = e.getPageX(),
63792                 ss = hd.style,
63793                 cur = '';
63794             if(this.grid.enableColumnResize !== false){
63795                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
63796                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
63797                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
63798                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
63799                 }
63800             }
63801             ss.cursor = cur;
63802         }
63803     },
63804
63805     // private
63806     handleHdOut : function(e, t){
63807         var hd = this.findHeaderCell(t);
63808         if(hd && (!Ext.isIE || !e.within(hd, true))){
63809             this.activeHdRef = null;
63810             this.fly(hd).removeClass('x-grid3-hd-over');
63811             hd.style.cursor = '';
63812         }
63813     },
63814
63815     // private
63816     hasRows : function(){
63817         var fc = this.mainBody.dom.firstChild;
63818         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
63819     },
63820
63821     // back compat
63822     bind : function(d, c){
63823         this.initData(d, c);
63824     }
63825 });
63826
63827
63828 // private
63829 // This is a support class used internally by the Grid components
63830 Ext.grid.GridView.SplitDragZone = function(grid, hd){
63831     this.grid = grid;
63832     this.view = grid.getView();
63833     this.marker = this.view.resizeMarker;
63834     this.proxy = this.view.resizeProxy;
63835     Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
63836         'gridSplitters' + this.grid.getGridEl().id, {
63837         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
63838     });
63839     this.scroll = false;
63840     this.hw = this.view.splitHandleWidth || 5;
63841 };
63842 Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {
63843
63844     b4StartDrag : function(x, y){
63845         this.view.headersDisabled = true;
63846         var h = this.view.mainWrap.getHeight();
63847         this.marker.setHeight(h);
63848         this.marker.show();
63849         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
63850         this.proxy.setHeight(h);
63851         var w = this.cm.getColumnWidth(this.cellIndex);
63852         var minw = Math.max(w-this.grid.minColumnWidth, 0);
63853         this.resetConstraints();
63854         this.setXConstraint(minw, 1000);
63855         this.setYConstraint(0, 0);
63856         this.minX = x - minw;
63857         this.maxX = x + 1000;
63858         this.startPos = x;
63859         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
63860     },
63861
63862
63863     handleMouseDown : function(e){
63864         var t = this.view.findHeaderCell(e.getTarget());
63865         if(t){
63866             var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];
63867             var exy = e.getXY(), ex = exy[0];
63868             var w = t.offsetWidth, adjust = false;
63869             if((ex - x) <= this.hw){
63870                 adjust = -1;
63871             }else if((x+w) - ex <= this.hw){
63872                 adjust = 0;
63873             }
63874             if(adjust !== false){
63875                 this.cm = this.grid.colModel;
63876                 var ci = this.view.getCellIndex(t);
63877                 if(adjust == -1){
63878                   if (ci + adjust < 0) {
63879                     return;
63880                   }
63881                     while(this.cm.isHidden(ci+adjust)){
63882                         --adjust;
63883                         if(ci+adjust < 0){
63884                             return;
63885                         }
63886                     }
63887                 }
63888                 this.cellIndex = ci+adjust;
63889                 this.split = t.dom;
63890                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
63891                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
63892                 }
63893             }else if(this.view.columnDrag){
63894                 this.view.columnDrag.callHandleMouseDown(e);
63895             }
63896         }
63897     },
63898
63899     endDrag : function(e){
63900         this.marker.hide();
63901         var v = this.view;
63902         var endX = Math.max(this.minX, e.getPageX());
63903         var diff = endX - this.startPos;
63904         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
63905         setTimeout(function(){
63906             v.headersDisabled = false;
63907         }, 50);
63908     },
63909
63910     autoOffset : function(){
63911         this.setDelta(0,0);
63912     }
63913 });
63914 // private\r
63915 // This is a support class used internally by the Grid components\r
63916 Ext.grid.HeaderDragZone = function(grid, hd, hd2){\r
63917     this.grid = grid;\r
63918     this.view = grid.getView();\r
63919     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
63920     Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);\r
63921     if(hd2){\r
63922         this.setHandleElId(Ext.id(hd));\r
63923         this.setOuterHandleElId(Ext.id(hd2));\r
63924     }\r
63925     this.scroll = false;\r
63926 };\r
63927 Ext.extend(Ext.grid.HeaderDragZone, Ext.dd.DragZone, {\r
63928     maxDragWidth: 120,\r
63929     getDragData : function(e){\r
63930         var t = Ext.lib.Event.getTarget(e);\r
63931         var h = this.view.findHeaderCell(t);\r
63932         if(h){\r
63933             return {ddel: h.firstChild, header:h};\r
63934         }\r
63935         return false;\r
63936     },\r
63937 \r
63938     onInitDrag : function(e){\r
63939         this.view.headersDisabled = true;\r
63940         var clone = this.dragData.ddel.cloneNode(true);\r
63941         clone.id = Ext.id();\r
63942         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";\r
63943         this.proxy.update(clone);\r
63944         return true;\r
63945     },\r
63946 \r
63947     afterValidDrop : function(){\r
63948         var v = this.view;\r
63949         setTimeout(function(){\r
63950             v.headersDisabled = false;\r
63951         }, 50);\r
63952     },\r
63953 \r
63954     afterInvalidDrop : function(){\r
63955         var v = this.view;\r
63956         setTimeout(function(){\r
63957             v.headersDisabled = false;\r
63958         }, 50);\r
63959     }\r
63960 });\r
63961 \r
63962 // private\r
63963 // This is a support class used internally by the Grid components\r
63964 Ext.grid.HeaderDropZone = function(grid, hd, hd2){\r
63965     this.grid = grid;\r
63966     this.view = grid.getView();\r
63967     // split the proxies so they don't interfere with mouse events\r
63968     this.proxyTop = Ext.DomHelper.append(document.body, {\r
63969         cls:"col-move-top", html:"&#160;"\r
63970     }, true);\r
63971     this.proxyBottom = Ext.DomHelper.append(document.body, {\r
63972         cls:"col-move-bottom", html:"&#160;"\r
63973     }, true);\r
63974     this.proxyTop.hide = this.proxyBottom.hide = function(){\r
63975         this.setLeftTop(-100,-100);\r
63976         this.setStyle("visibility", "hidden");\r
63977     };\r
63978     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
63979     // temporarily disabled\r
63980     //Ext.dd.ScrollManager.register(this.view.scroller.dom);\r
63981     Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);\r
63982 };\r
63983 Ext.extend(Ext.grid.HeaderDropZone, Ext.dd.DropZone, {\r
63984     proxyOffsets : [-4, -9],\r
63985     fly: Ext.Element.fly,\r
63986 \r
63987     getTargetFromEvent : function(e){\r
63988         var t = Ext.lib.Event.getTarget(e);\r
63989         var cindex = this.view.findCellIndex(t);\r
63990         if(cindex !== false){\r
63991             return this.view.getHeaderCell(cindex);\r
63992         }\r
63993     },\r
63994 \r
63995     nextVisible : function(h){\r
63996         var v = this.view, cm = this.grid.colModel;\r
63997         h = h.nextSibling;\r
63998         while(h){\r
63999             if(!cm.isHidden(v.getCellIndex(h))){\r
64000                 return h;\r
64001             }\r
64002             h = h.nextSibling;\r
64003         }\r
64004         return null;\r
64005     },\r
64006 \r
64007     prevVisible : function(h){\r
64008         var v = this.view, cm = this.grid.colModel;\r
64009         h = h.prevSibling;\r
64010         while(h){\r
64011             if(!cm.isHidden(v.getCellIndex(h))){\r
64012                 return h;\r
64013             }\r
64014             h = h.prevSibling;\r
64015         }\r
64016         return null;\r
64017     },\r
64018 \r
64019     positionIndicator : function(h, n, e){\r
64020         var x = Ext.lib.Event.getPageX(e);\r
64021         var r = Ext.lib.Dom.getRegion(n.firstChild);\r
64022         var px, pt, py = r.top + this.proxyOffsets[1];\r
64023         if((r.right - x) <= (r.right-r.left)/2){\r
64024             px = r.right+this.view.borderWidth;\r
64025             pt = "after";\r
64026         }else{\r
64027             px = r.left;\r
64028             pt = "before";\r
64029         }\r
64030 \r
64031         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){\r
64032             return false;\r
64033         }\r
64034 \r
64035         px +=  this.proxyOffsets[0];\r
64036         this.proxyTop.setLeftTop(px, py);\r
64037         this.proxyTop.show();\r
64038         if(!this.bottomOffset){\r
64039             this.bottomOffset = this.view.mainHd.getHeight();\r
64040         }\r
64041         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);\r
64042         this.proxyBottom.show();\r
64043         return pt;\r
64044     },\r
64045 \r
64046     onNodeEnter : function(n, dd, e, data){\r
64047         if(data.header != n){\r
64048             this.positionIndicator(data.header, n, e);\r
64049         }\r
64050     },\r
64051 \r
64052     onNodeOver : function(n, dd, e, data){\r
64053         var result = false;\r
64054         if(data.header != n){\r
64055             result = this.positionIndicator(data.header, n, e);\r
64056         }\r
64057         if(!result){\r
64058             this.proxyTop.hide();\r
64059             this.proxyBottom.hide();\r
64060         }\r
64061         return result ? this.dropAllowed : this.dropNotAllowed;\r
64062     },\r
64063 \r
64064     onNodeOut : function(n, dd, e, data){\r
64065         this.proxyTop.hide();\r
64066         this.proxyBottom.hide();\r
64067     },\r
64068 \r
64069     onNodeDrop : function(n, dd, e, data){\r
64070         var h = data.header;\r
64071         if(h != n){\r
64072             var cm = this.grid.colModel;\r
64073             var x = Ext.lib.Event.getPageX(e);\r
64074             var r = Ext.lib.Dom.getRegion(n.firstChild);\r
64075             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";\r
64076             var oldIndex = this.view.getCellIndex(h);\r
64077             var newIndex = this.view.getCellIndex(n);\r
64078             if(pt == "after"){\r
64079                 newIndex++;\r
64080             }\r
64081             if(oldIndex < newIndex){\r
64082                 newIndex--;\r
64083             }\r
64084             cm.moveColumn(oldIndex, newIndex);\r
64085             return true;\r
64086         }\r
64087         return false;\r
64088     }\r
64089 });\r
64090 \r
64091 \r
64092 Ext.grid.GridView.ColumnDragZone = function(grid, hd){\r
64093     Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);\r
64094     this.proxy.el.addClass('x-grid3-col-dd');\r
64095 };\r
64096 \r
64097 Ext.extend(Ext.grid.GridView.ColumnDragZone, Ext.grid.HeaderDragZone, {\r
64098     handleMouseDown : function(e){\r
64099 \r
64100     },\r
64101 \r
64102     callHandleMouseDown : function(e){\r
64103         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);\r
64104     }\r
64105 });// private
64106 // This is a support class used internally by the Grid components
64107 Ext.grid.SplitDragZone = function(grid, hd, hd2){
64108     this.grid = grid;
64109     this.view = grid.getView();
64110     this.proxy = this.view.resizeProxy;
64111     Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
64112         "gridSplitters" + this.grid.getGridEl().id, {
64113         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
64114     });
64115     this.setHandleElId(Ext.id(hd));
64116     this.setOuterHandleElId(Ext.id(hd2));
64117     this.scroll = false;
64118 };
64119 Ext.extend(Ext.grid.SplitDragZone, Ext.dd.DDProxy, {
64120     fly: Ext.Element.fly,
64121
64122     b4StartDrag : function(x, y){
64123         this.view.headersDisabled = true;
64124         this.proxy.setHeight(this.view.mainWrap.getHeight());
64125         var w = this.cm.getColumnWidth(this.cellIndex);
64126         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64127         this.resetConstraints();
64128         this.setXConstraint(minw, 1000);
64129         this.setYConstraint(0, 0);
64130         this.minX = x - minw;
64131         this.maxX = x + 1000;
64132         this.startPos = x;
64133         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64134     },
64135
64136
64137     handleMouseDown : function(e){
64138         var ev = Ext.EventObject.setEvent(e);
64139         var t = this.fly(ev.getTarget());
64140         if(t.hasClass("x-grid-split")){
64141             this.cellIndex = this.view.getCellIndex(t.dom);
64142             this.split = t.dom;
64143             this.cm = this.grid.colModel;
64144             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64145                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64146             }
64147         }
64148     },
64149
64150     endDrag : function(e){
64151         this.view.headersDisabled = false;
64152         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
64153         var diff = endX - this.startPos;
64154         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
64155     },
64156
64157     autoOffset : function(){
64158         this.setDelta(0,0);
64159     }
64160 });/**
64161  * @class Ext.grid.GridDragZone
64162  * @extends Ext.dd.DragZone
64163  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
64164  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
64165  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
64166  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
64167  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
64168  * to process the {@link #getDragData data} which is provided.
64169  */
64170 Ext.grid.GridDragZone = function(grid, config){
64171     this.view = grid.getView();
64172     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64173     this.scroll = false;
64174     this.grid = grid;
64175     this.ddel = document.createElement('div');
64176     this.ddel.className = 'x-grid-dd-wrap';
64177 };
64178
64179 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
64180     ddGroup : "GridDD",
64181
64182     /**
64183      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
64184      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
64185      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
64186      * <p>The data object contains the following properties:<ul>
64187      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
64188      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
64189      * <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>
64190      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
64191      * </ul></p>
64192      */
64193     getDragData : function(e){
64194         var t = Ext.lib.Event.getTarget(e);
64195         var rowIndex = this.view.findRowIndex(t);
64196         if(rowIndex !== false){
64197             var sm = this.grid.selModel;
64198             if(!sm.isSelected(rowIndex) || e.hasModifier()){
64199                 sm.handleMouseDown(this.grid, rowIndex, e);
64200             }
64201             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
64202         }
64203         return false;
64204     },
64205
64206     /**
64207      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
64208      * of the data being dragged.</p>
64209      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
64210      */
64211     onInitDrag : function(e){
64212         var data = this.dragData;
64213         this.ddel.innerHTML = this.grid.getDragDropText();
64214         this.proxy.update(this.ddel);
64215         // fire start drag?
64216     },
64217
64218     /**
64219      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
64220      * the selected rows to show that they have not been dragged.
64221      */
64222     afterRepair : function(){
64223         this.dragging = false;
64224     },
64225
64226     /**
64227      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
64228      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
64229      * @param {EventObject} e The mouse up event
64230      * @return {Array} The xy location (e.g. [100, 200])
64231      */
64232     getRepairXY : function(e, data){
64233         return false;
64234     },
64235
64236     onEndDrag : function(data, e){
64237         // fire end drag?
64238     },
64239
64240     onValidDrop : function(dd, e, id){
64241         // fire drag drop?
64242         this.hideProxy();
64243     },
64244
64245     beforeInvalidDrop : function(e, id){
64246
64247     }
64248 });
64249 /**
64250  * @class Ext.grid.ColumnModel
64251  * @extends Ext.util.Observable
64252  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
64253  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
64254  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
64255  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
64256  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
64257  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
64258  * <pre><code>
64259 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
64260  * </code></pre>
64261  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
64262  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
64263  * each record in the store is indexed into the ColumnModel.</p>
64264  * <p>There are two ways to initialize the ColumnModel class:</p>
64265  * <p><u>Initialization Method 1: an Array</u></p>
64266 <pre><code>
64267  var colModel = new Ext.grid.ColumnModel([
64268     { header: "Ticker", width: 60, sortable: true},
64269     { header: "Company Name", width: 150, sortable: true, id: 'company'},
64270     { header: "Market Cap.", width: 100, sortable: true},
64271     { header: "$ Sales", width: 100, sortable: true, renderer: money},
64272     { header: "Employees", width: 100, sortable: true, resizable: false}
64273  ]);
64274  </code></pre>
64275  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
64276  * objects to define the initial layout / display of the columns in the Grid. The order of each
64277  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
64278  * order of the column display.  A Column's display may be initially hidden using the
64279  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
64280  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
64281  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
64282  * {@link Ext.data.Store Store} the column draws its data from is configured through the
64283  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
64284  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
64285  * example above) it will use the column configuration's index in the Array as the index.</p>
64286  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
64287  * <p><u>Initialization Method 2: an Object</u></p>
64288  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
64289  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
64290  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
64291  * for all columns, e.g.:</p><pre><code>
64292  var colModel = new Ext.grid.ColumnModel({
64293     columns: [
64294         { header: "Ticker", width: 60, menuDisabled: false},
64295         { header: "Company Name", width: 150, id: 'company'},
64296         { header: "Market Cap."},
64297         { header: "$ Sales", renderer: money},
64298         { header: "Employees", resizable: false}
64299     ],
64300     defaults: {
64301         sortable: true,
64302         menuDisabled: true,
64303         width: 100
64304     },
64305     listeners: {
64306         {@link #hiddenchange}: function(cm, colIndex, hidden) {
64307             saveConfig(colIndex, hidden);
64308         }
64309     }
64310 });
64311  </code></pre>
64312  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
64313  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
64314  * option. This column could be styled by including the following css:</p><pre><code>
64315  //add this css *after* the core css is loaded
64316 .x-grid3-td-company {
64317     color: red; // entire column will have red font
64318 }
64319 // modify the header row only, adding an icon to the column header
64320 .x-grid3-hd-company {
64321     background: transparent
64322         url(../../resources/images/icons/silk/building.png)
64323         no-repeat 3px 3px ! important;
64324         padding-left:20px;
64325 }
64326  </code></pre>
64327  * Note that the "Company Name" column could be specified as the
64328  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
64329  * @constructor
64330  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
64331  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
64332  */
64333 Ext.grid.ColumnModel = function(config){
64334     /**
64335      * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
64336      * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
64337      * be specified.
64338      * @property config
64339      * @type Array
64340      */
64341     if(config.columns){
64342         Ext.apply(this, config);
64343         this.setConfig(config.columns, true);
64344     }else{
64345         this.setConfig(config, true);
64346     }
64347     this.addEvents(
64348         /**
64349          * @event widthchange
64350          * Fires when the width of a column is programmaticially changed using
64351          * <code>{@link #setColumnWidth}</code>.
64352          * Note internal resizing suppresses the event from firing. See also
64353          * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
64354          * @param {ColumnModel} this
64355          * @param {Number} columnIndex The column index
64356          * @param {Number} newWidth The new width
64357          */
64358         "widthchange",
64359         /**
64360          * @event headerchange
64361          * Fires when the text of a header changes.
64362          * @param {ColumnModel} this
64363          * @param {Number} columnIndex The column index
64364          * @param {String} newText The new header text
64365          */
64366         "headerchange",
64367         /**
64368          * @event hiddenchange
64369          * Fires when a column is hidden or "unhidden".
64370          * @param {ColumnModel} this
64371          * @param {Number} columnIndex The column index
64372          * @param {Boolean} hidden true if hidden, false otherwise
64373          */
64374         "hiddenchange",
64375         /**
64376          * @event columnmoved
64377          * Fires when a column is moved.
64378          * @param {ColumnModel} this
64379          * @param {Number} oldIndex
64380          * @param {Number} newIndex
64381          */
64382         "columnmoved",
64383         /**
64384          * @event configchange
64385          * Fires when the configuration is changed
64386          * @param {ColumnModel} this
64387          */
64388         "configchange"
64389     );
64390     Ext.grid.ColumnModel.superclass.constructor.call(this);
64391 };
64392 Ext.extend(Ext.grid.ColumnModel, Ext.util.Observable, {
64393     /**
64394      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
64395      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
64396      * <tt><b>{@link #defaults}</b></tt> config property.
64397      */
64398     defaultWidth: 100,
64399     /**
64400      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
64401      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
64402      * through the <tt><b>{@link #defaults}</b></tt> config property.
64403      */
64404     defaultSortable: false,
64405     /**
64406      * @cfg {Array} columns An Array of object literals.  The config options defined by
64407      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
64408      * individual column definition.
64409      */
64410     /**
64411      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
64412      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
64413      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
64414      */
64415
64416     /**
64417      * Returns the id of the column at the specified index.
64418      * @param {Number} index The column index
64419      * @return {String} the id
64420      */
64421     getColumnId : function(index){
64422         return this.config[index].id;
64423     },
64424
64425     getColumnAt : function(index){
64426         return this.config[index];
64427     },
64428
64429     /**
64430      * <p>Reconfigures this column model according to the passed Array of column definition objects.
64431      * For a description of the individual properties of a column definition object, see the
64432      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
64433      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
64434      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
64435      * @param {Array} config Array of Column definition objects.
64436      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
64437      * and destroys existing editors.
64438      */
64439     setConfig : function(config, initial){
64440         var i, c, len;
64441         if(!initial){ // cleanup
64442             delete this.totalWidth;
64443             for(i = 0, len = this.config.length; i < len; i++){
64444                 c = this.config[i];
64445                 if(c.editor){
64446                     c.editor.destroy();
64447                 }
64448             }
64449         }
64450
64451         // backward compatibility
64452         this.defaults = Ext.apply({
64453             width: this.defaultWidth,
64454             sortable: this.defaultSortable
64455         }, this.defaults);
64456
64457         this.config = config;
64458         this.lookup = {};
64459
64460         for(i = 0, len = config.length; i < len; i++){
64461             c = Ext.applyIf(config[i], this.defaults);
64462             // if no id, create one using column's ordinal position
64463             if(typeof c.id == 'undefined'){
64464                 c.id = i;
64465             }
64466             if(!c.isColumn){
64467                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
64468                 c = new Cls(c);
64469                 config[i] = c;
64470             }
64471             this.lookup[c.id] = c;
64472         }
64473         if(!initial){
64474             this.fireEvent('configchange', this);
64475         }
64476     },
64477
64478     /**
64479      * Returns the column for a specified id.
64480      * @param {String} id The column id
64481      * @return {Object} the column
64482      */
64483     getColumnById : function(id){
64484         return this.lookup[id];
64485     },
64486
64487     /**
64488      * Returns the index for a specified column id.
64489      * @param {String} id The column id
64490      * @return {Number} the index, or -1 if not found
64491      */
64492     getIndexById : function(id){
64493         for(var i = 0, len = this.config.length; i < len; i++){
64494             if(this.config[i].id == id){
64495                 return i;
64496             }
64497         }
64498         return -1;
64499     },
64500
64501     /**
64502      * Moves a column from one position to another.
64503      * @param {Number} oldIndex The index of the column to move.
64504      * @param {Number} newIndex The position at which to reinsert the coolumn.
64505      */
64506     moveColumn : function(oldIndex, newIndex){
64507         var c = this.config[oldIndex];
64508         this.config.splice(oldIndex, 1);
64509         this.config.splice(newIndex, 0, c);
64510         this.dataMap = null;
64511         this.fireEvent("columnmoved", this, oldIndex, newIndex);
64512     },
64513
64514     /**
64515      * Returns the number of columns.
64516      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
64517      * @return {Number}
64518      */
64519     getColumnCount : function(visibleOnly){
64520         if(visibleOnly === true){
64521             var c = 0;
64522             for(var i = 0, len = this.config.length; i < len; i++){
64523                 if(!this.isHidden(i)){
64524                     c++;
64525                 }
64526             }
64527             return c;
64528         }
64529         return this.config.length;
64530     },
64531
64532     /**
64533      * Returns the column configs that return true by the passed function that is called
64534      * with (columnConfig, index)
64535 <pre><code>
64536 // returns an array of column config objects for all hidden columns
64537 var columns = grid.getColumnModel().getColumnsBy(function(c){
64538   return c.hidden;
64539 });
64540 </code></pre>
64541      * @param {Function} fn
64542      * @param {Object} scope (optional)
64543      * @return {Array} result
64544      */
64545     getColumnsBy : function(fn, scope){
64546         var r = [];
64547         for(var i = 0, len = this.config.length; i < len; i++){
64548             var c = this.config[i];
64549             if(fn.call(scope||this, c, i) === true){
64550                 r[r.length] = c;
64551             }
64552         }
64553         return r;
64554     },
64555
64556     /**
64557      * Returns true if the specified column is sortable.
64558      * @param {Number} col The column index
64559      * @return {Boolean}
64560      */
64561     isSortable : function(col){
64562         return !!this.config[col].sortable;
64563     },
64564
64565     /**
64566      * Returns true if the specified column menu is disabled.
64567      * @param {Number} col The column index
64568      * @return {Boolean}
64569      */
64570     isMenuDisabled : function(col){
64571         return !!this.config[col].menuDisabled;
64572     },
64573
64574     /**
64575      * Returns the rendering (formatting) function defined for the column.
64576      * @param {Number} col The column index.
64577      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64578      */
64579     getRenderer : function(col){
64580         if(!this.config[col].renderer){
64581             return Ext.grid.ColumnModel.defaultRenderer;
64582         }
64583         return this.config[col].renderer;
64584     },
64585
64586     /**
64587      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
64588      * default formatting functions.
64589      * @param {Number} col The column index
64590      * @param {Function} fn The function to use to process the cell's raw data
64591      * to return HTML markup for the grid view. The render function is called with
64592      * the following parameters:<ul>
64593      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
64594      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
64595      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
64596      * <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
64597      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
64598      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
64599      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
64600      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
64601      * <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>
64602      */
64603     setRenderer : function(col, fn){
64604         this.config[col].renderer = fn;
64605     },
64606
64607     /**
64608      * Returns the width for the specified column.
64609      * @param {Number} col The column index
64610      * @return {Number}
64611      */
64612     getColumnWidth : function(col){
64613         return this.config[col].width;
64614     },
64615
64616     /**
64617      * Sets the width for a column.
64618      * @param {Number} col The column index
64619      * @param {Number} width The new width
64620      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
64621      * event. Defaults to false.
64622      */
64623     setColumnWidth : function(col, width, suppressEvent){
64624         this.config[col].width = width;
64625         this.totalWidth = null;
64626         if(!suppressEvent){
64627              this.fireEvent("widthchange", this, col, width);
64628         }
64629     },
64630
64631     /**
64632      * Returns the total width of all columns.
64633      * @param {Boolean} includeHidden True to include hidden column widths
64634      * @return {Number}
64635      */
64636     getTotalWidth : function(includeHidden){
64637         if(!this.totalWidth){
64638             this.totalWidth = 0;
64639             for(var i = 0, len = this.config.length; i < len; i++){
64640                 if(includeHidden || !this.isHidden(i)){
64641                     this.totalWidth += this.getColumnWidth(i);
64642                 }
64643             }
64644         }
64645         return this.totalWidth;
64646     },
64647
64648     /**
64649      * Returns the header for the specified column.
64650      * @param {Number} col The column index
64651      * @return {String}
64652      */
64653     getColumnHeader : function(col){
64654         return this.config[col].header;
64655     },
64656
64657     /**
64658      * Sets the header for a column.
64659      * @param {Number} col The column index
64660      * @param {String} header The new header
64661      */
64662     setColumnHeader : function(col, header){
64663         this.config[col].header = header;
64664         this.fireEvent("headerchange", this, col, header);
64665     },
64666
64667     /**
64668      * Returns the tooltip for the specified column.
64669      * @param {Number} col The column index
64670      * @return {String}
64671      */
64672     getColumnTooltip : function(col){
64673             return this.config[col].tooltip;
64674     },
64675     /**
64676      * Sets the tooltip for a column.
64677      * @param {Number} col The column index
64678      * @param {String} tooltip The new tooltip
64679      */
64680     setColumnTooltip : function(col, tooltip){
64681             this.config[col].tooltip = tooltip;
64682     },
64683
64684     /**
64685      * Returns the dataIndex for the specified column.
64686 <pre><code>
64687 // Get field name for the column
64688 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
64689 </code></pre>
64690      * @param {Number} col The column index
64691      * @return {String} The column's dataIndex
64692      */
64693     getDataIndex : function(col){
64694         return this.config[col].dataIndex;
64695     },
64696
64697     /**
64698      * Sets the dataIndex for a column.
64699      * @param {Number} col The column index
64700      * @param {String} dataIndex The new dataIndex
64701      */
64702     setDataIndex : function(col, dataIndex){
64703         this.config[col].dataIndex = dataIndex;
64704     },
64705
64706     /**
64707      * Finds the index of the first matching column for the given dataIndex.
64708      * @param {String} col The dataIndex to find
64709      * @return {Number} The column index, or -1 if no match was found
64710      */
64711     findColumnIndex : function(dataIndex){
64712         var c = this.config;
64713         for(var i = 0, len = c.length; i < len; i++){
64714             if(c[i].dataIndex == dataIndex){
64715                 return i;
64716             }
64717         }
64718         return -1;
64719     },
64720
64721     /**
64722      * Returns true if the cell is editable.
64723 <pre><code>
64724 var store = new Ext.data.Store({...});
64725 var colModel = new Ext.grid.ColumnModel({
64726   columns: [...],
64727   isCellEditable: function(col, row) {
64728     var record = store.getAt(row);
64729     if (record.get('readonly')) { // replace with your condition
64730       return false;
64731     }
64732     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
64733   }
64734 });
64735 var grid = new Ext.grid.GridPanel({
64736   store: store,
64737   colModel: colModel,
64738   ...
64739 });
64740 </code></pre>
64741      * @param {Number} colIndex The column index
64742      * @param {Number} rowIndex The row index
64743      * @return {Boolean}
64744      */
64745     isCellEditable : function(colIndex, rowIndex){
64746         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64747     },
64748
64749     /**
64750      * Returns the editor defined for the cell/column.
64751      * @param {Number} colIndex The column index
64752      * @param {Number} rowIndex The row index
64753      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
64754      * the {@link Ext.form.Field Field} used to edit the cell.
64755      */
64756     getCellEditor : function(colIndex, rowIndex){
64757         return this.config[colIndex].getCellEditor(rowIndex);
64758     },
64759
64760     /**
64761      * Sets if a column is editable.
64762      * @param {Number} col The column index
64763      * @param {Boolean} editable True if the column is editable
64764      */
64765     setEditable : function(col, editable){
64766         this.config[col].editable = editable;
64767     },
64768
64769     /**
64770      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
64771      * <tt>false</tt> otherwise.
64772      * @param {Number} colIndex The column index
64773      * @return {Boolean}
64774      */
64775     isHidden : function(colIndex){
64776         return !!this.config[colIndex].hidden; // ensure returns boolean
64777     },
64778
64779     /**
64780      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
64781      * <tt>false</tt> otherwise.
64782      * @param {Number} colIndex The column index
64783      * @return {Boolean}
64784      */
64785     isFixed : function(colIndex){
64786         return !!this.config[colIndex].fixed;
64787     },
64788
64789     /**
64790      * Returns true if the column can be resized
64791      * @return {Boolean}
64792      */
64793     isResizable : function(colIndex){
64794         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64795     },
64796     /**
64797      * Sets if a column is hidden.
64798 <pre><code>
64799 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
64800 </code></pre>
64801      * @param {Number} colIndex The column index
64802      * @param {Boolean} hidden True if the column is hidden
64803      */
64804     setHidden : function(colIndex, hidden){
64805         var c = this.config[colIndex];
64806         if(c.hidden !== hidden){
64807             c.hidden = hidden;
64808             this.totalWidth = null;
64809             this.fireEvent("hiddenchange", this, colIndex, hidden);
64810         }
64811     },
64812
64813     /**
64814      * Sets the editor for a column and destroys the prior editor.
64815      * @param {Number} col The column index
64816      * @param {Object} editor The editor object
64817      */
64818     setEditor : function(col, editor){
64819         Ext.destroy(this.config[col].editor);
64820         this.config[col].editor = editor;
64821     },
64822
64823     /**
64824      * Destroys this column model by purging any event listeners, and removing any editors.
64825      */
64826     destroy : function(){
64827         for(var i = 0, c = this.config, len = c.length; i < len; i++){
64828             Ext.destroy(c[i].editor);
64829         }
64830         this.purgeListeners();
64831     }
64832 });
64833
64834 // private
64835 Ext.grid.ColumnModel.defaultRenderer = function(value){
64836     if(typeof value == "string" && value.length < 1){
64837         return "&#160;";
64838     }
64839     return value;
64840 };/**\r
64841  * @class Ext.grid.AbstractSelectionModel\r
64842  * @extends Ext.util.Observable\r
64843  * Abstract base class for grid SelectionModels.  It provides the interface that should be\r
64844  * implemented by descendant classes.  This class should not be directly instantiated.\r
64845  * @constructor\r
64846  */\r
64847 Ext.grid.AbstractSelectionModel = function(){\r
64848     this.locked = false;\r
64849     Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);\r
64850 };\r
64851 \r
64852 Ext.extend(Ext.grid.AbstractSelectionModel, Ext.util.Observable,  {\r
64853     /**\r
64854      * The GridPanel for which this SelectionModel is handling selection. Read-only.\r
64855      * @type Object\r
64856      * @property grid\r
64857      */\r
64858 \r
64859     /** @ignore Called by the grid automatically. Do not call directly. */\r
64860     init : function(grid){\r
64861         this.grid = grid;\r
64862         this.initEvents();\r
64863     },\r
64864 \r
64865     /**\r
64866      * Locks the selections.\r
64867      */\r
64868     lock : function(){\r
64869         this.locked = true;\r
64870     },\r
64871 \r
64872     /**\r
64873      * Unlocks the selections.\r
64874      */\r
64875     unlock : function(){\r
64876         this.locked = false;\r
64877     },\r
64878 \r
64879     /**\r
64880      * Returns true if the selections are locked.\r
64881      * @return {Boolean}\r
64882      */\r
64883     isLocked : function(){\r
64884         return this.locked;\r
64885     },\r
64886     \r
64887     destroy: function(){\r
64888         this.purgeListeners();\r
64889     }\r
64890 });/**
64891  * @class Ext.grid.RowSelectionModel
64892  * @extends Ext.grid.AbstractSelectionModel
64893  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
64894  * It supports multiple selections and keyboard selection/navigation. The objects stored
64895  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
64896  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
64897  * @constructor
64898  * @param {Object} config
64899  */
64900 Ext.grid.RowSelectionModel = function(config){
64901     Ext.apply(this, config);
64902     this.selections = new Ext.util.MixedCollection(false, function(o){
64903         return o.id;
64904     });
64905
64906     this.last = false;
64907     this.lastActive = false;
64908
64909     this.addEvents(
64910         /**
64911          * @event selectionchange
64912          * Fires when the selection changes
64913          * @param {SelectionModel} this
64914          */
64915         'selectionchange',
64916         /**
64917          * @event beforerowselect
64918          * Fires before a row is selected, return false to cancel the selection.
64919          * @param {SelectionModel} this
64920          * @param {Number} rowIndex The index to be selected
64921          * @param {Boolean} keepExisting False if other selections will be cleared
64922          * @param {Record} record The record to be selected
64923          */
64924         'beforerowselect',
64925         /**
64926          * @event rowselect
64927          * Fires when a row is selected.
64928          * @param {SelectionModel} this
64929          * @param {Number} rowIndex The selected index
64930          * @param {Ext.data.Record} r The selected record
64931          */
64932         'rowselect',
64933         /**
64934          * @event rowdeselect
64935          * Fires when a row is deselected.  To prevent deselection
64936          * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
64937          * @param {SelectionModel} this
64938          * @param {Number} rowIndex
64939          * @param {Record} record
64940          */
64941         'rowdeselect'
64942     );
64943
64944     Ext.grid.RowSelectionModel.superclass.constructor.call(this);
64945 };
64946
64947 Ext.extend(Ext.grid.RowSelectionModel, Ext.grid.AbstractSelectionModel,  {
64948     /**
64949      * @cfg {Boolean} singleSelect
64950      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
64951      * allowing multiple selections)
64952      */
64953     singleSelect : false,
64954
64955     /**
64956      * @cfg {Boolean} moveEditorOnEnter
64957      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
64958      * or the next row up when shift + enter keys are pressed.
64959      */
64960     // private
64961     initEvents : function(){
64962
64963         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
64964             this.grid.on('rowmousedown', this.handleMouseDown, this);
64965         }
64966
64967         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
64968             'up' : function(e){
64969                 if(!e.shiftKey || this.singleSelect){
64970                     this.selectPrevious(false);
64971                 }else if(this.last !== false && this.lastActive !== false){
64972                     var last = this.last;
64973                     this.selectRange(this.last,  this.lastActive-1);
64974                     this.grid.getView().focusRow(this.lastActive);
64975                     if(last !== false){
64976                         this.last = last;
64977                     }
64978                 }else{
64979                     this.selectFirstRow();
64980                 }
64981             },
64982             'down' : function(e){
64983                 if(!e.shiftKey || this.singleSelect){
64984                     this.selectNext(false);
64985                 }else if(this.last !== false && this.lastActive !== false){
64986                     var last = this.last;
64987                     this.selectRange(this.last,  this.lastActive+1);
64988                     this.grid.getView().focusRow(this.lastActive);
64989                     if(last !== false){
64990                         this.last = last;
64991                     }
64992                 }else{
64993                     this.selectFirstRow();
64994                 }
64995             },
64996             scope: this
64997         });
64998
64999         this.grid.getView().on({
65000             scope: this,
65001             refresh: this.onRefresh,
65002             rowupdated: this.onRowUpdated,
65003             rowremoved: this.onRemove
65004         });
65005     },
65006
65007     // private
65008     onRefresh : function(){
65009         var ds = this.grid.store, index;
65010         var s = this.getSelections();
65011         this.clearSelections(true);
65012         for(var i = 0, len = s.length; i < len; i++){
65013             var r = s[i];
65014             if((index = ds.indexOfId(r.id)) != -1){
65015                 this.selectRow(index, true);
65016             }
65017         }
65018         if(s.length != this.selections.getCount()){
65019             this.fireEvent('selectionchange', this);
65020         }
65021     },
65022
65023     // private
65024     onRemove : function(v, index, r){
65025         if(this.selections.remove(r) !== false){
65026             this.fireEvent('selectionchange', this);
65027         }
65028     },
65029
65030     // private
65031     onRowUpdated : function(v, index, r){
65032         if(this.isSelected(r)){
65033             v.onRowSelect(index);
65034         }
65035     },
65036
65037     /**
65038      * Select records.
65039      * @param {Array} records The records to select
65040      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
65041      */
65042     selectRecords : function(records, keepExisting){
65043         if(!keepExisting){
65044             this.clearSelections();
65045         }
65046         var ds = this.grid.store;
65047         for(var i = 0, len = records.length; i < len; i++){
65048             this.selectRow(ds.indexOf(records[i]), true);
65049         }
65050     },
65051
65052     /**
65053      * Gets the number of selected rows.
65054      * @return {Number}
65055      */
65056     getCount : function(){
65057         return this.selections.length;
65058     },
65059
65060     /**
65061      * Selects the first row in the grid.
65062      */
65063     selectFirstRow : function(){
65064         this.selectRow(0);
65065     },
65066
65067     /**
65068      * Select the last row.
65069      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
65070      */
65071     selectLastRow : function(keepExisting){
65072         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
65073     },
65074
65075     /**
65076      * Selects the row immediately following the last selected row.
65077      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
65078      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
65079      */
65080     selectNext : function(keepExisting){
65081         if(this.hasNext()){
65082             this.selectRow(this.last+1, keepExisting);
65083             this.grid.getView().focusRow(this.last);
65084             return true;
65085         }
65086         return false;
65087     },
65088
65089     /**
65090      * Selects the row that precedes the last selected row.
65091      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
65092      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
65093      */
65094     selectPrevious : function(keepExisting){
65095         if(this.hasPrevious()){
65096             this.selectRow(this.last-1, keepExisting);
65097             this.grid.getView().focusRow(this.last);
65098             return true;
65099         }
65100         return false;
65101     },
65102
65103     /**
65104      * Returns true if there is a next record to select
65105      * @return {Boolean}
65106      */
65107     hasNext : function(){
65108         return this.last !== false && (this.last+1) < this.grid.store.getCount();
65109     },
65110
65111     /**
65112      * Returns true if there is a previous record to select
65113      * @return {Boolean}
65114      */
65115     hasPrevious : function(){
65116         return !!this.last;
65117     },
65118
65119
65120     /**
65121      * Returns the selected records
65122      * @return {Array} Array of selected records
65123      */
65124     getSelections : function(){
65125         return [].concat(this.selections.items);
65126     },
65127
65128     /**
65129      * Returns the first selected record.
65130      * @return {Record}
65131      */
65132     getSelected : function(){
65133         return this.selections.itemAt(0);
65134     },
65135
65136     /**
65137      * Calls the passed function with each selection. If the function returns
65138      * <tt>false</tt>, iteration is stopped and this function returns
65139      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
65140      * @param {Function} fn
65141      * @param {Object} scope (optional)
65142      * @return {Boolean} true if all selections were iterated
65143      */
65144     each : function(fn, scope){
65145         var s = this.getSelections();
65146         for(var i = 0, len = s.length; i < len; i++){
65147             if(fn.call(scope || this, s[i], i) === false){
65148                 return false;
65149             }
65150         }
65151         return true;
65152     },
65153
65154     /**
65155      * Clears all selections if the selection model
65156      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
65157      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
65158      * conditional checks and events described in {@link #deselectRow}.
65159      */
65160     clearSelections : function(fast){
65161         if(this.isLocked()){
65162             return;
65163         }
65164         if(fast !== true){
65165             var ds = this.grid.store;
65166             var s = this.selections;
65167             s.each(function(r){
65168                 this.deselectRow(ds.indexOfId(r.id));
65169             }, this);
65170             s.clear();
65171         }else{
65172             this.selections.clear();
65173         }
65174         this.last = false;
65175     },
65176
65177
65178     /**
65179      * Selects all rows if the selection model
65180      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
65181      */
65182     selectAll : function(){
65183         if(this.isLocked()){
65184             return;
65185         }
65186         this.selections.clear();
65187         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
65188             this.selectRow(i, true);
65189         }
65190     },
65191
65192     /**
65193      * Returns <tt>true</tt> if there is a selection.
65194      * @return {Boolean}
65195      */
65196     hasSelection : function(){
65197         return this.selections.length > 0;
65198     },
65199
65200     /**
65201      * Returns <tt>true</tt> if the specified row is selected.
65202      * @param {Number/Record} index The record or index of the record to check
65203      * @return {Boolean}
65204      */
65205     isSelected : function(index){
65206         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
65207         return (r && this.selections.key(r.id) ? true : false);
65208     },
65209
65210     /**
65211      * Returns <tt>true</tt> if the specified record id is selected.
65212      * @param {String} id The id of record to check
65213      * @return {Boolean}
65214      */
65215     isIdSelected : function(id){
65216         return (this.selections.key(id) ? true : false);
65217     },
65218
65219     // private
65220     handleMouseDown : function(g, rowIndex, e){
65221         if(e.button !== 0 || this.isLocked()){
65222             return;
65223         }
65224         var view = this.grid.getView();
65225         if(e.shiftKey && !this.singleSelect && this.last !== false){
65226             var last = this.last;
65227             this.selectRange(last, rowIndex, e.ctrlKey);
65228             this.last = last; // reset the last
65229             view.focusRow(rowIndex);
65230         }else{
65231             var isSelected = this.isSelected(rowIndex);
65232             if(e.ctrlKey && isSelected){
65233                 this.deselectRow(rowIndex);
65234             }else if(!isSelected || this.getCount() > 1){
65235                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
65236                 view.focusRow(rowIndex);
65237             }
65238         }
65239     },
65240
65241     /**
65242      * Selects multiple rows.
65243      * @param {Array} rows Array of the indexes of the row to select
65244      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
65245      * existing selections (defaults to <tt>false</tt>)
65246      */
65247     selectRows : function(rows, keepExisting){
65248         if(!keepExisting){
65249             this.clearSelections();
65250         }
65251         for(var i = 0, len = rows.length; i < len; i++){
65252             this.selectRow(rows[i], true);
65253         }
65254     },
65255
65256     /**
65257      * Selects a range of rows if the selection model
65258      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
65259      * All rows in between startRow and endRow are also selected.
65260      * @param {Number} startRow The index of the first row in the range
65261      * @param {Number} endRow The index of the last row in the range
65262      * @param {Boolean} keepExisting (optional) True to retain existing selections
65263      */
65264     selectRange : function(startRow, endRow, keepExisting){
65265         var i;
65266         if(this.isLocked()){
65267             return;
65268         }
65269         if(!keepExisting){
65270             this.clearSelections();
65271         }
65272         if(startRow <= endRow){
65273             for(i = startRow; i <= endRow; i++){
65274                 this.selectRow(i, true);
65275             }
65276         }else{
65277             for(i = startRow; i >= endRow; i--){
65278                 this.selectRow(i, true);
65279             }
65280         }
65281     },
65282
65283     /**
65284      * Deselects a range of rows if the selection model
65285      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
65286      * All rows in between startRow and endRow are also deselected.
65287      * @param {Number} startRow The index of the first row in the range
65288      * @param {Number} endRow The index of the last row in the range
65289      */
65290     deselectRange : function(startRow, endRow, preventViewNotify){
65291         if(this.isLocked()){
65292             return;
65293         }
65294         for(var i = startRow; i <= endRow; i++){
65295             this.deselectRow(i, preventViewNotify);
65296         }
65297     },
65298
65299     /**
65300      * Selects a row.  Before selecting a row, checks if the selection model
65301      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
65302      * {@link #beforerowselect} event.  If these checks are satisfied the row
65303      * will be selected and followed up by  firing the {@link #rowselect} and
65304      * {@link #selectionchange} events.
65305      * @param {Number} row The index of the row to select
65306      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
65307      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
65308      * prevent notifying the view (disables updating the selected appearance)
65309      */
65310     selectRow : function(index, keepExisting, preventViewNotify){
65311         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
65312             return;
65313         }
65314         var r = this.grid.store.getAt(index);
65315         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
65316             if(!keepExisting || this.singleSelect){
65317                 this.clearSelections();
65318             }
65319             this.selections.add(r);
65320             this.last = this.lastActive = index;
65321             if(!preventViewNotify){
65322                 this.grid.getView().onRowSelect(index);
65323             }
65324             this.fireEvent('rowselect', this, index, r);
65325             this.fireEvent('selectionchange', this);
65326         }
65327     },
65328
65329     /**
65330      * Deselects a row.  Before deselecting a row, checks if the selection model
65331      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
65332      * If this check is satisfied the row will be deselected and followed up by
65333      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
65334      * @param {Number} row The index of the row to deselect
65335      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
65336      * prevent notifying the view (disables updating the selected appearance)
65337      */
65338     deselectRow : function(index, preventViewNotify){
65339         if(this.isLocked()){
65340             return;
65341         }
65342         if(this.last == index){
65343             this.last = false;
65344         }
65345         if(this.lastActive == index){
65346             this.lastActive = false;
65347         }
65348         var r = this.grid.store.getAt(index);
65349         if(r){
65350             this.selections.remove(r);
65351             if(!preventViewNotify){
65352                 this.grid.getView().onRowDeselect(index);
65353             }
65354             this.fireEvent('rowdeselect', this, index, r);
65355             this.fireEvent('selectionchange', this);
65356         }
65357     },
65358
65359     // private
65360     restoreLast : function(){
65361         if(this._last){
65362             this.last = this._last;
65363         }
65364     },
65365
65366     // private
65367     acceptsNav : function(row, col, cm){
65368         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65369     },
65370
65371     // private
65372     onEditorKey : function(field, e){
65373         var k = e.getKey(), 
65374             newCell, 
65375             g = this.grid, 
65376             last = g.lastEdit,
65377             ed = g.activeEditor,
65378             ae, last, r, c;
65379         var shift = e.shiftKey;
65380         if(k == e.TAB){
65381             e.stopEvent();
65382             ed.completeEdit();
65383             if(shift){
65384                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65385             }else{
65386                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65387             }
65388         }else if(k == e.ENTER){
65389             if(this.moveEditorOnEnter !== false){
65390                 if(shift){
65391                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
65392                 }else{
65393                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
65394                 }
65395             }
65396         }
65397         if(newCell){
65398             r = newCell[0];
65399             c = newCell[1];
65400
65401             if(last.row != r){
65402                 this.selectRow(r); // *** highlight newly-selected cell and update selection
65403             }
65404
65405             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
65406                 ae = g.activeEditor;
65407                 if(ae && ae.field.triggerBlur){
65408                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
65409                     ae.field.triggerBlur();
65410                 }
65411             }
65412             g.startEditing(r, c);
65413         }
65414     },
65415     
65416     destroy : function(){
65417         if(this.rowNav){
65418             this.rowNav.disable();
65419             this.rowNav = null;
65420         }
65421         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
65422     }
65423 });/**\r
65424  * @class Ext.grid.Column\r
65425  * <p>This class encapsulates column configuration data to be used in the initialization of a\r
65426  * {@link Ext.grid.ColumnModel ColumnModel}.</p>\r
65427  * <p>While subclasses are provided to render data in different ways, this class renders a passed\r
65428  * data field unchanged and is usually used for textual columns.</p>\r
65429  */\r
65430 Ext.grid.Column = function(config){\r
65431     Ext.apply(this, config);\r
65432 \r
65433     if(Ext.isString(this.renderer)){\r
65434         this.renderer = Ext.util.Format[this.renderer];\r
65435     } else if(Ext.isObject(this.renderer)){\r
65436         this.scope = this.renderer.scope;\r
65437         this.renderer = this.renderer.fn;\r
65438     }\r
65439     this.renderer = this.renderer.createDelegate(this.scope || config);\r
65440 \r
65441     if(this.editor){\r
65442         this.editor = Ext.create(this.editor, 'textfield');\r
65443     }\r
65444 };\r
65445 \r
65446 Ext.grid.Column.prototype = {\r
65447     /**\r
65448      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured\r
65449      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.\r
65450      * The initial configuration may be dynamically altered using\r
65451      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.\r
65452      */\r
65453     /**\r
65454      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial\r
65455      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all\r
65456      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be\r
65457      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>\r
65458      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>\r
65459      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>\r
65460      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this\r
65461      * unique identifier.\r
65462      */\r
65463     /**\r
65464      * @cfg {String} header Optional. The header text to be used as innerHTML\r
65465      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to\r
65466      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.\r
65467      */\r
65468     /**\r
65469      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
65470      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,\r
65471      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the\r
65472      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.\r
65473      */\r
65474     /**\r
65475      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
65476      * may be used to specify the text with which to prefix the group field value in the group header line.\r
65477      * See also {@link #groupRenderer} and\r
65478      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.\r
65479      */\r
65480     /**\r
65481      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
65482      * may be used to specify the function used to format the grouping field value for display in the group\r
65483      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured\r
65484      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified\r
65485      * the new value of the group field will be used.</p>\r
65486      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be\r
65487      * passed the following parameters:\r
65488      * <div class="mdetail-params"><ul>\r
65489      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>\r
65490      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>\r
65491      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data\r
65492      * for the row which caused group change.</p></li>\r
65493      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
65494      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>\r
65495      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>\r
65496      * </ul></div></p>\r
65497      * <p>The function should return a string value.</p>\r
65498      */\r
65499     /**\r
65500      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
65501      * may be used to specify the text to display when there is an empty group value. Defaults to the\r
65502      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.\r
65503      */\r
65504     /**\r
65505      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the\r
65506      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from\r
65507      * which to draw the column's value.</p>\r
65508      */\r
65509     /**\r
65510      * @cfg {Number} width\r
65511      * Optional. The initial width in pixels of the column.\r
65512      * The width of each column can also be affected if any of the following are configured:\r
65513      * <div class="mdetail-params"><ul>\r
65514      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>\r
65515      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>\r
65516      * <div class="sub-desc">\r
65517      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be\r
65518      * re-proportioned (based on the relative initial widths) to fill the width of the grid so\r
65519      * that no horizontal scrollbar is shown.</p>\r
65520      * </div></li>\r
65521      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>\r
65522      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>\r
65523      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side\r
65524      * is reserved for the vertical scrollbar.  The\r
65525      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>\r
65526      * can be modified to reduce or eliminate the reserved offset.</p>\r
65527      */\r
65528     /**\r
65529      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.\r
65530      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.\r
65531      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.\r
65532      */\r
65533     /**\r
65534      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.\r
65535      */\r
65536     /**\r
65537      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.\r
65538      */\r
65539     /**\r
65540      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.\r
65541      */\r
65542     /**\r
65543      * @cfg {Boolean} hidden\r
65544      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.\r
65545      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.\r
65546      * If a column is never to be shown, simply do not include this column in the Column Model at all. \r
65547      */\r
65548     /**\r
65549      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips\r
65550      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the\r
65551      * header's HTML title attribute. Defaults to ''.\r
65552      */\r
65553     /**\r
65554      * @cfg {Mixed} renderer\r
65555      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>\r
65556      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,\r
65557      * appearance, etc.) before it is rendered). This may be specified in either of three ways:\r
65558      * <div class="mdetail-params"><ul>\r
65559      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>\r
65560      * <li>A string which references a property name of the {@link Ext.util.Format} class which\r
65561      * provides a renderer function.</li>\r
65562      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>\r
65563      * reference) e.g.:<pre style="margin-left:1.2em"><code>\r
65564 {\r
65565     fn: this.gridRenderer,\r
65566     scope: this\r
65567 }\r
65568 </code></pre></li></ul></div>\r
65569      * If not specified, the default renderer uses the raw data value.</p>\r
65570      * <p>For information about the renderer function (passed parameters, etc.), see\r
65571      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>\r
65572 var companyColumn = {\r
65573    header: 'Company Name',\r
65574    dataIndex: 'company',\r
65575    renderer: function(value, metaData, record, rowIndex, colIndex, store) {\r
65576       // provide the logic depending on business rules\r
65577       // name of your own choosing to manipulate the cell depending upon\r
65578       // the data in the underlying Record object.\r
65579       if (value == 'whatever') {\r
65580           //metaData.css : String : A CSS class name to add to the TD element of the cell.\r
65581           //metaData.attr : String : An html attribute definition string to apply to\r
65582           //                         the data container element within the table\r
65583           //                         cell (e.g. 'style="color:red;"').\r
65584           metaData.css = 'name-of-css-class-you-will-define';\r
65585       }\r
65586       return value;\r
65587    }\r
65588 }\r
65589      * </code></pre>\r
65590      * See also {@link #scope}.\r
65591      */\r
65592     /**\r
65593      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass\r
65594      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily\r
65595      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:\r
65596      * <div class="mdetail-params"><ul>\r
65597      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>\r
65598      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>\r
65599      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>\r
65600      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>\r
65601      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>\r
65602      * </ul></div>\r
65603      * <p>Configuration properties for the specified <code>xtype</code> may be specified with\r
65604      * the Column configuration properties, for example:</p>\r
65605      * <pre><code>\r
65606 var grid = new Ext.grid.GridPanel({\r
65607     ...\r
65608     columns: [{\r
65609         header: 'Last Updated',\r
65610         dataIndex: 'lastChange',\r
65611         width: 85,\r
65612         sortable: true,\r
65613         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),\r
65614         xtype: 'datecolumn', // use xtype instead of renderer\r
65615         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}\r
65616     }, {\r
65617         ...\r
65618     }]\r
65619 });\r
65620      * </code></pre>\r
65621      */\r
65622     /**\r
65623      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the\r
65624      * renderer.  Defaults to the Column configuration object.\r
65625      */\r
65626     /**\r
65627      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.\r
65628      */\r
65629     /**\r
65630      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column\r
65631      * (excluding headers). Defaults to undefined.\r
65632      */\r
65633     /**\r
65634      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column\r
65635      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use\r
65636      * {@link Ext.grid.GridPanel#enableColumnHide} instead.\r
65637      */\r
65638     /**\r
65639      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column\r
65640      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.\r
65641      */\r
65642 \r
65643     /**\r
65644      * @private\r
65645      * @cfg {Boolean} isColumn\r
65646      * Used by ColumnModel setConfig method to avoid reprocessing a Column\r
65647      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column\r
65648      * Defaults to true.\r
65649      */\r
65650     isColumn : true,\r
65651 \r
65652     /**\r
65653      * Optional. A function which returns displayable data when passed the following parameters:\r
65654      * <div class="mdetail-params"><ul>\r
65655      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>\r
65656      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>\r
65657      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>\r
65658      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container\r
65659      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>\r
65660      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was\r
65661      * extracted.</p></li>\r
65662      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>\r
65663      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>\r
65664      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record\r
65665      * was extracted.</p></li>\r
65666      * </ul></div>\r
65667      * @property renderer\r
65668      * @type Function\r
65669      */\r
65670     renderer : function(value){\r
65671         if(Ext.isString(value) && value.length < 1){\r
65672             return '&#160;';\r
65673         }\r
65674         return value;\r
65675     },\r
65676 \r
65677     // private\r
65678     getEditor: function(rowIndex){\r
65679         return this.editable !== false ? this.editor : null;\r
65680     },\r
65681 \r
65682     /**\r
65683      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}\r
65684      * used to edit the cell.\r
65685      * @param {Number} rowIndex The row index\r
65686      * @return {Ext.Editor}\r
65687      */\r
65688     getCellEditor: function(rowIndex){\r
65689         var editor = this.getEditor(rowIndex);\r
65690         if(editor){\r
65691             if(!editor.startEdit){\r
65692                 if(!editor.gridEditor){\r
65693                     editor.gridEditor = new Ext.grid.GridEditor(editor);\r
65694                 }\r
65695                 return editor.gridEditor;\r
65696             }else if(editor.startEdit){\r
65697                 return editor;\r
65698             }\r
65699         }\r
65700         return null;\r
65701     }\r
65702 };\r
65703 \r
65704 /**\r
65705  * @class Ext.grid.BooleanColumn\r
65706  * @extends Ext.grid.Column\r
65707  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}\r
65708  * config option of {@link Ext.grid.Column} for more details.</p>\r
65709  */\r
65710 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {\r
65711     /**\r
65712      * @cfg {String} trueText\r
65713      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).\r
65714      */\r
65715     trueText: 'true',\r
65716     /**\r
65717      * @cfg {String} falseText\r
65718      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to\r
65719      * <tt>'false'</tt>).\r
65720      */\r
65721     falseText: 'false',\r
65722     /**\r
65723      * @cfg {String} undefinedText\r
65724      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).\r
65725      */\r
65726     undefinedText: '&#160;',\r
65727 \r
65728     constructor: function(cfg){\r
65729         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);\r
65730         var t = this.trueText, f = this.falseText, u = this.undefinedText;\r
65731         this.renderer = function(v){\r
65732             if(v === undefined){\r
65733                 return u;\r
65734             }\r
65735             if(!v || v === 'false'){\r
65736                 return f;\r
65737             }\r
65738             return t;\r
65739         };\r
65740     }\r
65741 });\r
65742 \r
65743 /**\r
65744  * @class Ext.grid.NumberColumn\r
65745  * @extends Ext.grid.Column\r
65746  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the\r
65747  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>\r
65748  */\r
65749 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {\r
65750     /**\r
65751      * @cfg {String} format\r
65752      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column\r
65753      * (defaults to <tt>'0,000.00'</tt>).\r
65754      */\r
65755     format : '0,000.00',\r
65756     constructor: function(cfg){\r
65757         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);\r
65758         this.renderer = Ext.util.Format.numberRenderer(this.format);\r
65759     }\r
65760 });\r
65761 \r
65762 /**\r
65763  * @class Ext.grid.DateColumn\r
65764  * @extends Ext.grid.Column\r
65765  * <p>A Column definition class which renders a passed date according to the default locale, or a configured\r
65766  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}\r
65767  * for more details.</p>\r
65768  */\r
65769 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {\r
65770     /**\r
65771      * @cfg {String} format\r
65772      * A formatting string as used by {@link Date#format} to format a Date for this Column\r
65773      * (defaults to <tt>'m/d/Y'</tt>).\r
65774      */\r
65775     format : 'm/d/Y',\r
65776     constructor: function(cfg){\r
65777         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);\r
65778         this.renderer = Ext.util.Format.dateRenderer(this.format);\r
65779     }\r
65780 });\r
65781 \r
65782 /**\r
65783  * @class Ext.grid.TemplateColumn\r
65784  * @extends Ext.grid.Column\r
65785  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s\r
65786  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.\r
65787  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more\r
65788  * details.</p>\r
65789  */\r
65790 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {\r
65791     /**\r
65792      * @cfg {String/XTemplate} tpl\r
65793      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a\r
65794      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.\r
65795      */\r
65796     constructor: function(cfg){\r
65797         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);\r
65798         var tpl = Ext.isObject(this.tpl) ? this.tpl : new Ext.XTemplate(this.tpl);\r
65799         this.renderer = function(value, p, r){\r
65800             return tpl.apply(r.data);\r
65801         };\r
65802         this.tpl = tpl;\r
65803     }\r
65804 });\r
65805 \r
65806 /*\r
65807  * @property types\r
65808  * @type Object\r
65809  * @member Ext.grid.Column\r
65810  * @static\r
65811  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced\r
65812  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>\r
65813  * <p>This contains the following properties</p><div class="mdesc-details"><ul>\r
65814  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>\r
65815  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>\r
65816  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>\r
65817  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>\r
65818  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>\r
65819  * </ul></div>\r
65820  */\r
65821 Ext.grid.Column.types = {\r
65822     gridcolumn : Ext.grid.Column,\r
65823     booleancolumn: Ext.grid.BooleanColumn,\r
65824     numbercolumn: Ext.grid.NumberColumn,\r
65825     datecolumn: Ext.grid.DateColumn,\r
65826     templatecolumn: Ext.grid.TemplateColumn\r
65827 };/**
65828  * @class Ext.grid.RowNumberer
65829  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
65830  * an automatic row numbering column.
65831  * <br>Usage:<br>
65832  <pre><code>
65833  // This is a typical column config with the first column providing row numbers
65834  var colModel = new Ext.grid.ColumnModel([
65835     new Ext.grid.RowNumberer(),
65836     {header: "Name", width: 80, sortable: true},
65837     {header: "Code", width: 50, sortable: true},
65838     {header: "Description", width: 200, sortable: true}
65839  ]);
65840  </code></pre>
65841  * @constructor
65842  * @param {Object} config The configuration options
65843  */
65844 Ext.grid.RowNumberer = function(config){
65845     Ext.apply(this, config);
65846     if(this.rowspan){
65847         this.renderer = this.renderer.createDelegate(this);
65848     }
65849 };
65850
65851 Ext.grid.RowNumberer.prototype = {
65852     /**
65853      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
65854      * number column (defaults to '').
65855      */
65856     header: "",
65857     /**
65858      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
65859      */
65860     width: 23,
65861     /**
65862      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
65863      * @hide
65864      */
65865     sortable: false,
65866
65867     // private
65868     fixed:true,
65869     menuDisabled:true,
65870     dataIndex: '',
65871     id: 'numberer',
65872     rowspan: undefined,
65873
65874     // private
65875     renderer : function(v, p, record, rowIndex){
65876         if(this.rowspan){
65877             p.cellAttr = 'rowspan="'+this.rowspan+'"';
65878         }
65879         return rowIndex+1;
65880     }
65881 };/**\r
65882  * @class Ext.grid.CheckboxSelectionModel\r
65883  * @extends Ext.grid.RowSelectionModel\r
65884  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.\r
65885  * @constructor\r
65886  * @param {Object} config The configuration options\r
65887  */\r
65888 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {\r
65889 \r
65890     /**\r
65891      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the\r
65892      * checkbox column (defaults to <tt>false</tt>).\r
65893      */\r
65894     /**\r
65895      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the\r
65896      * checkbox column.  Defaults to:<pre><code>\r
65897      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>\r
65898      * </code></pre>\r
65899      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header\r
65900      * and provides support for automatic check all/none behavior on header click. This string\r
65901      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,\r
65902      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the\r
65903      * <tt>'x-grid3-hd-checker'</tt> class is supplied.\r
65904      */\r
65905     header : '<div class="x-grid3-hd-checker">&#160;</div>',\r
65906     /**\r
65907      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).\r
65908      */\r
65909     width : 20,\r
65910     /**\r
65911      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to\r
65912      * <tt>false</tt>).\r
65913      */\r
65914     sortable : false,\r
65915 \r
65916     // private\r
65917     menuDisabled : true,\r
65918     fixed : true,\r
65919     dataIndex : '',\r
65920     id : 'checker',\r
65921 \r
65922     constructor : function(){\r
65923         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);\r
65924 \r
65925         if(this.checkOnly){\r
65926             this.handleMouseDown = Ext.emptyFn;\r
65927         }\r
65928     },\r
65929 \r
65930     // private\r
65931     initEvents : function(){\r
65932         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);\r
65933         this.grid.on('render', function(){\r
65934             var view = this.grid.getView();\r
65935             view.mainBody.on('mousedown', this.onMouseDown, this);\r
65936             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);\r
65937 \r
65938         }, this);\r
65939     },\r
65940 \r
65941     // private\r
65942     onMouseDown : function(e, t){\r
65943         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click\r
65944             e.stopEvent();\r
65945             var row = e.getTarget('.x-grid3-row');\r
65946             if(row){\r
65947                 var index = row.rowIndex;\r
65948                 if(this.isSelected(index)){\r
65949                     this.deselectRow(index);\r
65950                 }else{\r
65951                     this.selectRow(index, true);\r
65952                 }\r
65953             }\r
65954         }\r
65955     },\r
65956 \r
65957     // private\r
65958     onHdMouseDown : function(e, t){\r
65959         if(t.className == 'x-grid3-hd-checker'){\r
65960             e.stopEvent();\r
65961             var hd = Ext.fly(t.parentNode);\r
65962             var isChecked = hd.hasClass('x-grid3-hd-checker-on');\r
65963             if(isChecked){\r
65964                 hd.removeClass('x-grid3-hd-checker-on');\r
65965                 this.clearSelections();\r
65966             }else{\r
65967                 hd.addClass('x-grid3-hd-checker-on');\r
65968                 this.selectAll();\r
65969             }\r
65970         }\r
65971     },\r
65972 \r
65973     // private\r
65974     renderer : function(v, p, record){\r
65975         return '<div class="x-grid3-row-checker">&#160;</div>';\r
65976     }\r
65977 });/**
65978  * @class Ext.grid.CellSelectionModel
65979  * @extends Ext.grid.AbstractSelectionModel
65980  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
65981  * The object stored as the selection contains the following properties:
65982  * <div class="mdetail-params"><ul>
65983  * <li><b>cell</b> : see {@link #getSelectedCell} 
65984  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
65985  * which provides the data for the row containing the selection</li>
65986  * </ul></div>
65987  * @constructor
65988  * @param {Object} config The object containing the configuration of this model.
65989  */
65990 Ext.grid.CellSelectionModel = function(config){
65991     Ext.apply(this, config);
65992
65993     this.selection = null;
65994
65995     this.addEvents(
65996         /**
65997              * @event beforecellselect
65998              * Fires before a cell is selected, return false to cancel the selection.
65999              * @param {SelectionModel} this
66000              * @param {Number} rowIndex The selected row index
66001              * @param {Number} colIndex The selected cell index
66002              */
66003             "beforecellselect",
66004         /**
66005              * @event cellselect
66006              * Fires when a cell is selected.
66007              * @param {SelectionModel} this
66008              * @param {Number} rowIndex The selected row index
66009              * @param {Number} colIndex The selected cell index
66010              */
66011             "cellselect",
66012         /**
66013              * @event selectionchange
66014              * Fires when the active selection changes.
66015              * @param {SelectionModel} this
66016              * @param {Object} selection null for no selection or an object with two properties
66017          * <div class="mdetail-params"><ul>
66018          * <li><b>cell</b> : see {@link #getSelectedCell} 
66019          * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
66020          * which provides the data for the row containing the selection</p></li>
66021          * </ul></div>
66022              */
66023             "selectionchange"
66024     );
66025
66026     Ext.grid.CellSelectionModel.superclass.constructor.call(this);
66027 };
66028
66029 Ext.extend(Ext.grid.CellSelectionModel, Ext.grid.AbstractSelectionModel,  {
66030
66031     /** @ignore */
66032     initEvents : function(){
66033         this.grid.on('cellmousedown', this.handleMouseDown, this);
66034         this.grid.on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.handleKeyDown, this);
66035         this.grid.getView().on({
66036             scope: this,
66037             refresh: this.onViewChange,
66038             rowupdated: this.onRowUpdated,
66039             beforerowremoved: this.clearSelections,
66040             beforerowsinserted: this.clearSelections
66041         });
66042         if(this.grid.isEditor){
66043             this.grid.on('beforeedit', this.beforeEdit,  this);
66044         }
66045     },
66046
66047         //private
66048     beforeEdit : function(e){
66049         this.select(e.row, e.column, false, true, e.record);
66050     },
66051
66052         //private
66053     onRowUpdated : function(v, index, r){
66054         if(this.selection && this.selection.record == r){
66055             v.onCellSelect(index, this.selection.cell[1]);
66056         }
66057     },
66058
66059         //private
66060     onViewChange : function(){
66061         this.clearSelections(true);
66062     },
66063
66064         /**
66065      * Returns an array containing the row and column indexes of the currently selected cell
66066      * (e.g., [0, 0]), or null if none selected. The array has elements:
66067      * <div class="mdetail-params"><ul>
66068      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
66069      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
66070      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
66071      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
66072      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
66073 // get name
66074 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
66075 // get data value based on name
66076 var data = record.get(fieldName);
66077      * </code></pre></p></li>
66078      * </ul></div>
66079      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
66080          */
66081     getSelectedCell : function(){
66082         return this.selection ? this.selection.cell : null;
66083     },
66084
66085     /**
66086      * If anything is selected, clears all selections and fires the selectionchange event.
66087      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
66088      * being notified about the change.
66089      */
66090     clearSelections : function(preventNotify){
66091         var s = this.selection;
66092         if(s){
66093             if(preventNotify !== true){
66094                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
66095             }
66096             this.selection = null;
66097             this.fireEvent("selectionchange", this, null);
66098         }
66099     },
66100
66101     /**
66102      * Returns <tt>true</tt> if there is a selection.
66103      * @return {Boolean}
66104      */
66105     hasSelection : function(){
66106         return this.selection ? true : false;
66107     },
66108
66109     /** @ignore */
66110     handleMouseDown : function(g, row, cell, e){
66111         if(e.button !== 0 || this.isLocked()){
66112             return;
66113         }
66114         this.select(row, cell);
66115     },
66116
66117     /**
66118      * Selects a cell.  Before selecting a cell, fires the
66119      * {@link #beforecellselect} event.  If this check is satisfied the cell
66120      * will be selected and followed up by  firing the {@link #cellselect} and
66121      * {@link #selectionchange} events.
66122      * @param {Number} rowIndex The index of the row to select
66123      * @param {Number} colIndex The index of the column to select
66124      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
66125      * prevent notifying the view (disables updating the selected appearance)
66126      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
66127      * the specified rowIndex / colIndex from being focused.
66128      * @param {Ext.data.Record} r (optional) The record to select
66129      */
66130     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
66131         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
66132             this.clearSelections();
66133             r = r || this.grid.store.getAt(rowIndex);
66134             this.selection = {
66135                 record : r,
66136                 cell : [rowIndex, colIndex]
66137             };
66138             if(!preventViewNotify){
66139                 var v = this.grid.getView();
66140                 v.onCellSelect(rowIndex, colIndex);
66141                 if(preventFocus !== true){
66142                     v.focusCell(rowIndex, colIndex);
66143                 }
66144             }
66145             this.fireEvent("cellselect", this, rowIndex, colIndex);
66146             this.fireEvent("selectionchange", this, this.selection);
66147         }
66148     },
66149
66150         //private
66151     isSelectable : function(rowIndex, colIndex, cm){
66152         return !cm.isHidden(colIndex);
66153     },
66154     
66155     // private
66156     onEditorKey: function(field, e){
66157         if(e.getKey() == e.TAB){
66158             this.handleKeyDown(e);
66159         }
66160     },
66161
66162     /** @ignore */
66163     handleKeyDown : function(e){
66164         if(!e.isNavKeyPress()){
66165             return;
66166         }
66167         
66168         var k = e.getKey(),
66169             g = this.grid,
66170             s = this.selection,
66171             sm = this,
66172             walk = function(row, col, step){
66173                 return g.walkCells(
66174                     row,
66175                     col,
66176                     step,
66177                     g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
66178                     sm
66179                 );
66180             },
66181             cell, newCell, r, c, ae;
66182
66183         switch(k){
66184             case e.ESC:
66185             case e.PAGE_UP:
66186             case e.PAGE_DOWN:
66187                 // do nothing
66188                 break;
66189             default:
66190                 // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
66191                 e.stopEvent();
66192                 break;
66193         }
66194
66195         if(!s){
66196             cell = walk(0, 0, 1); // *** use private walk() function defined above
66197             if(cell){
66198                 this.select(cell[0], cell[1]);
66199             }
66200             return;
66201         }
66202
66203         cell = s.cell;  // currently selected cell
66204         r = cell[0];    // current row
66205         c = cell[1];    // current column
66206         
66207         switch(k){
66208             case e.TAB:
66209                 if(e.shiftKey){
66210                     newCell = walk(r, c - 1, -1);
66211                 }else{
66212                     newCell = walk(r, c + 1, 1);
66213                 }
66214                 break;
66215             case e.DOWN:
66216                 newCell = walk(r + 1, c, 1);
66217                 break;
66218             case e.UP:
66219                 newCell = walk(r - 1, c, -1);
66220                 break;
66221             case e.RIGHT:
66222                 newCell = walk(r, c + 1, 1);
66223                 break;
66224             case e.LEFT:
66225                 newCell = walk(r, c - 1, -1);
66226                 break;
66227             case e.ENTER:
66228                 if (g.isEditor && !g.editing) {
66229                     g.startEditing(r, c);
66230                     return;
66231                 }
66232                 break;
66233         }
66234
66235         if(newCell){
66236             // *** reassign r & c variables to newly-selected cell's row and column
66237             r = newCell[0];
66238             c = newCell[1];
66239
66240             this.select(r, c); // *** highlight newly-selected cell and update selection
66241
66242             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
66243                 ae = g.activeEditor;
66244                 if(ae && ae.field.triggerBlur){
66245                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
66246                     ae.field.triggerBlur();
66247                 }
66248                 g.startEditing(r, c);
66249             }
66250         }
66251     },
66252
66253     acceptsNav : function(row, col, cm){
66254         return !cm.isHidden(col) && cm.isCellEditable(col, row);
66255     }
66256 });/**
66257  * @class Ext.grid.EditorGridPanel
66258  * @extends Ext.grid.GridPanel
66259  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
66260  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
66261  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
66262  * <p>Editability of columns may be controlled programatically by inserting an implementation
66263  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
66264  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
66265  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
66266  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
66267  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
66268  * transformed data, this must be accounted for).</p>
66269  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
66270  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
66271  * mapping would be an appropriate editor.</p>
66272  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
66273  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
66274  * injected using the {@link #beforeedit} and {@link #afteredit} events.
66275  * @constructor
66276  * @param {Object} config The config object
66277  * @xtype editorgrid
66278  */
66279 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
66280     /**
66281      * @cfg {Number} clicksToEdit
66282      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
66283      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
66284      * editing that cell.</p>
66285      */
66286     clicksToEdit: 2,
66287     
66288     /**
66289     * @cfg {Boolean} forceValidation
66290     * True to force validation even if the value is unmodified (defaults to false)
66291     */
66292     forceValidation: false,
66293
66294     // private
66295     isEditor : true,
66296     // private
66297     detectEdit: false,
66298
66299         /**
66300          * @cfg {Boolean} autoEncode
66301          * True to automatically HTML encode and decode values pre and post edit (defaults to false)
66302          */
66303         autoEncode : false,
66304
66305         /**
66306          * @cfg {Boolean} trackMouseOver @hide
66307          */
66308     // private
66309     trackMouseOver: false, // causes very odd FF errors
66310
66311     // private
66312     initComponent : function(){
66313         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
66314
66315         if(!this.selModel){
66316             /**
66317              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
66318              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
66319              */
66320             this.selModel = new Ext.grid.CellSelectionModel();
66321         }
66322
66323         this.activeEditor = null;
66324
66325             this.addEvents(
66326             /**
66327              * @event beforeedit
66328              * Fires before cell editing is triggered. The edit event object has the following properties <br />
66329              * <ul style="padding:5px;padding-left:16px;">
66330              * <li>grid - This grid</li>
66331              * <li>record - The record being edited</li>
66332              * <li>field - The field name being edited</li>
66333              * <li>value - The value for the field being edited.</li>
66334              * <li>row - The grid row index</li>
66335              * <li>column - The grid column index</li>
66336              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66337              * </ul>
66338              * @param {Object} e An edit event (see above for description)
66339              */
66340             "beforeedit",
66341             /**
66342              * @event afteredit
66343              * Fires after a cell is edited. The edit event object has the following properties <br />
66344              * <ul style="padding:5px;padding-left:16px;">
66345              * <li>grid - This grid</li>
66346              * <li>record - The record being edited</li>
66347              * <li>field - The field name being edited</li>
66348              * <li>value - The value being set</li>
66349              * <li>originalValue - The original value for the field, before the edit.</li>
66350              * <li>row - The grid row index</li>
66351              * <li>column - The grid column index</li>
66352              * </ul>
66353              *
66354              * <pre><code> 
66355 grid.on('afteredit', afterEdit, this );
66356
66357 function afterEdit(e) {
66358     // execute an XHR to send/commit data to the server, in callback do (if successful):
66359     e.record.commit();
66360 }; 
66361              * </code></pre>
66362              * @param {Object} e An edit event (see above for description)
66363              */
66364             "afteredit",
66365             /**
66366              * @event validateedit
66367              * Fires after a cell is edited, but before the value is set in the record. Return false
66368              * to cancel the change. The edit event object has the following properties <br />
66369              * <ul style="padding:5px;padding-left:16px;">
66370              * <li>grid - This grid</li>
66371              * <li>record - The record being edited</li>
66372              * <li>field - The field name being edited</li>
66373              * <li>value - The value being set</li>
66374              * <li>originalValue - The original value for the field, before the edit.</li>
66375              * <li>row - The grid row index</li>
66376              * <li>column - The grid column index</li>
66377              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66378              * </ul>
66379              * Usage example showing how to remove the red triangle (dirty record indicator) from some
66380              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
66381              * the edit occurs on a targeted row (for example) and then setting the field's new value
66382              * in the Record directly:
66383              * <pre><code> 
66384 grid.on('validateedit', function(e) {
66385   var myTargetRow = 6;
66386  
66387   if (e.row == myTargetRow) {
66388     e.cancel = true;
66389     e.record.data[e.field] = e.value;
66390   }
66391 });
66392              * </code></pre>
66393              * @param {Object} e An edit event (see above for description)
66394              */
66395             "validateedit"
66396         );
66397     },
66398
66399     // private
66400     initEvents : function(){
66401         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
66402
66403         this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
66404         this.on('columnresize', this.stopEditing, this, [true]);
66405
66406         if(this.clicksToEdit == 1){
66407             this.on("cellclick", this.onCellDblClick, this);
66408         }else {
66409             var view = this.getView();
66410             if(this.clicksToEdit == 'auto' && view.mainBody){
66411                 view.mainBody.on('mousedown', this.onAutoEditClick, this);
66412             }
66413             this.on('celldblclick', this.onCellDblClick, this);
66414         }
66415     },
66416
66417     // private
66418     onCellDblClick : function(g, row, col){
66419         this.startEditing(row, col);
66420     },
66421
66422     // private
66423     onAutoEditClick : function(e, t){
66424         if(e.button !== 0){
66425             return;
66426         }
66427         var row = this.view.findRowIndex(t);
66428         var col = this.view.findCellIndex(t);
66429         if(row !== false && col !== false){
66430             this.stopEditing();
66431             if(this.selModel.getSelectedCell){ // cell sm
66432                 var sc = this.selModel.getSelectedCell();
66433                 if(sc && sc[0] === row && sc[1] === col){
66434                     this.startEditing(row, col);
66435                 }
66436             }else{
66437                 if(this.selModel.isSelected(row)){
66438                     this.startEditing(row, col);
66439                 }
66440             }
66441         }
66442     },
66443
66444     // private
66445     onEditComplete : function(ed, value, startValue){
66446         this.editing = false;
66447         this.activeEditor = null;
66448         
66449                 var r = ed.record;
66450         var field = this.colModel.getDataIndex(ed.col);
66451         value = this.postEditValue(value, startValue, r, field);
66452         if(this.forceValidation === true || String(value) !== String(startValue)){
66453             var e = {
66454                 grid: this,
66455                 record: r,
66456                 field: field,
66457                 originalValue: startValue,
66458                 value: value,
66459                 row: ed.row,
66460                 column: ed.col,
66461                 cancel:false
66462             };
66463             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
66464                 r.set(field, e.value);
66465                 delete e.cancel;
66466                 this.fireEvent("afteredit", e);
66467             }
66468         }
66469         this.view.focusCell(ed.row, ed.col);
66470     },
66471
66472     /**
66473      * Starts editing the specified for the specified row/column
66474      * @param {Number} rowIndex
66475      * @param {Number} colIndex
66476      */
66477     startEditing : function(row, col){
66478         this.stopEditing();
66479         if(this.colModel.isCellEditable(col, row)){
66480             this.view.ensureVisible(row, col, true);
66481             var r = this.store.getAt(row);
66482             var field = this.colModel.getDataIndex(col);
66483             var e = {
66484                 grid: this,
66485                 record: r,
66486                 field: field,
66487                 value: r.data[field],
66488                 row: row,
66489                 column: col,
66490                 cancel:false
66491             };
66492             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66493                 this.editing = true;
66494                 var ed = this.colModel.getCellEditor(col, row);
66495                 if(!ed){
66496                     return;
66497                 }
66498                 if(!ed.rendered){
66499                     ed.parentEl = this.view.getEditorParent(ed);
66500                     ed.on({
66501                         scope: this,
66502                         render: {
66503                             fn: function(c){
66504                                 c.field.focus(false, true);
66505                             },
66506                             single: true,
66507                             scope: this
66508                         },
66509                         specialkey: function(field, e){
66510                             this.getSelectionModel().onEditorKey(field, e);
66511                         },
66512                         complete: this.onEditComplete,
66513                         canceledit: this.stopEditing.createDelegate(this, [true])
66514                     });
66515                 }
66516                 Ext.apply(ed, {
66517                     row     : row,
66518                     col     : col,
66519                     record  : r
66520                 });
66521                 this.lastEdit = {
66522                     row: row,
66523                     col: col
66524                 };
66525                 this.activeEditor = ed;
66526                 var v = this.preEditValue(r, field);
66527                 ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
66528             }
66529         }
66530     },
66531
66532     // private
66533     preEditValue : function(r, field){
66534         var value = r.data[field];
66535         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
66536     },
66537
66538     // private
66539         postEditValue : function(value, originalValue, r, field){
66540                 return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
66541         },
66542
66543     /**
66544      * Stops any active editing
66545      * @param {Boolean} cancel (optional) True to cancel any changes
66546      */
66547     stopEditing : function(cancel){
66548         if(this.editing){
66549             var ae = this.activeEditor;
66550             if(ae){
66551                 ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
66552                 this.view.focusCell(ae.row, ae.col);
66553             }
66554             this.activeEditor = null;
66555         }
66556         this.editing = false;
66557     }
66558 });
66559 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
66560 // This is a support class used internally by the Grid components
66561 Ext.grid.GridEditor = function(field, config){
66562     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
66563     field.monitorTab = false;
66564 };
66565
66566 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
66567     alignment: "tl-tl",
66568     autoSize: "width",
66569     hideEl : false,
66570     cls: "x-small-editor x-grid-editor",
66571     shim:false,
66572     shadow:false
66573 });/**
66574  * @class Ext.grid.PropertyRecord
66575  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
66576  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
66577  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
66578  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
66579  * can also be created explicitly as shwon below.  Example usage:
66580  * <pre><code>
66581 var rec = new Ext.grid.PropertyRecord({
66582     name: 'Birthday',
66583     value: new Date(Date.parse('05/26/1972'))
66584 });
66585 // Add record to an already populated grid
66586 grid.store.addSorted(rec);
66587 </code></pre>
66588  * @constructor
66589  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
66590  * will be read automatically by the grid to determine the type of editor to use when displaying it.
66591  */
66592 Ext.grid.PropertyRecord = Ext.data.Record.create([
66593     {name:'name',type:'string'}, 'value'
66594 ]);
66595
66596 /**
66597  * @class Ext.grid.PropertyStore
66598  * @extends Ext.util.Observable
66599  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
66600  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
66601  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
66602  * the grid's data should be accessed from the underlying store via the {@link #store} property.
66603  * @constructor
66604  * @param {Ext.grid.Grid} grid The grid this store will be bound to
66605  * @param {Object} source The source data config object
66606  */
66607 Ext.grid.PropertyStore = function(grid, source){
66608     this.grid = grid;
66609     this.store = new Ext.data.Store({
66610         recordType : Ext.grid.PropertyRecord
66611     });
66612     this.store.on('update', this.onUpdate,  this);
66613     if(source){
66614         this.setSource(source);
66615     }
66616     Ext.grid.PropertyStore.superclass.constructor.call(this);
66617 };
66618 Ext.extend(Ext.grid.PropertyStore, Ext.util.Observable, {
66619     // protected - should only be called by the grid.  Use grid.setSource instead.
66620     setSource : function(o){
66621         this.source = o;
66622         this.store.removeAll();
66623         var data = [];
66624         for(var k in o){
66625             if(this.isEditableValue(o[k])){
66626                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
66627             }
66628         }
66629         this.store.loadRecords({records: data}, {}, true);
66630     },
66631
66632     // private
66633     onUpdate : function(ds, record, type){
66634         if(type == Ext.data.Record.EDIT){
66635             var v = record.data.value;
66636             var oldValue = record.modified.value;
66637             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66638                 this.source[record.id] = v;
66639                 record.commit();
66640                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66641             }else{
66642                 record.reject();
66643             }
66644         }
66645     },
66646
66647     // private
66648     getProperty : function(row){
66649        return this.store.getAt(row);
66650     },
66651
66652     // private
66653     isEditableValue: function(val){
66654         if(Ext.isDate(val)){
66655             return true;
66656         }
66657         return !(Ext.isObject(val) || Ext.isFunction(val));
66658     },
66659
66660     // private
66661     setValue : function(prop, value){
66662         this.source[prop] = value;
66663         this.store.getById(prop).set('value', value);
66664     },
66665
66666     // protected - should only be called by the grid.  Use grid.getSource instead.
66667     getSource : function(){
66668         return this.source;
66669     }
66670 });
66671
66672 /**
66673  * @class Ext.grid.PropertyColumnModel
66674  * @extends Ext.grid.ColumnModel
66675  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
66676  * @constructor
66677  * @param {Ext.grid.Grid} grid The grid this store will be bound to
66678  * @param {Object} source The source data config object
66679  */
66680 Ext.grid.PropertyColumnModel = function(grid, store){
66681     var g = Ext.grid,
66682         f = Ext.form;
66683         
66684     this.grid = grid;
66685     g.PropertyColumnModel.superclass.constructor.call(this, [
66686         {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
66687         {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
66688     ]);
66689     this.store = store;
66690
66691     var bfield = new f.Field({
66692         autoCreate: {tag: 'select', children: [
66693             {tag: 'option', value: 'true', html: 'true'},
66694             {tag: 'option', value: 'false', html: 'false'}
66695         ]},
66696         getValue : function(){
66697             return this.el.dom.value == 'true';
66698         }
66699     });
66700     this.editors = {
66701         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66702         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66703         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66704         'boolean' : new g.GridEditor(bfield, {
66705             autoSize: 'both'
66706         })
66707     };
66708     this.renderCellDelegate = this.renderCell.createDelegate(this);
66709     this.renderPropDelegate = this.renderProp.createDelegate(this);
66710 };
66711
66712 Ext.extend(Ext.grid.PropertyColumnModel, Ext.grid.ColumnModel, {
66713     // private - strings used for locale support
66714     nameText : 'Name',
66715     valueText : 'Value',
66716     dateFormat : 'm/j/Y',
66717
66718     // private
66719     renderDate : function(dateVal){
66720         return dateVal.dateFormat(this.dateFormat);
66721     },
66722
66723     // private
66724     renderBool : function(bVal){
66725         return bVal ? 'true' : 'false';
66726     },
66727
66728     // private
66729     isCellEditable : function(colIndex, rowIndex){
66730         return colIndex == 1;
66731     },
66732
66733     // private
66734     getRenderer : function(col){
66735         return col == 1 ?
66736             this.renderCellDelegate : this.renderPropDelegate;
66737     },
66738
66739     // private
66740     renderProp : function(v){
66741         return this.getPropertyName(v);
66742     },
66743
66744     // private
66745     renderCell : function(val){
66746         var rv = val;
66747         if(Ext.isDate(val)){
66748             rv = this.renderDate(val);
66749         }else if(typeof val == 'boolean'){
66750             rv = this.renderBool(val);
66751         }
66752         return Ext.util.Format.htmlEncode(rv);
66753     },
66754
66755     // private
66756     getPropertyName : function(name){
66757         var pn = this.grid.propertyNames;
66758         return pn && pn[name] ? pn[name] : name;
66759     },
66760
66761     // private
66762     getCellEditor : function(colIndex, rowIndex){
66763         var p = this.store.getProperty(rowIndex),
66764             n = p.data.name, 
66765             val = p.data.value;
66766         if(this.grid.customEditors[n]){
66767             return this.grid.customEditors[n];
66768         }
66769         if(Ext.isDate(val)){
66770             return this.editors.date;
66771         }else if(typeof val == 'number'){
66772             return this.editors.number;
66773         }else if(typeof val == 'boolean'){
66774             return this.editors['boolean'];
66775         }else{
66776             return this.editors.string;
66777         }
66778     },
66779
66780     // inherit docs
66781     destroy : function(){
66782         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
66783         for(var ed in this.editors){
66784             Ext.destroy(ed);
66785         }
66786     }
66787 });
66788
66789 /**
66790  * @class Ext.grid.PropertyGrid
66791  * @extends Ext.grid.EditorGridPanel
66792  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
66793  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
66794  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
66795  * <pre><code>
66796 var grid = new Ext.grid.PropertyGrid({
66797     title: 'Properties Grid',
66798     autoHeight: true,
66799     width: 300,
66800     renderTo: 'grid-ct',
66801     source: {
66802         "(name)": "My Object",
66803         "Created": new Date(Date.parse('10/15/2006')),
66804         "Available": false,
66805         "Version": .01,
66806         "Description": "A test object"
66807     }
66808 });
66809 </code></pre>
66810  * @constructor
66811  * @param {Object} config The grid config object
66812  */
66813 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
66814     /**
66815     * @cfg {Object} propertyNames An object containing property name/display name pairs.
66816     * If specified, the display name will be shown in the name column instead of the property name.
66817     */
66818     /**
66819     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
66820     */
66821     /**
66822     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
66823     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
66824     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
66825     * associated with a custom input control by specifying a custom editor.  The name of the editor
66826     * type should correspond with the name of the property that will use the editor.  Example usage:
66827     * <pre><code>
66828 var grid = new Ext.grid.PropertyGrid({
66829     ...
66830     customEditors: {
66831         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
66832     },
66833     source: {
66834         'Start Time': '10:00 AM'
66835     }
66836 });
66837 </code></pre>
66838     */
66839
66840     // private config overrides
66841     enableColumnMove:false,
66842     stripeRows:false,
66843     trackMouseOver: false,
66844     clicksToEdit:1,
66845     enableHdMenu : false,
66846     viewConfig : {
66847         forceFit:true
66848     },
66849
66850     // private
66851     initComponent : function(){
66852         this.customEditors = this.customEditors || {};
66853         this.lastEditRow = null;
66854         var store = new Ext.grid.PropertyStore(this);
66855         this.propStore = store;
66856         var cm = new Ext.grid.PropertyColumnModel(this, store);
66857         store.store.sort('name', 'ASC');
66858         this.addEvents(
66859             /**
66860              * @event beforepropertychange
66861              * Fires before a property value changes.  Handlers can return false to cancel the property change
66862              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
66863              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
66864              * as the {@link #source} config property).
66865              * @param {String} recordId The record's id in the data store
66866              * @param {Mixed} value The current edited property value
66867              * @param {Mixed} oldValue The original property value prior to editing
66868              */
66869             'beforepropertychange',
66870             /**
66871              * @event propertychange
66872              * Fires after a property value has changed.
66873              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
66874              * as the {@link #source} config property).
66875              * @param {String} recordId The record's id in the data store
66876              * @param {Mixed} value The current edited property value
66877              * @param {Mixed} oldValue The original property value prior to editing
66878              */
66879             'propertychange'
66880         );
66881         this.cm = cm;
66882         this.ds = store.store;
66883         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
66884
66885                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
66886             if(colIndex === 0){
66887                 this.startEditing.defer(200, this, [rowIndex, 1]);
66888                 return false;
66889             }
66890         }, this);
66891     },
66892
66893     // private
66894     onRender : function(){
66895         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
66896
66897         this.getGridEl().addClass('x-props-grid');
66898     },
66899
66900     // private
66901     afterRender: function(){
66902         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
66903         if(this.source){
66904             this.setSource(this.source);
66905         }
66906     },
66907
66908     /**
66909      * Sets the source data object containing the property data.  The data object can contain one or more name/value
66910      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
66911      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
66912      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
66913      * existing data.  See also the {@link #source} config value.  Example usage:
66914      * <pre><code>
66915 grid.setSource({
66916     "(name)": "My Object",
66917     "Created": new Date(Date.parse('10/15/2006')),  // date type
66918     "Available": false,  // boolean type
66919     "Version": .01,      // decimal type
66920     "Description": "A test object"
66921 });
66922 </code></pre>
66923      * @param {Object} source The data object
66924      */
66925     setSource : function(source){
66926         this.propStore.setSource(source);
66927     },
66928
66929     /**
66930      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
66931      * format of the data object.
66932      * @return {Object} The data object
66933      */
66934     getSource : function(){
66935         return this.propStore.getSource();
66936     }
66937 });
66938 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
66939 /**\r
66940  * @class Ext.grid.GroupingView\r
66941  * @extends Ext.grid.GridView\r
66942  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}\r
66943  * must be used to enable grouping.  Some grouping characteristics may also be configured at the\r
66944  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>\r
66945  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</li>\r
66946  * <li><code>{@link Ext.grid.Column#groupable groupable}</li>\r
66947  * <li><code>{@link Ext.grid.Column#groupName groupName}</li>\r
66948  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</li>\r
66949  * </ul></div>\r
66950  * <p>Sample usage:</p>\r
66951  * <pre><code>\r
66952 var grid = new Ext.grid.GridPanel({\r
66953     // A groupingStore is required for a GroupingView\r
66954     store: new {@link Ext.data.GroupingStore}({\r
66955         autoDestroy: true,\r
66956         reader: reader,\r
66957         data: xg.dummyData,\r
66958         sortInfo: {field: 'company', direction: 'ASC'},\r
66959         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,\r
66960         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,\r
66961         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'\r
66962     }),\r
66963     colModel: new {@link Ext.grid.ColumnModel}({\r
66964         columns:[\r
66965             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},\r
66966             // {@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
66967             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},\r
66968             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},\r
66969             {header: 'Industry', dataIndex: 'industry'},\r
66970             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}\r
66971         ],\r
66972         defaults: {\r
66973             sortable: true,\r
66974             menuDisabled: false,\r
66975             width: 20\r
66976         }\r
66977     }),\r
66978 \r
66979     view: new Ext.grid.GroupingView({\r
66980         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
66981         // custom grouping text template to display the number of items per group\r
66982         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
66983     }),\r
66984 \r
66985     frame:true,\r
66986     width: 700,\r
66987     height: 450,\r
66988     collapsible: true,\r
66989     animCollapse: false,\r
66990     title: 'Grouping Example',\r
66991     iconCls: 'icon-grid',\r
66992     renderTo: document.body\r
66993 });\r
66994  * </code></pre>\r
66995  * @constructor\r
66996  * @param {Object} config\r
66997  */\r
66998 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {\r
66999 \r
67000     /**\r
67001      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column\r
67002      * (defaults to 'Group By This Field').\r
67003      */\r
67004     groupByText : 'Group By This Field',\r
67005     /**\r
67006      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping\r
67007      * (defaults to 'Show in Groups').\r
67008      */\r
67009     showGroupsText : 'Show in Groups',\r
67010     /**\r
67011      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)\r
67012      */\r
67013     hideGroupedColumn : false,\r
67014     /**\r
67015      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value\r
67016      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>\r
67017      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the\r
67018      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).\r
67019      */\r
67020     showGroupName : true,\r
67021     /**\r
67022      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)\r
67023      */\r
67024     startCollapsed : false,\r
67025     /**\r
67026      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)\r
67027      */\r
67028     enableGrouping : true,\r
67029     /**\r
67030      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)\r
67031      */\r
67032     enableGroupingMenu : true,\r
67033     /**\r
67034      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)\r
67035      */\r
67036     enableNoGroups : true,\r
67037     /**\r
67038      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).\r
67039      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.\r
67040      */\r
67041     emptyGroupText : '(None)',\r
67042     /**\r
67043      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)\r
67044      */\r
67045     ignoreAdd : false,\r
67046     /**\r
67047      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).\r
67048      * This is used to format an object which contains the following properties:\r
67049      * <div class="mdetail-params"><ul>\r
67050      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.\r
67051      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>\r
67052      * is specified, it is the result of a call to that function.</p></li>\r
67053      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>\r
67054      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>\r
67055      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>\r
67056      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the\r
67057      * View Element which contains the group.</p></li>\r
67058      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
67059      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data\r
67060      * for the row which caused group change.</p></li>\r
67061      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>\r
67062      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>\r
67063      * </ul></div></p>\r
67064      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>\r
67065 var grid = new Ext.grid.GridPanel({\r
67066     ...\r
67067     view: new Ext.grid.GroupingView({\r
67068         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
67069     }),\r
67070 });\r
67071      * </code></pre>\r
67072      */\r
67073     groupTextTpl : '{text}',\r
67074     \r
67075     /**\r
67076      * @cfg {String} groupMode Indicates how to construct the group identifier. <tt>'value'</tt> constructs the id using\r
67077      * raw value, <tt>'display'</tt> constructs the id using the rendered value. Defaults to <tt>'value'</tt>.\r
67078      */\r
67079     groupMode: 'value',\r
67080     \r
67081     /**\r
67082      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for\r
67083      * each column.\r
67084      */\r
67085 \r
67086     // private\r
67087     gidSeed : 1000,\r
67088 \r
67089     // private\r
67090     initTemplates : function(){\r
67091         Ext.grid.GroupingView.superclass.initTemplates.call(this);\r
67092         this.state = {};\r
67093 \r
67094         var sm = this.grid.getSelectionModel();\r
67095         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',\r
67096                 this.onBeforeRowSelect, this);\r
67097 \r
67098         if(!this.startGroup){\r
67099             this.startGroup = new Ext.XTemplate(\r
67100                 '<div id="{groupId}" class="x-grid-group {cls}">',\r
67101                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',\r
67102                     '<div id="{groupId}-bd" class="x-grid-group-body">'\r
67103             );\r
67104         }\r
67105         this.startGroup.compile();\r
67106         this.endGroup = '</div></div>';\r
67107     },\r
67108 \r
67109     // private\r
67110     findGroup : function(el){\r
67111         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);\r
67112     },\r
67113 \r
67114     // private\r
67115     getGroups : function(){\r
67116         return this.hasRows() ? this.mainBody.dom.childNodes : [];\r
67117     },\r
67118 \r
67119     // private\r
67120     onAdd : function(){\r
67121         if(this.enableGrouping && !this.ignoreAdd){\r
67122             var ss = this.getScrollState();\r
67123             this.refresh();\r
67124             this.restoreScroll(ss);\r
67125         }else if(!this.enableGrouping){\r
67126             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);\r
67127         }\r
67128     },\r
67129 \r
67130     // private\r
67131     onRemove : function(ds, record, index, isUpdate){\r
67132         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);\r
67133         var g = document.getElementById(record._groupId);\r
67134         if(g && g.childNodes[1].childNodes.length < 1){\r
67135             Ext.removeNode(g);\r
67136         }\r
67137         this.applyEmptyText();\r
67138     },\r
67139 \r
67140     // private\r
67141     refreshRow : function(record){\r
67142         if(this.ds.getCount()==1){\r
67143             this.refresh();\r
67144         }else{\r
67145             this.isUpdating = true;\r
67146             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);\r
67147             this.isUpdating = false;\r
67148         }\r
67149     },\r
67150 \r
67151     // private\r
67152     beforeMenuShow : function(){\r
67153         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;\r
67154         if((item = items.get('groupBy'))){\r
67155             item.setDisabled(disabled);\r
67156         }\r
67157         if((item = items.get('showGroups'))){\r
67158             item.setDisabled(disabled);\r
67159                     item.setChecked(!!this.getGroupField(), true);\r
67160         }\r
67161     },\r
67162 \r
67163     // private\r
67164     renderUI : function(){\r
67165         Ext.grid.GroupingView.superclass.renderUI.call(this);\r
67166         this.mainBody.on('mousedown', this.interceptMouse, this);\r
67167 \r
67168         if(this.enableGroupingMenu && this.hmenu){\r
67169             this.hmenu.add('-',{\r
67170                 itemId:'groupBy',\r
67171                 text: this.groupByText,\r
67172                 handler: this.onGroupByClick,\r
67173                 scope: this,\r
67174                 iconCls:'x-group-by-icon'\r
67175             });\r
67176             if(this.enableNoGroups){\r
67177                 this.hmenu.add({\r
67178                     itemId:'showGroups',\r
67179                     text: this.showGroupsText,\r
67180                     checked: true,\r
67181                     checkHandler: this.onShowGroupsClick,\r
67182                     scope: this\r
67183                 });\r
67184             }\r
67185             this.hmenu.on('beforeshow', this.beforeMenuShow, this);\r
67186         }\r
67187     },\r
67188 \r
67189     // private\r
67190     onGroupByClick : function(){\r
67191         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));\r
67192         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups\r
67193     },\r
67194 \r
67195     // private\r
67196     onShowGroupsClick : function(mi, checked){\r
67197         if(checked){\r
67198             this.onGroupByClick();\r
67199         }else{\r
67200             this.grid.store.clearGrouping();\r
67201         }\r
67202     },\r
67203 \r
67204     /**\r
67205      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.\r
67206      * @param {String} groupId The groupId assigned to the group (see getGroupId)\r
67207      * @param {Boolean} expanded (optional)\r
67208      */\r
67209     toggleGroup : function(group, expanded){\r
67210         this.grid.stopEditing(true);\r
67211         group = Ext.getDom(group);\r
67212         var gel = Ext.fly(group);\r
67213         expanded = expanded !== undefined ?\r
67214                 expanded : gel.hasClass('x-grid-group-collapsed');\r
67215 \r
67216         this.state[gel.dom.id] = expanded;\r
67217         gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');\r
67218     },\r
67219 \r
67220     /**\r
67221      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.\r
67222      * @param {Boolean} expanded (optional)\r
67223      */\r
67224     toggleAllGroups : function(expanded){\r
67225         var groups = this.getGroups();\r
67226         for(var i = 0, len = groups.length; i < len; i++){\r
67227             this.toggleGroup(groups[i], expanded);\r
67228         }\r
67229     },\r
67230 \r
67231     /**\r
67232      * Expands all grouped rows.\r
67233      */\r
67234     expandAllGroups : function(){\r
67235         this.toggleAllGroups(true);\r
67236     },\r
67237 \r
67238     /**\r
67239      * Collapses all grouped rows.\r
67240      */\r
67241     collapseAllGroups : function(){\r
67242         this.toggleAllGroups(false);\r
67243     },\r
67244 \r
67245     // private\r
67246     interceptMouse : function(e){\r
67247         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);\r
67248         if(hd){\r
67249             e.stopEvent();\r
67250             this.toggleGroup(hd.parentNode);\r
67251         }\r
67252     },\r
67253 \r
67254     // private\r
67255     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){\r
67256         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);\r
67257         if(g === '' || g === '&#160;'){\r
67258             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;\r
67259         }\r
67260         return g;\r
67261     },\r
67262 \r
67263     // private\r
67264     getGroupField : function(){\r
67265         return this.grid.store.getGroupState();\r
67266     },\r
67267     \r
67268     // private\r
67269     afterRender : function(){\r
67270         Ext.grid.GroupingView.superclass.afterRender.call(this);\r
67271         if(this.grid.deferRowRender){\r
67272             this.updateGroupWidths();\r
67273         }\r
67274     },\r
67275 \r
67276     // private\r
67277     renderRows : function(){\r
67278         var groupField = this.getGroupField();\r
67279         var eg = !!groupField;\r
67280         // if they turned off grouping and the last grouped field is hidden\r
67281         if(this.hideGroupedColumn) {\r
67282             var colIndex = this.cm.findColumnIndex(groupField);\r
67283             if(!eg && this.lastGroupField !== undefined) {\r
67284                 this.mainBody.update('');\r
67285                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);\r
67286                 delete this.lastGroupField;\r
67287             }else if (eg && this.lastGroupField === undefined) {\r
67288                 this.lastGroupField = groupField;\r
67289                 this.cm.setHidden(colIndex, true);\r
67290             }else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) {\r
67291                 this.mainBody.update('');\r
67292                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);\r
67293                 this.cm.setHidden(oldIndex, false);\r
67294                 this.lastGroupField = groupField;\r
67295                 this.cm.setHidden(colIndex, true);\r
67296             }\r
67297         }\r
67298         return Ext.grid.GroupingView.superclass.renderRows.apply(\r
67299                     this, arguments);\r
67300     },\r
67301 \r
67302     // private\r
67303     doRender : function(cs, rs, ds, startRow, colCount, stripe){\r
67304         if(rs.length < 1){\r
67305             return '';\r
67306         }\r
67307         var groupField = this.getGroupField(),\r
67308             colIndex = this.cm.findColumnIndex(groupField),\r
67309             g;\r
67310 \r
67311         this.enableGrouping = !!groupField;\r
67312 \r
67313         if(!this.enableGrouping || this.isUpdating){\r
67314             return Ext.grid.GroupingView.superclass.doRender.apply(\r
67315                     this, arguments);\r
67316         }\r
67317         var gstyle = 'width:' + this.getTotalWidth() + ';',\r
67318             gidPrefix = this.grid.getGridEl().id,\r
67319             cfg = this.cm.config[colIndex],\r
67320             groupRenderer = cfg.groupRenderer || cfg.renderer,\r
67321             prefix = this.showGroupName ? (cfg.groupName || cfg.header)+': ' : '',\r
67322             groups = [],\r
67323             curGroup, i, len, gid;\r
67324 \r
67325         for(i = 0, len = rs.length; i < len; i++){\r
67326             var rowIndex = startRow + i,\r
67327                 r = rs[i],\r
67328                 gvalue = r.data[groupField];\r
67329                 \r
67330                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);\r
67331             if(!curGroup || curGroup.group != g){\r
67332                 gid = this.constructId(gvalue, gidPrefix, groupField, colIndex);\r
67333                 // if state is defined use it, however state is in terms of expanded\r
67334                                 // so negate it, otherwise use the default.\r
67335                                 var isCollapsed  = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;\r
67336                                 var gcls = isCollapsed ? 'x-grid-group-collapsed' : ''; \r
67337                 curGroup = {\r
67338                     group: g,\r
67339                     gvalue: gvalue,\r
67340                     text: prefix + g,\r
67341                     groupId: gid,\r
67342                     startRow: rowIndex,\r
67343                     rs: [r],\r
67344                     cls: gcls,\r
67345                     style: gstyle\r
67346                 };\r
67347                 groups.push(curGroup);\r
67348             }else{\r
67349                 curGroup.rs.push(r);\r
67350             }\r
67351             r._groupId = gid;\r
67352         }\r
67353 \r
67354         var buf = [];\r
67355         for(i = 0, len = groups.length; i < len; i++){\r
67356             g = groups[i];\r
67357             this.doGroupStart(buf, g, cs, ds, colCount);\r
67358             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(\r
67359                     this, cs, g.rs, ds, g.startRow, colCount, stripe);\r
67360 \r
67361             this.doGroupEnd(buf, g, cs, ds, colCount);\r
67362         }\r
67363         return buf.join('');\r
67364     },\r
67365 \r
67366     /**\r
67367      * Dynamically tries to determine the groupId of a specific value\r
67368      * @param {String} value\r
67369      * @return {String} The group id\r
67370      */\r
67371     getGroupId : function(value){\r
67372         var field = this.getGroupField();\r
67373         return this.constructId(value, this.grid.getGridEl().id, field, this.cm.findColumnIndex(field));\r
67374     },\r
67375     \r
67376     // private\r
67377     constructId : function(value, prefix, field, idx){\r
67378         var cfg = this.cm.config[idx],\r
67379             groupRenderer = cfg.groupRenderer || cfg.renderer,\r
67380             val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds);\r
67381             \r
67382         return prefix + '-gp-' + field + '-' + Ext.util.Format.htmlEncode(val);\r
67383     },\r
67384 \r
67385     // private\r
67386     doGroupStart : function(buf, g, cs, ds, colCount){\r
67387         buf[buf.length] = this.startGroup.apply(g);\r
67388     },\r
67389 \r
67390     // private\r
67391     doGroupEnd : function(buf, g, cs, ds, colCount){\r
67392         buf[buf.length] = this.endGroup;\r
67393     },\r
67394 \r
67395     // private\r
67396     getRows : function(){\r
67397         if(!this.enableGrouping){\r
67398             return Ext.grid.GroupingView.superclass.getRows.call(this);\r
67399         }\r
67400         var r = [];\r
67401         var g, gs = this.getGroups();\r
67402         for(var i = 0, len = gs.length; i < len; i++){\r
67403             g = gs[i].childNodes[1].childNodes;\r
67404             for(var j = 0, jlen = g.length; j < jlen; j++){\r
67405                 r[r.length] = g[j];\r
67406             }\r
67407         }\r
67408         return r;\r
67409     },\r
67410 \r
67411     // private\r
67412     updateGroupWidths : function(){\r
67413         if(!this.enableGrouping || !this.hasRows()){\r
67414             return;\r
67415         }\r
67416         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';\r
67417         var gs = this.getGroups();\r
67418         for(var i = 0, len = gs.length; i < len; i++){\r
67419             gs[i].firstChild.style.width = tw;\r
67420         }\r
67421     },\r
67422 \r
67423     // private\r
67424     onColumnWidthUpdated : function(col, w, tw){\r
67425         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);\r
67426         this.updateGroupWidths();\r
67427     },\r
67428 \r
67429     // private\r
67430     onAllColumnWidthsUpdated : function(ws, tw){\r
67431         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);\r
67432         this.updateGroupWidths();\r
67433     },\r
67434 \r
67435     // private\r
67436     onColumnHiddenUpdated : function(col, hidden, tw){\r
67437         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);\r
67438         this.updateGroupWidths();\r
67439     },\r
67440 \r
67441     // private\r
67442     onLayout : function(){\r
67443         this.updateGroupWidths();\r
67444     },\r
67445 \r
67446     // private\r
67447     onBeforeRowSelect : function(sm, rowIndex){\r
67448         if(!this.enableGrouping){\r
67449             return;\r
67450         }\r
67451         var row = this.getRow(rowIndex);\r
67452         if(row && !row.offsetParent){\r
67453             var g = this.findGroup(row);\r
67454             this.toggleGroup(g, true);\r
67455         }\r
67456     }\r
67457 });\r
67458 // private\r
67459 Ext.grid.GroupingView.GROUP_ID = 1000;