Upgrade to ExtJS 3.3.0 - Released 10/06/2010
[extjs.git] / pkgs / ext-foundation-debug.js
1 /*!
2  * Ext JS Library 3.3.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext
9  */
10
11 Ext.ns("Ext.grid", "Ext.list", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu",
12        "Ext.state", "Ext.layout", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct");
13     /**
14      * Namespace alloted for extensions to the framework.
15      * @property ux
16      * @type Object
17      */
18
19 Ext.apply(Ext, function(){
20     var E = Ext,
21         idSeed = 0,
22         scrollWidth = null;
23
24     return {
25         /**
26         * A reusable empty function
27         * @property
28         * @type Function
29         */
30         emptyFn : function(){},
31
32         /**
33          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
34          * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server.
35          * For other browsers it uses an inline data URL.
36          * @type String
37          */
38         BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 || Ext.isAir ?
39                             'http:/' + '/www.extjs.com/s.gif' :
40                             'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
41
42         extendX : function(supr, fn){
43             return Ext.extend(supr, fn(supr.prototype));
44         },
45
46         /**
47          * Returns the current HTML document object as an {@link Ext.Element}.
48          * @return Ext.Element The document
49          */
50         getDoc : function(){
51             return Ext.get(document);
52         },
53
54         /**
55          * Utility method for validating that a value is numeric, returning the specified default value if it is not.
56          * @param {Mixed} value Should be a number, but any type will be handled appropriately
57          * @param {Number} defaultValue The value to return if the original value is non-numeric
58          * @return {Number} Value, if numeric, else defaultValue
59          */
60         num : function(v, defaultValue){
61             v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && v.trim().length == 0) ? NaN : v);
62             return isNaN(v) ? defaultValue : v;
63         },
64
65         /**
66          * <p>Utility method for returning a default value if the passed value is empty.</p>
67          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
68          * <li>null</li>
69          * <li>undefined</li>
70          * <li>an empty array</li>
71          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
72          * </ul></div>
73          * @param {Mixed} value The value to test
74          * @param {Mixed} defaultValue The value to return if the original value is empty
75          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
76          * @return {Mixed} value, if non-empty, else defaultValue
77          */
78         value : function(v, defaultValue, allowBlank){
79             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
80         },
81
82         /**
83          * Escapes the passed string for use in a regular expression
84          * @param {String} str
85          * @return {String}
86          */
87         escapeRe : function(s) {
88             return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
89         },
90
91         sequence : function(o, name, fn, scope){
92             o[name] = o[name].createSequence(fn, scope);
93         },
94
95         /**
96          * Applies event listeners to elements by selectors when the document is ready.
97          * The event name is specified with an <tt>&#64;</tt> suffix.
98          * <pre><code>
99 Ext.addBehaviors({
100     // add a listener for click on all anchors in element with id foo
101     '#foo a&#64;click' : function(e, t){
102         // do something
103     },
104
105     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
106     '#foo a, #bar span.some-class&#64;mouseover' : function(){
107         // do something
108     }
109 });
110          * </code></pre>
111          * @param {Object} obj The list of behaviors to apply
112          */
113         addBehaviors : function(o){
114             if(!Ext.isReady){
115                 Ext.onReady(function(){
116                     Ext.addBehaviors(o);
117                 });
118             } else {
119                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
120                     parts,
121                     b,
122                     s;
123                 for (b in o) {
124                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
125                         s = parts[0];
126                         if(!cache[s]){
127                             cache[s] = Ext.select(s);
128                         }
129                         cache[s].on(parts[1], o[b]);
130                     }
131                 }
132                 cache = null;
133             }
134         },
135
136         /**
137          * Utility method for getting the width of the browser scrollbar. This can differ depending on
138          * operating system settings, such as the theme or font size.
139          * @param {Boolean} force (optional) true to force a recalculation of the value.
140          * @return {Number} The width of the scrollbar.
141          */
142         getScrollBarWidth: function(force){
143             if(!Ext.isReady){
144                 return 0;
145             }
146
147             if(force === true || scrollWidth === null){
148                     // Append our div, do our calculation and then remove it
149                 var div = Ext.getBody().createChild('<div class="x-hide-offsets" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
150                     child = div.child('div', true);
151                 var w1 = child.offsetWidth;
152                 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
153                 var w2 = child.offsetWidth;
154                 div.remove();
155                 // Need to add 2 to ensure we leave enough space
156                 scrollWidth = w1 - w2 + 2;
157             }
158             return scrollWidth;
159         },
160
161
162         // deprecated
163         combine : function(){
164             var as = arguments, l = as.length, r = [];
165             for(var i = 0; i < l; i++){
166                 var a = as[i];
167                 if(Ext.isArray(a)){
168                     r = r.concat(a);
169                 }else if(a.length !== undefined && !a.substr){
170                     r = r.concat(Array.prototype.slice.call(a, 0));
171                 }else{
172                     r.push(a);
173                 }
174             }
175             return r;
176         },
177
178         /**
179          * Copies a set of named properties fom the source object to the destination object.
180          * <p>example:<pre><code>
181 ImageComponent = Ext.extend(Ext.BoxComponent, {
182     initComponent: function() {
183         this.autoEl = { tag: 'img' };
184         MyComponent.superclass.initComponent.apply(this, arguments);
185         this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
186     }
187 });
188          * </code></pre>
189          * @param {Object} dest The destination object.
190          * @param {Object} source The source object.
191          * @param {Array/String} names Either an Array of property names, or a comma-delimited list
192          * of property names to copy.
193          * @return {Object} The modified object.
194         */
195         copyTo : function(dest, source, names){
196             if(typeof names == 'string'){
197                 names = names.split(/[,;\s]/);
198             }
199             Ext.each(names, function(name){
200                 if(source.hasOwnProperty(name)){
201                     dest[name] = source[name];
202                 }
203             }, this);
204             return dest;
205         },
206
207         /**
208          * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
209          * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
210          * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
211          * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
212          * passed into this function in a single call as separate arguments.
213          * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
214          * @param {Mixed} arg2 (optional)
215          * @param {Mixed} etc... (optional)
216          */
217         destroy : function(){
218             Ext.each(arguments, function(arg){
219                 if(arg){
220                     if(Ext.isArray(arg)){
221                         this.destroy.apply(this, arg);
222                     }else if(typeof arg.destroy == 'function'){
223                         arg.destroy();
224                     }else if(arg.dom){
225                         arg.remove();
226                     }
227                 }
228             }, this);
229         },
230
231         /**
232          * Attempts to destroy and then remove a set of named properties of the passed object.
233          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
234          * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
235          * @param {Mixed} etc... More property names to destroy and remove.
236          */
237         destroyMembers : function(o, arg1, arg2, etc){
238             for(var i = 1, a = arguments, len = a.length; i < len; i++) {
239                 Ext.destroy(o[a[i]]);
240                 delete o[a[i]];
241             }
242         },
243
244         /**
245          * Creates a copy of the passed Array with falsy values removed.
246          * @param {Array/NodeList} arr The Array from which to remove falsy values.
247          * @return {Array} The new, compressed Array.
248          */
249         clean : function(arr){
250             var ret = [];
251             Ext.each(arr, function(v){
252                 if(!!v){
253                     ret.push(v);
254                 }
255             });
256             return ret;
257         },
258
259         /**
260          * Creates a copy of the passed Array, filtered to contain only unique values.
261          * @param {Array} arr The Array to filter
262          * @return {Array} The new Array containing unique values.
263          */
264         unique : function(arr){
265             var ret = [],
266                 collect = {};
267
268             Ext.each(arr, function(v) {
269                 if(!collect[v]){
270                     ret.push(v);
271                 }
272                 collect[v] = true;
273             });
274             return ret;
275         },
276
277         /**
278          * Recursively flattens into 1-d Array. Injects Arrays inline.
279          * @param {Array} arr The array to flatten
280          * @return {Array} The new, flattened array.
281          */
282         flatten : function(arr){
283             var worker = [];
284             function rFlatten(a) {
285                 Ext.each(a, function(v) {
286                     if(Ext.isArray(v)){
287                         rFlatten(v);
288                     }else{
289                         worker.push(v);
290                     }
291                 });
292                 return worker;
293             }
294             return rFlatten(arr);
295         },
296
297         /**
298          * Returns the minimum value in the Array.
299          * @param {Array|NodeList} arr The Array from which to select the minimum value.
300          * @param {Function} comp (optional) a function to perform the comparision which determines minimization.
301          *                   If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
302          * @return {Object} The minimum value in the Array.
303          */
304         min : function(arr, comp){
305             var ret = arr[0];
306             comp = comp || function(a,b){ return a < b ? -1 : 1; };
307             Ext.each(arr, function(v) {
308                 ret = comp(ret, v) == -1 ? ret : v;
309             });
310             return ret;
311         },
312
313         /**
314          * Returns the maximum value in the Array
315          * @param {Array|NodeList} arr The Array from which to select the maximum value.
316          * @param {Function} comp (optional) a function to perform the comparision which determines maximization.
317          *                   If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
318          * @return {Object} The maximum value in the Array.
319          */
320         max : function(arr, comp){
321             var ret = arr[0];
322             comp = comp || function(a,b){ return a > b ? 1 : -1; };
323             Ext.each(arr, function(v) {
324                 ret = comp(ret, v) == 1 ? ret : v;
325             });
326             return ret;
327         },
328
329         /**
330          * Calculates the mean of the Array
331          * @param {Array} arr The Array to calculate the mean value of.
332          * @return {Number} The mean.
333          */
334         mean : function(arr){
335            return arr.length > 0 ? Ext.sum(arr) / arr.length : undefined;
336         },
337
338         /**
339          * Calculates the sum of the Array
340          * @param {Array} arr The Array to calculate the sum value of.
341          * @return {Number} The sum.
342          */
343         sum : function(arr){
344            var ret = 0;
345            Ext.each(arr, function(v) {
346                ret += v;
347            });
348            return ret;
349         },
350
351         /**
352          * Partitions the set into two sets: a true set and a false set.
353          * Example:
354          * Example2:
355          * <pre><code>
356 // Example 1:
357 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
358
359 // Example 2:
360 Ext.partition(
361     Ext.query("p"),
362     function(val){
363         return val.className == "class1"
364     }
365 );
366 // true are those paragraph elements with a className of "class1",
367 // false set are those that do not have that className.
368          * </code></pre>
369          * @param {Array|NodeList} arr The array to partition
370          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
371          *                   itself must be able to be evaluated for its truthfulness.
372          * @return {Array} [true<Array>,false<Array>]
373          */
374         partition : function(arr, truth){
375             var ret = [[],[]];
376             Ext.each(arr, function(v, i, a) {
377                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
378             });
379             return ret;
380         },
381
382         /**
383          * Invokes a method on each item in an Array.
384          * <pre><code>
385 // Example:
386 Ext.invoke(Ext.query("p"), "getAttribute", "id");
387 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
388          * </code></pre>
389          * @param {Array|NodeList} arr The Array of items to invoke the method on.
390          * @param {String} methodName The method name to invoke.
391          * @param {...*} args Arguments to send into the method invocation.
392          * @return {Array} The results of invoking the method on each item in the array.
393          */
394         invoke : function(arr, methodName){
395             var ret = [],
396                 args = Array.prototype.slice.call(arguments, 2);
397             Ext.each(arr, function(v,i) {
398                 if (v && typeof v[methodName] == 'function') {
399                     ret.push(v[methodName].apply(v, args));
400                 } else {
401                     ret.push(undefined);
402                 }
403             });
404             return ret;
405         },
406
407         /**
408          * Plucks the value of a property from each item in the Array
409          * <pre><code>
410 // Example:
411 Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
412          * </code></pre>
413          * @param {Array|NodeList} arr The Array of items to pluck the value from.
414          * @param {String} prop The property name to pluck from each element.
415          * @return {Array} The value from each item in the Array.
416          */
417         pluck : function(arr, prop){
418             var ret = [];
419             Ext.each(arr, function(v) {
420                 ret.push( v[prop] );
421             });
422             return ret;
423         },
424
425         /**
426          * <p>Zips N sets together.</p>
427          * <pre><code>
428 // Example 1:
429 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
430 // Example 2:
431 Ext.zip(
432     [ "+", "-", "+"],
433     [  12,  10,  22],
434     [  43,  15,  96],
435     function(a, b, c){
436         return "$" + a + "" + b + "." + c
437     }
438 ); // ["$+12.43", "$-10.15", "$+22.96"]
439          * </code></pre>
440          * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
441          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
442          * @return {Array} The zipped set.
443          */
444         zip : function(){
445             var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
446                 arrs = parts[0],
447                 fn = parts[1][0],
448                 len = Ext.max(Ext.pluck(arrs, "length")),
449                 ret = [];
450
451             for (var i = 0; i < len; i++) {
452                 ret[i] = [];
453                 if(fn){
454                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
455                 }else{
456                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
457                         ret[i].push( arrs[j][i] );
458                     }
459                 }
460             }
461             return ret;
462         },
463
464         /**
465          * This is shorthand reference to {@link Ext.ComponentMgr#get}.
466          * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
467          * @param {String} id The component {@link Ext.Component#id id}
468          * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
469          * Class was found.
470         */
471         getCmp : function(id){
472             return Ext.ComponentMgr.get(id);
473         },
474
475         /**
476          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
477          * you may want to set this to true.
478          * @type Boolean
479          */
480         useShims: E.isIE6 || (E.isMac && E.isGecko2),
481
482         // inpired by a similar function in mootools library
483         /**
484          * Returns the type of object that is passed in. If the object passed in is null or undefined it
485          * return false otherwise it returns one of the following values:<div class="mdetail-params"><ul>
486          * <li><b>string</b>: If the object passed is a string</li>
487          * <li><b>number</b>: If the object passed is a number</li>
488          * <li><b>boolean</b>: If the object passed is a boolean value</li>
489          * <li><b>date</b>: If the object passed is a Date object</li>
490          * <li><b>function</b>: If the object passed is a function reference</li>
491          * <li><b>object</b>: If the object passed is an object</li>
492          * <li><b>array</b>: If the object passed is an array</li>
493          * <li><b>regexp</b>: If the object passed is a regular expression</li>
494          * <li><b>element</b>: If the object passed is a DOM Element</li>
495          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
496          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
497          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
498          * </ul></div>
499          * @param {Mixed} object
500          * @return {String}
501          */
502         type : function(o){
503             if(o === undefined || o === null){
504                 return false;
505             }
506             if(o.htmlElement){
507                 return 'element';
508             }
509             var t = typeof o;
510             if(t == 'object' && o.nodeName) {
511                 switch(o.nodeType) {
512                     case 1: return 'element';
513                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
514                 }
515             }
516             if(t == 'object' || t == 'function') {
517                 switch(o.constructor) {
518                     case Array: return 'array';
519                     case RegExp: return 'regexp';
520                     case Date: return 'date';
521                 }
522                 if(typeof o.length == 'number' && typeof o.item == 'function') {
523                     return 'nodelist';
524                 }
525             }
526             return t;
527         },
528
529         intercept : function(o, name, fn, scope){
530             o[name] = o[name].createInterceptor(fn, scope);
531         },
532
533         // internal
534         callback : function(cb, scope, args, delay){
535             if(typeof cb == 'function'){
536                 if(delay){
537                     cb.defer(delay, scope, args || []);
538                 }else{
539                     cb.apply(scope, args || []);
540                 }
541             }
542         }
543     };
544 }());
545
546 /**
547  * @class Function
548  * These functions are available on every Function object (any JavaScript function).
549  */
550 Ext.apply(Function.prototype, {
551     /**
552      * Create a combined function call sequence of the original function + the passed function.
553      * The resulting function returns the results of the original function.
554      * The passed fcn is called with the parameters of the original function. Example usage:
555      * <pre><code>
556 var sayHi = function(name){
557     alert('Hi, ' + name);
558 }
559
560 sayHi('Fred'); // alerts "Hi, Fred"
561
562 var sayGoodbye = sayHi.createSequence(function(name){
563     alert('Bye, ' + name);
564 });
565
566 sayGoodbye('Fred'); // both alerts show
567 </code></pre>
568      * @param {Function} fcn The function to sequence
569      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
570      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
571      * @return {Function} The new function
572      */
573     createSequence : function(fcn, scope){
574         var method = this;
575         return (typeof fcn != 'function') ?
576                 this :
577                 function(){
578                     var retval = method.apply(this || window, arguments);
579                     fcn.apply(scope || this || window, arguments);
580                     return retval;
581                 };
582     }
583 });
584
585
586 /**
587  * @class String
588  * These functions are available as static methods on the JavaScript String object.
589  */
590 Ext.applyIf(String, {
591
592     /**
593      * Escapes the passed string for ' and \
594      * @param {String} string The string to escape
595      * @return {String} The escaped string
596      * @static
597      */
598     escape : function(string) {
599         return string.replace(/('|\\)/g, "\\$1");
600     },
601
602     /**
603      * Pads the left side of a string with a specified character.  This is especially useful
604      * for normalizing number and date strings.  Example usage:
605      * <pre><code>
606 var s = String.leftPad('123', 5, '0');
607 // s now contains the string: '00123'
608      * </code></pre>
609      * @param {String} string The original string
610      * @param {Number} size The total length of the output string
611      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
612      * @return {String} The padded string
613      * @static
614      */
615     leftPad : function (val, size, ch) {
616         var result = String(val);
617         if(!ch) {
618             ch = " ";
619         }
620         while (result.length < size) {
621             result = ch + result;
622         }
623         return result;
624     }
625 });
626
627 /**
628  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
629  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
630  * they are already different, the first value passed in is returned.  Note that this method returns the new value
631  * but does not change the current string.
632  * <pre><code>
633 // alternate sort directions
634 sort = sort.toggle('ASC', 'DESC');
635
636 // instead of conditional logic:
637 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
638 </code></pre>
639  * @param {String} value The value to compare to the current string
640  * @param {String} other The new value to use if the string already equals the first value passed in
641  * @return {String} The new value
642  */
643 String.prototype.toggle = function(value, other){
644     return this == value ? other : value;
645 };
646
647 /**
648  * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
649  * <pre><code>
650 var s = '  foo bar  ';
651 alert('-' + s + '-');         //alerts "- foo bar -"
652 alert('-' + s.trim() + '-');  //alerts "-foo bar-"
653 </code></pre>
654  * @return {String} The trimmed string
655  */
656 String.prototype.trim = function(){
657     var re = /^\s+|\s+$/g;
658     return function(){ return this.replace(re, ""); };
659 }();
660
661 // here to prevent dependency on Date.js
662 /**
663  Returns the number of milliseconds between this date and date
664  @param {Date} date (optional) Defaults to now
665  @return {Number} The diff in milliseconds
666  @member Date getElapsed
667  */
668 Date.prototype.getElapsed = function(date) {
669     return Math.abs((date || new Date()).getTime()-this.getTime());
670 };
671
672
673 /**
674  * @class Number
675  */
676 Ext.applyIf(Number.prototype, {
677     /**
678      * Checks whether or not the current number is within a desired range.  If the number is already within the
679      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
680      * exceeded.  Note that this method returns the constrained value but does not change the current number.
681      * @param {Number} min The minimum number in the range
682      * @param {Number} max The maximum number in the range
683      * @return {Number} The constrained value if outside the range, otherwise the current value
684      */
685     constrain : function(min, max){
686         return Math.min(Math.max(this, min), max);
687     }
688 });
689 Ext.lib.Dom.getRegion = function(el) {
690     return Ext.lib.Region.getRegion(el);
691 };      Ext.lib.Region = function(t, r, b, l) {
692                 var me = this;
693         me.top = t;
694         me[1] = t;
695         me.right = r;
696         me.bottom = b;
697         me.left = l;
698         me[0] = l;
699     };
700
701     Ext.lib.Region.prototype = {
702         contains : function(region) {
703                 var me = this;
704             return ( region.left >= me.left &&
705                      region.right <= me.right &&
706                      region.top >= me.top &&
707                      region.bottom <= me.bottom );
708
709         },
710
711         getArea : function() {
712                 var me = this;
713             return ( (me.bottom - me.top) * (me.right - me.left) );
714         },
715
716         intersect : function(region) {
717             var me = this,
718                 t = Math.max(me.top, region.top),
719                 r = Math.min(me.right, region.right),
720                 b = Math.min(me.bottom, region.bottom),
721                 l = Math.max(me.left, region.left);
722
723             if (b >= t && r >= l) {
724                 return new Ext.lib.Region(t, r, b, l);
725             }
726         },
727         
728         union : function(region) {
729                 var me = this,
730                 t = Math.min(me.top, region.top),
731                 r = Math.max(me.right, region.right),
732                 b = Math.max(me.bottom, region.bottom),
733                 l = Math.min(me.left, region.left);
734
735             return new Ext.lib.Region(t, r, b, l);
736         },
737
738         constrainTo : function(r) {
739                 var me = this;
740             me.top = me.top.constrain(r.top, r.bottom);
741             me.bottom = me.bottom.constrain(r.top, r.bottom);
742             me.left = me.left.constrain(r.left, r.right);
743             me.right = me.right.constrain(r.left, r.right);
744             return me;
745         },
746
747         adjust : function(t, l, b, r) {
748                 var me = this;
749             me.top += t;
750             me.left += l;
751             me.right += r;
752             me.bottom += b;
753             return me;
754         }
755     };
756
757     Ext.lib.Region.getRegion = function(el) {
758         var p = Ext.lib.Dom.getXY(el),
759                 t = p[1],
760                 r = p[0] + el.offsetWidth,
761                 b = p[1] + el.offsetHeight,
762                 l = p[0];
763
764         return new Ext.lib.Region(t, r, b, l);
765     };  Ext.lib.Point = function(x, y) {
766         if (Ext.isArray(x)) {
767             y = x[1];
768             x = x[0];
769         }
770         var me = this;
771         me.x = me.right = me.left = me[0] = x;
772         me.y = me.top = me.bottom = me[1] = y;
773     };
774
775     Ext.lib.Point.prototype = new Ext.lib.Region();
776 /**
777  * @class Ext.DomHelper
778  */
779 Ext.apply(Ext.DomHelper,
780 function(){
781     var pub,
782         afterbegin = 'afterbegin',
783         afterend = 'afterend',
784         beforebegin = 'beforebegin',
785         beforeend = 'beforeend',
786         confRe = /tag|children|cn|html$/i;
787
788     // private
789     function doInsert(el, o, returnElement, pos, sibling, append){
790         el = Ext.getDom(el);
791         var newNode;
792         if (pub.useDom) {
793             newNode = createDom(o, null);
794             if (append) {
795                 el.appendChild(newNode);
796             } else {
797                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
798             }
799         } else {
800             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
801         }
802         return returnElement ? Ext.get(newNode, true) : newNode;
803     }
804
805     // build as dom
806     /** @ignore */
807     function createDom(o, parentNode){
808         var el,
809             doc = document,
810             useSet,
811             attr,
812             val,
813             cn;
814
815         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
816             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
817             for (var i = 0, l = o.length; i < l; i++) {
818                 createDom(o[i], el);
819             }
820         } else if (typeof o == 'string') {         // Allow a string as a child spec.
821             el = doc.createTextNode(o);
822         } else {
823             el = doc.createElement( o.tag || 'div' );
824             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
825             for (var attr in o) {
826                 if(!confRe.test(attr)){
827                     val = o[attr];
828                     if(attr == 'cls'){
829                         el.className = val;
830                     }else{
831                         if(useSet){
832                             el.setAttribute(attr, val);
833                         }else{
834                             el[attr] = val;
835                         }
836                     }
837                 }
838             }
839             Ext.DomHelper.applyStyles(el, o.style);
840
841             if ((cn = o.children || o.cn)) {
842                 createDom(cn, el);
843             } else if (o.html) {
844                 el.innerHTML = o.html;
845             }
846         }
847         if(parentNode){
848            parentNode.appendChild(el);
849         }
850         return el;
851     }
852
853     pub = {
854         /**
855          * Creates a new Ext.Template from the DOM object spec.
856          * @param {Object} o The DOM object spec (and children)
857          * @return {Ext.Template} The new template
858          */
859         createTemplate : function(o){
860             var html = Ext.DomHelper.createHtml(o);
861             return new Ext.Template(html);
862         },
863
864         /** True to force the use of DOM instead of html fragments @type Boolean */
865         useDom : false,
866
867         /**
868          * Creates new DOM element(s) and inserts them before el.
869          * @param {Mixed} el The context element
870          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
871          * @param {Boolean} returnElement (optional) true to return a Ext.Element
872          * @return {HTMLElement/Ext.Element} The new node
873          * @hide (repeat)
874          */
875         insertBefore : function(el, o, returnElement){
876             return doInsert(el, o, returnElement, beforebegin);
877         },
878
879         /**
880          * Creates new DOM element(s) and inserts them after el.
881          * @param {Mixed} el The context element
882          * @param {Object} o The DOM object spec (and children)
883          * @param {Boolean} returnElement (optional) true to return a Ext.Element
884          * @return {HTMLElement/Ext.Element} The new node
885          * @hide (repeat)
886          */
887         insertAfter : function(el, o, returnElement){
888             return doInsert(el, o, returnElement, afterend, 'nextSibling');
889         },
890
891         /**
892          * Creates new DOM element(s) and inserts them as the first child of el.
893          * @param {Mixed} el The context element
894          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
895          * @param {Boolean} returnElement (optional) true to return a Ext.Element
896          * @return {HTMLElement/Ext.Element} The new node
897          * @hide (repeat)
898          */
899         insertFirst : function(el, o, returnElement){
900             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
901         },
902
903         /**
904          * Creates new DOM element(s) and appends them to el.
905          * @param {Mixed} el The context element
906          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
907          * @param {Boolean} returnElement (optional) true to return a Ext.Element
908          * @return {HTMLElement/Ext.Element} The new node
909          * @hide (repeat)
910          */
911         append: function(el, o, returnElement){
912             return doInsert(el, o, returnElement, beforeend, '', true);
913         },
914
915         /**
916          * Creates new DOM element(s) without inserting them to the document.
917          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
918          * @return {HTMLElement} The new uninserted node
919          */
920         createDom: createDom
921     };
922     return pub;
923 }());
924 /**
925  * @class Ext.Template
926  */
927 Ext.apply(Ext.Template.prototype, {
928     /**
929      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
930      * functions in the template. If the template does not contain
931      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
932      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
933      * <pre><code>
934 var t = new Ext.Template(
935     '&lt;div name="{id}"&gt;',
936         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
937     '&lt;/div&gt;',
938     {
939         compiled: true,      // {@link #compile} immediately
940         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
941     }
942 );
943      * </code></pre>
944      * For a list of available format functions, see {@link Ext.util.Format}.
945      */
946     disableFormats : false,
947     /**
948      * See <code>{@link #disableFormats}</code>.
949      * @type Boolean
950      * @property disableFormats
951      */
952
953     /**
954      * The regular expression used to match template variables
955      * @type RegExp
956      * @property
957      * @hide repeat doc
958      */
959     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
960     argsRe : /^\s*['"](.*)["']\s*$/,
961     compileARe : /\\/g,
962     compileBRe : /(\r\n|\n)/g,
963     compileCRe : /'/g,
964
965     /**
966      * Returns an HTML fragment of this template with the specified values applied.
967      * @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'})
968      * @return {String} The HTML fragment
969      * @hide repeat doc
970      */
971     applyTemplate : function(values){
972         var me = this,
973             useF = me.disableFormats !== true,
974             fm = Ext.util.Format,
975             tpl = me;
976
977         if(me.compiled){
978             return me.compiled(values);
979         }
980         function fn(m, name, format, args){
981             if (format && useF) {
982                 if (format.substr(0, 5) == "this.") {
983                     return tpl.call(format.substr(5), values[name], values);
984                 } else {
985                     if (args) {
986                         // quoted values are required for strings in compiled templates,
987                         // but for non compiled we need to strip them
988                         // quoted reversed for jsmin
989                         var re = me.argsRe;
990                         args = args.split(',');
991                         for(var i = 0, len = args.length; i < len; i++){
992                             args[i] = args[i].replace(re, "$1");
993                         }
994                         args = [values[name]].concat(args);
995                     } else {
996                         args = [values[name]];
997                     }
998                     return fm[format].apply(fm, args);
999                 }
1000             } else {
1001                 return values[name] !== undefined ? values[name] : "";
1002             }
1003         }
1004         return me.html.replace(me.re, fn);
1005     },
1006
1007     /**
1008      * Compiles the template into an internal function, eliminating the RegEx overhead.
1009      * @return {Ext.Template} this
1010      * @hide repeat doc
1011      */
1012     compile : function(){
1013         var me = this,
1014             fm = Ext.util.Format,
1015             useF = me.disableFormats !== true,
1016             sep = Ext.isGecko ? "+" : ",",
1017             body;
1018
1019         function fn(m, name, format, args){
1020             if(format && useF){
1021                 args = args ? ',' + args : "";
1022                 if(format.substr(0, 5) != "this."){
1023                     format = "fm." + format + '(';
1024                 }else{
1025                     format = 'this.call("'+ format.substr(5) + '", ';
1026                     args = ", values";
1027                 }
1028             }else{
1029                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
1030             }
1031             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
1032         }
1033
1034         // branched to use + in gecko and [].join() in others
1035         if(Ext.isGecko){
1036             body = "this.compiled = function(values){ return '" +
1037                    me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
1038                     "';};";
1039         }else{
1040             body = ["this.compiled = function(values){ return ['"];
1041             body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
1042             body.push("'].join('');};");
1043             body = body.join('');
1044         }
1045         eval(body);
1046         return me;
1047     },
1048
1049     // private function used to call members
1050     call : function(fnName, value, allValues){
1051         return this[fnName](value, allValues);
1052     }
1053 });
1054 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
1055 /**
1056  * @class Ext.util.Functions
1057  * @singleton
1058  */
1059 Ext.util.Functions = {
1060     /**
1061      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
1062      * the original one is not called. The resulting function returns the results of the original function.
1063      * The passed function is called with the parameters of the original function. Example usage:
1064      * <pre><code>
1065 var sayHi = function(name){
1066     alert('Hi, ' + name);
1067 }
1068
1069 sayHi('Fred'); // alerts "Hi, Fred"
1070
1071 // create a new function that validates input without
1072 // directly modifying the original function:
1073 var sayHiToFriend = Ext.createInterceptor(sayHi, function(name){
1074     return name == 'Brian';
1075 });
1076
1077 sayHiToFriend('Fred');  // no alert
1078 sayHiToFriend('Brian'); // alerts "Hi, Brian"
1079        </code></pre>
1080      * @param {Function} origFn The original function.
1081      * @param {Function} newFn The function to call before the original
1082      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
1083      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
1084      * @return {Function} The new function
1085      */
1086     createInterceptor: function(origFn, newFn, scope) { 
1087         var method = origFn;
1088         if (!Ext.isFunction(newFn)) {
1089             return origFn;
1090         }
1091         else {
1092             return function() {
1093                 var me = this,
1094                     args = arguments;
1095                 newFn.target = me;
1096                 newFn.method = origFn;
1097                 return (newFn.apply(scope || me || window, args) !== false) ?
1098                         origFn.apply(me || window, args) :
1099                         null;
1100             };
1101         }
1102     },
1103
1104     /**
1105      * Creates a delegate (callback) that sets the scope to obj.
1106      * Call directly on any function. Example: <code>Ext.createDelegate(this.myFunction, this, [arg1, arg2])</code>
1107      * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
1108      * callback points to obj. Example usage:
1109      * <pre><code>
1110 var sayHi = function(name){
1111     // Note this use of "this.text" here.  This function expects to
1112     // execute within a scope that contains a text property.  In this
1113     // example, the "this" variable is pointing to the btn object that
1114     // was passed in createDelegate below.
1115     alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
1116 }
1117
1118 var btn = new Ext.Button({
1119     text: 'Say Hi',
1120     renderTo: Ext.getBody()
1121 });
1122
1123 // This callback will execute in the scope of the
1124 // button instance. Clicking the button alerts
1125 // "Hi, Fred. You clicked the "Say Hi" button."
1126 btn.on('click', Ext.createDelegate(sayHi, btn, ['Fred']));
1127        </code></pre>
1128      * @param {Function} fn The function to delegate.
1129      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1130      * <b>If omitted, defaults to the browser window.</b>
1131      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1132      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1133      * if a number the args are inserted at the specified position
1134      * @return {Function} The new function
1135      */
1136     createDelegate: function(fn, obj, args, appendArgs) {
1137         if (!Ext.isFunction(fn)) {
1138             return fn;
1139         }
1140         return function() {
1141             var callArgs = args || arguments;
1142             if (appendArgs === true) {
1143                 callArgs = Array.prototype.slice.call(arguments, 0);
1144                 callArgs = callArgs.concat(args);
1145             }
1146             else if (Ext.isNumber(appendArgs)) {
1147                 callArgs = Array.prototype.slice.call(arguments, 0);
1148                 // copy arguments first
1149                 var applyArgs = [appendArgs, 0].concat(args);
1150                 // create method call params
1151                 Array.prototype.splice.apply(callArgs, applyArgs);
1152                 // splice them in
1153             }
1154             return fn.apply(obj || window, callArgs);
1155         };
1156     },
1157
1158     /**
1159      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
1160      * <pre><code>
1161 var sayHi = function(name){
1162     alert('Hi, ' + name);
1163 }
1164
1165 // executes immediately:
1166 sayHi('Fred');
1167
1168 // executes after 2 seconds:
1169 Ext.defer(sayHi, 2000, this, ['Fred']);
1170
1171 // this syntax is sometimes useful for deferring
1172 // execution of an anonymous function:
1173 Ext.defer(function(){
1174     alert('Anonymous');
1175 }, 100);
1176        </code></pre>
1177      * @param {Function} fn The function to defer.
1178      * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
1179      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1180      * <b>If omitted, defaults to the browser window.</b>
1181      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1182      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1183      * if a number the args are inserted at the specified position
1184      * @return {Number} The timeout id that can be used with clearTimeout
1185      */
1186     defer: function(fn, millis, obj, args, appendArgs) {
1187         fn = Ext.util.Functions.createDelegate(fn, obj, args, appendArgs);
1188         if (millis > 0) {
1189             return setTimeout(fn, millis);
1190         }
1191         fn();
1192         return 0;
1193     },
1194
1195
1196     /**
1197      * Create a combined function call sequence of the original function + the passed function.
1198      * The resulting function returns the results of the original function.
1199      * The passed fcn is called with the parameters of the original function. Example usage:
1200      * 
1201
1202 var sayHi = function(name){
1203     alert('Hi, ' + name);
1204 }
1205
1206 sayHi('Fred'); // alerts "Hi, Fred"
1207
1208 var sayGoodbye = Ext.createSequence(sayHi, function(name){
1209     alert('Bye, ' + name);
1210 });
1211
1212 sayGoodbye('Fred'); // both alerts show
1213
1214      * @param {Function} origFn The original function.
1215      * @param {Function} newFn The function to sequence
1216      * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
1217      * If omitted, defaults to the scope in which the original function is called or the browser window.
1218      * @return {Function} The new function
1219      */
1220     createSequence: function(origFn, newFn, scope) {
1221         if (!Ext.isFunction(newFn)) {
1222             return origFn;
1223         }
1224         else {
1225             return function() {
1226                 var retval = origFn.apply(this || window, arguments);
1227                 newFn.apply(scope || this || window, arguments);
1228                 return retval;
1229             };
1230         }
1231     }
1232 };
1233
1234 /**
1235  * Shorthand for {@link Ext.util.Functions#defer}   
1236  * @param {Function} fn The function to defer.
1237  * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
1238  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1239  * <b>If omitted, defaults to the browser window.</b>
1240  * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1241  * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1242  * if a number the args are inserted at the specified position
1243  * @return {Number} The timeout id that can be used with clearTimeout
1244  * @member Ext
1245  * @method defer
1246  */
1247
1248 Ext.defer = Ext.util.Functions.defer;
1249
1250 /**
1251  * Shorthand for {@link Ext.util.Functions#createInterceptor}   
1252  * @param {Function} origFn The original function.
1253  * @param {Function} newFn The function to call before the original
1254  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
1255  * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
1256  * @return {Function} The new function
1257  * @member Ext
1258  * @method defer
1259  */
1260
1261 Ext.createInterceptor = Ext.util.Functions.createInterceptor;
1262
1263 /**
1264  * Shorthand for {@link Ext.util.Functions#createSequence}
1265  * @param {Function} origFn The original function.
1266  * @param {Function} newFn The function to sequence
1267  * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
1268  * If omitted, defaults to the scope in which the original function is called or the browser window.
1269  * @return {Function} The new function
1270  * @member Ext
1271  * @method defer
1272  */
1273
1274 Ext.createSequence = Ext.util.Functions.createSequence;
1275
1276 /**
1277  * Shorthand for {@link Ext.util.Functions#createDelegate}
1278  * @param {Function} fn The function to delegate.
1279  * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1280  * <b>If omitted, defaults to the browser window.</b>
1281  * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1282  * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1283  * if a number the args are inserted at the specified position
1284  * @return {Function} The new function
1285  * @member Ext
1286  * @method defer
1287  */
1288 Ext.createDelegate = Ext.util.Functions.createDelegate;
1289 /**
1290  * @class Ext.util.Observable
1291  */
1292 Ext.apply(Ext.util.Observable.prototype, function(){
1293     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
1294     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
1295     // private
1296     function getMethodEvent(method){
1297         var e = (this.methodEvents = this.methodEvents ||
1298         {})[method], returnValue, v, cancel, obj = this;
1299
1300         if (!e) {
1301             this.methodEvents[method] = e = {};
1302             e.originalFn = this[method];
1303             e.methodName = method;
1304             e.before = [];
1305             e.after = [];
1306
1307             var makeCall = function(fn, scope, args){
1308                 if((v = fn.apply(scope || obj, args)) !== undefined){
1309                     if (typeof v == 'object') {
1310                         if(v.returnValue !== undefined){
1311                             returnValue = v.returnValue;
1312                         }else{
1313                             returnValue = v;
1314                         }
1315                         cancel = !!v.cancel;
1316                     }
1317                     else
1318                         if (v === false) {
1319                             cancel = true;
1320                         }
1321                         else {
1322                             returnValue = v;
1323                         }
1324                 }
1325             };
1326
1327             this[method] = function(){
1328                 var args = Array.prototype.slice.call(arguments, 0),
1329                     b;
1330                 returnValue = v = undefined;
1331                 cancel = false;
1332
1333                 for(var i = 0, len = e.before.length; i < len; i++){
1334                     b = e.before[i];
1335                     makeCall(b.fn, b.scope, args);
1336                     if (cancel) {
1337                         return returnValue;
1338                     }
1339                 }
1340
1341                 if((v = e.originalFn.apply(obj, args)) !== undefined){
1342                     returnValue = v;
1343                 }
1344
1345                 for(var i = 0, len = e.after.length; i < len; i++){
1346                     b = e.after[i];
1347                     makeCall(b.fn, b.scope, args);
1348                     if (cancel) {
1349                         return returnValue;
1350                     }
1351                 }
1352                 return returnValue;
1353             };
1354         }
1355         return e;
1356     }
1357
1358     return {
1359         // these are considered experimental
1360         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
1361         // adds an 'interceptor' called before the original method
1362         beforeMethod : function(method, fn, scope){
1363             getMethodEvent.call(this, method).before.push({
1364                 fn: fn,
1365                 scope: scope
1366             });
1367         },
1368
1369         // adds a 'sequence' called after the original method
1370         afterMethod : function(method, fn, scope){
1371             getMethodEvent.call(this, method).after.push({
1372                 fn: fn,
1373                 scope: scope
1374             });
1375         },
1376
1377         removeMethodListener: function(method, fn, scope){
1378             var e = this.getMethodEvent(method);
1379             for(var i = 0, len = e.before.length; i < len; i++){
1380                 if(e.before[i].fn == fn && e.before[i].scope == scope){
1381                     e.before.splice(i, 1);
1382                     return;
1383                 }
1384             }
1385             for(var i = 0, len = e.after.length; i < len; i++){
1386                 if(e.after[i].fn == fn && e.after[i].scope == scope){
1387                     e.after.splice(i, 1);
1388                     return;
1389                 }
1390             }
1391         },
1392
1393         /**
1394          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
1395          * @param {Object} o The Observable whose events this object is to relay.
1396          * @param {Array} events Array of event names to relay.
1397          */
1398         relayEvents : function(o, events){
1399             var me = this;
1400             function createHandler(ename){
1401                 return function(){
1402                     return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
1403                 };
1404             }
1405             for(var i = 0, len = events.length; i < len; i++){
1406                 var ename = events[i];
1407                 me.events[ename] = me.events[ename] || true;
1408                 o.on(ename, createHandler(ename), me);
1409             }
1410         },
1411
1412         /**
1413          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
1414          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
1415          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
1416          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
1417          * access the required target more quickly.</p>
1418          * <p>Example:</p><pre><code>
1419 Ext.override(Ext.form.Field, {
1420     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
1421     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
1422         this.enableBubble('change');
1423     }),
1424
1425     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
1426     getBubbleTarget : function() {
1427         if (!this.formPanel) {
1428             this.formPanel = this.findParentByType('form');
1429         }
1430         return this.formPanel;
1431     }
1432 });
1433
1434 var myForm = new Ext.formPanel({
1435     title: 'User Details',
1436     items: [{
1437         ...
1438     }],
1439     listeners: {
1440         change: function() {
1441             // Title goes red if form has been modified.
1442             myForm.header.setStyle('color', 'red');
1443         }
1444     }
1445 });
1446 </code></pre>
1447          * @param {String/Array} events The event name to bubble, or an Array of event names.
1448          */
1449         enableBubble : function(events){
1450             var me = this;
1451             if(!Ext.isEmpty(events)){
1452                 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
1453                 for(var i = 0, len = events.length; i < len; i++){
1454                     var ename = events[i];
1455                     ename = ename.toLowerCase();
1456                     var ce = me.events[ename] || true;
1457                     if (typeof ce == 'boolean') {
1458                         ce = new Ext.util.Event(me, ename);
1459                         me.events[ename] = ce;
1460                     }
1461                     ce.bubble = true;
1462                 }
1463             }
1464         }
1465     };
1466 }());
1467
1468
1469 /**
1470  * Starts capture on the specified Observable. All events will be passed
1471  * to the supplied function with the event name + standard signature of the event
1472  * <b>before</b> the event is fired. If the supplied function returns false,
1473  * the event will not fire.
1474  * @param {Observable} o The Observable to capture events from.
1475  * @param {Function} fn The function to call when an event is fired.
1476  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
1477  * @static
1478  */
1479 Ext.util.Observable.capture = function(o, fn, scope){
1480     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
1481 };
1482
1483
1484 /**
1485  * Sets observability on the passed class constructor.<p>
1486  * <p>This makes any event fired on any instance of the passed class also fire a single event through
1487  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
1488  * <p>Usage:</p><pre><code>
1489 Ext.util.Observable.observeClass(Ext.data.Connection);
1490 Ext.data.Connection.on('beforerequest', function(con, options) {
1491     console.log('Ajax request made to ' + options.url);
1492 });</code></pre>
1493  * @param {Function} c The class constructor to make observable.
1494  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
1495  * @static
1496  */
1497 Ext.util.Observable.observeClass = function(c, listeners){
1498     if(c){
1499       if(!c.fireEvent){
1500           Ext.apply(c, new Ext.util.Observable());
1501           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
1502       }
1503       if(typeof listeners == 'object'){
1504           c.on(listeners);
1505       }
1506       return c;
1507    }
1508 };
1509 /**
1510 * @class Ext.EventManager
1511 */
1512 Ext.apply(Ext.EventManager, function(){
1513    var resizeEvent,
1514        resizeTask,
1515        textEvent,
1516        textSize,
1517        D = Ext.lib.Dom,
1518        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
1519        curWidth = 0,
1520        curHeight = 0,
1521        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
1522        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
1523        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
1524        useKeydown = Ext.isWebKit ?
1525                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
1526                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
1527
1528    return {
1529        // private
1530        doResizeEvent: function(){
1531            var h = D.getViewHeight(),
1532                w = D.getViewWidth();
1533
1534             //whacky problem in IE where the resize event will fire even though the w/h are the same.
1535             if(curHeight != h || curWidth != w){
1536                resizeEvent.fire(curWidth = w, curHeight = h);
1537             }
1538        },
1539
1540        /**
1541         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
1542         * passes new viewport width and height to handlers.
1543         * @param {Function} fn      The handler function the window resize event invokes.
1544         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
1545         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
1546         */
1547        onWindowResize : function(fn, scope, options){
1548            if(!resizeEvent){
1549                resizeEvent = new Ext.util.Event();
1550                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
1551                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
1552            }
1553            resizeEvent.addListener(fn, scope, options);
1554        },
1555
1556        // exposed only to allow manual firing
1557        fireWindowResize : function(){
1558            if(resizeEvent){
1559                resizeTask.delay(100);
1560            }
1561        },
1562
1563        /**
1564         * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
1565         * @param {Function} fn      The function the event invokes.
1566         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
1567         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
1568         */
1569        onTextResize : function(fn, scope, options){
1570            if(!textEvent){
1571                textEvent = new Ext.util.Event();
1572                var textEl = new Ext.Element(document.createElement('div'));
1573                textEl.dom.className = 'x-text-resize';
1574                textEl.dom.innerHTML = 'X';
1575                textEl.appendTo(document.body);
1576                textSize = textEl.dom.offsetHeight;
1577                setInterval(function(){
1578                    if(textEl.dom.offsetHeight != textSize){
1579                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
1580                    }
1581                }, this.textResizeInterval);
1582            }
1583            textEvent.addListener(fn, scope, options);
1584        },
1585
1586        /**
1587         * Removes the passed window resize listener.
1588         * @param {Function} fn        The method the event invokes
1589         * @param {Object}   scope    The scope of handler
1590         */
1591        removeResizeListener : function(fn, scope){
1592            if(resizeEvent){
1593                resizeEvent.removeListener(fn, scope);
1594            }
1595        },
1596
1597        // private
1598        fireResize : function(){
1599            if(resizeEvent){
1600                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
1601            }
1602        },
1603
1604         /**
1605         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
1606         */
1607        textResizeInterval : 50,
1608
1609        /**
1610         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
1611         */
1612        ieDeferSrc : false,
1613        
1614        // protected, short accessor for useKeydown
1615        getKeyEvent : function(){
1616            return useKeydown ? 'keydown' : 'keypress';
1617        },
1618
1619        // protected for use inside the framework
1620        // detects whether we should use keydown or keypress based on the browser.
1621        useKeydown: useKeydown
1622    };
1623 }());
1624
1625 Ext.EventManager.on = Ext.EventManager.addListener;
1626
1627
1628 Ext.apply(Ext.EventObjectImpl.prototype, {
1629    /** Key constant @type Number */
1630    BACKSPACE: 8,
1631    /** Key constant @type Number */
1632    TAB: 9,
1633    /** Key constant @type Number */
1634    NUM_CENTER: 12,
1635    /** Key constant @type Number */
1636    ENTER: 13,
1637    /** Key constant @type Number */
1638    RETURN: 13,
1639    /** Key constant @type Number */
1640    SHIFT: 16,
1641    /** Key constant @type Number */
1642    CTRL: 17,
1643    CONTROL : 17, // legacy
1644    /** Key constant @type Number */
1645    ALT: 18,
1646    /** Key constant @type Number */
1647    PAUSE: 19,
1648    /** Key constant @type Number */
1649    CAPS_LOCK: 20,
1650    /** Key constant @type Number */
1651    ESC: 27,
1652    /** Key constant @type Number */
1653    SPACE: 32,
1654    /** Key constant @type Number */
1655    PAGE_UP: 33,
1656    PAGEUP : 33, // legacy
1657    /** Key constant @type Number */
1658    PAGE_DOWN: 34,
1659    PAGEDOWN : 34, // legacy
1660    /** Key constant @type Number */
1661    END: 35,
1662    /** Key constant @type Number */
1663    HOME: 36,
1664    /** Key constant @type Number */
1665    LEFT: 37,
1666    /** Key constant @type Number */
1667    UP: 38,
1668    /** Key constant @type Number */
1669    RIGHT: 39,
1670    /** Key constant @type Number */
1671    DOWN: 40,
1672    /** Key constant @type Number */
1673    PRINT_SCREEN: 44,
1674    /** Key constant @type Number */
1675    INSERT: 45,
1676    /** Key constant @type Number */
1677    DELETE: 46,
1678    /** Key constant @type Number */
1679    ZERO: 48,
1680    /** Key constant @type Number */
1681    ONE: 49,
1682    /** Key constant @type Number */
1683    TWO: 50,
1684    /** Key constant @type Number */
1685    THREE: 51,
1686    /** Key constant @type Number */
1687    FOUR: 52,
1688    /** Key constant @type Number */
1689    FIVE: 53,
1690    /** Key constant @type Number */
1691    SIX: 54,
1692    /** Key constant @type Number */
1693    SEVEN: 55,
1694    /** Key constant @type Number */
1695    EIGHT: 56,
1696    /** Key constant @type Number */
1697    NINE: 57,
1698    /** Key constant @type Number */
1699    A: 65,
1700    /** Key constant @type Number */
1701    B: 66,
1702    /** Key constant @type Number */
1703    C: 67,
1704    /** Key constant @type Number */
1705    D: 68,
1706    /** Key constant @type Number */
1707    E: 69,
1708    /** Key constant @type Number */
1709    F: 70,
1710    /** Key constant @type Number */
1711    G: 71,
1712    /** Key constant @type Number */
1713    H: 72,
1714    /** Key constant @type Number */
1715    I: 73,
1716    /** Key constant @type Number */
1717    J: 74,
1718    /** Key constant @type Number */
1719    K: 75,
1720    /** Key constant @type Number */
1721    L: 76,
1722    /** Key constant @type Number */
1723    M: 77,
1724    /** Key constant @type Number */
1725    N: 78,
1726    /** Key constant @type Number */
1727    O: 79,
1728    /** Key constant @type Number */
1729    P: 80,
1730    /** Key constant @type Number */
1731    Q: 81,
1732    /** Key constant @type Number */
1733    R: 82,
1734    /** Key constant @type Number */
1735    S: 83,
1736    /** Key constant @type Number */
1737    T: 84,
1738    /** Key constant @type Number */
1739    U: 85,
1740    /** Key constant @type Number */
1741    V: 86,
1742    /** Key constant @type Number */
1743    W: 87,
1744    /** Key constant @type Number */
1745    X: 88,
1746    /** Key constant @type Number */
1747    Y: 89,
1748    /** Key constant @type Number */
1749    Z: 90,
1750    /** Key constant @type Number */
1751    CONTEXT_MENU: 93,
1752    /** Key constant @type Number */
1753    NUM_ZERO: 96,
1754    /** Key constant @type Number */
1755    NUM_ONE: 97,
1756    /** Key constant @type Number */
1757    NUM_TWO: 98,
1758    /** Key constant @type Number */
1759    NUM_THREE: 99,
1760    /** Key constant @type Number */
1761    NUM_FOUR: 100,
1762    /** Key constant @type Number */
1763    NUM_FIVE: 101,
1764    /** Key constant @type Number */
1765    NUM_SIX: 102,
1766    /** Key constant @type Number */
1767    NUM_SEVEN: 103,
1768    /** Key constant @type Number */
1769    NUM_EIGHT: 104,
1770    /** Key constant @type Number */
1771    NUM_NINE: 105,
1772    /** Key constant @type Number */
1773    NUM_MULTIPLY: 106,
1774    /** Key constant @type Number */
1775    NUM_PLUS: 107,
1776    /** Key constant @type Number */
1777    NUM_MINUS: 109,
1778    /** Key constant @type Number */
1779    NUM_PERIOD: 110,
1780    /** Key constant @type Number */
1781    NUM_DIVISION: 111,
1782    /** Key constant @type Number */
1783    F1: 112,
1784    /** Key constant @type Number */
1785    F2: 113,
1786    /** Key constant @type Number */
1787    F3: 114,
1788    /** Key constant @type Number */
1789    F4: 115,
1790    /** Key constant @type Number */
1791    F5: 116,
1792    /** Key constant @type Number */
1793    F6: 117,
1794    /** Key constant @type Number */
1795    F7: 118,
1796    /** Key constant @type Number */
1797    F8: 119,
1798    /** Key constant @type Number */
1799    F9: 120,
1800    /** Key constant @type Number */
1801    F10: 121,
1802    /** Key constant @type Number */
1803    F11: 122,
1804    /** Key constant @type Number */
1805    F12: 123,
1806
1807    /** @private */
1808    isNavKeyPress : function(){
1809        var me = this,
1810            k = this.normalizeKey(me.keyCode);
1811        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
1812        k == me.RETURN ||
1813        k == me.TAB ||
1814        k == me.ESC;
1815    },
1816
1817    isSpecialKey : function(){
1818        var k = this.normalizeKey(this.keyCode);
1819        return (this.type == 'keypress' && this.ctrlKey) ||
1820        this.isNavKeyPress() ||
1821        (k == this.BACKSPACE) || // Backspace
1822        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
1823        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
1824    },
1825
1826    getPoint : function(){
1827        return new Ext.lib.Point(this.xy[0], this.xy[1]);
1828    },
1829
1830    /**
1831     * Returns true if the control, meta, shift or alt key was pressed during this event.
1832     * @return {Boolean}
1833     */
1834    hasModifier : function(){
1835        return ((this.ctrlKey || this.altKey) || this.shiftKey);
1836    }
1837 });/**
1838  * @class Ext.Element
1839  */
1840 Ext.Element.addMethods({
1841     /**
1842      * Stops the specified event(s) from bubbling and optionally prevents the default action
1843      * @param {String/Array} eventName an event / array of events to stop from bubbling
1844      * @param {Boolean} preventDefault (optional) true to prevent the default action too
1845      * @return {Ext.Element} this
1846      */
1847     swallowEvent : function(eventName, preventDefault) {
1848         var me = this;
1849         function fn(e) {
1850             e.stopPropagation();
1851             if (preventDefault) {
1852                 e.preventDefault();
1853             }
1854         }
1855         
1856         if (Ext.isArray(eventName)) {
1857             Ext.each(eventName, function(e) {
1858                  me.on(e, fn);
1859             });
1860             return me;
1861         }
1862         me.on(eventName, fn);
1863         return me;
1864     },
1865
1866     /**
1867      * Create an event handler on this element such that when the event fires and is handled by this element,
1868      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
1869      * @param {String} eventName The type of event to relay
1870      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
1871      * for firing the relayed event
1872      */
1873     relayEvent : function(eventName, observable) {
1874         this.on(eventName, function(e) {
1875             observable.fireEvent(eventName, e);
1876         });
1877     },
1878
1879     /**
1880      * Removes worthless text nodes
1881      * @param {Boolean} forceReclean (optional) By default the element
1882      * keeps track if it has been cleaned already so
1883      * you can call this over and over. However, if you update the element and
1884      * need to force a reclean, you can pass true.
1885      */
1886     clean : function(forceReclean) {
1887         var me  = this,
1888             dom = me.dom,
1889             n   = dom.firstChild,
1890             ni  = -1;
1891
1892         if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) {
1893             return me;
1894         }
1895
1896         while (n) {
1897             var nx = n.nextSibling;
1898             if (n.nodeType == 3 && !(/\S/.test(n.nodeValue))) {
1899                 dom.removeChild(n);
1900             } else {
1901                 n.nodeIndex = ++ni;
1902             }
1903             n = nx;
1904         }
1905         
1906         Ext.Element.data(dom, 'isCleaned', true);
1907         return me;
1908     },
1909
1910     /**
1911      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
1912      * parameter as {@link Ext.Updater#update}
1913      * @return {Ext.Element} this
1914      */
1915     load : function() {
1916         var updateManager = this.getUpdater();
1917         updateManager.update.apply(updateManager, arguments);
1918         
1919         return this;
1920     },
1921
1922     /**
1923     * Gets this element's {@link Ext.Updater Updater}
1924     * @return {Ext.Updater} The Updater
1925     */
1926     getUpdater : function() {
1927         return this.updateManager || (this.updateManager = new Ext.Updater(this));
1928     },
1929
1930     /**
1931     * Update the innerHTML of this element, optionally searching for and processing scripts
1932     * @param {String} html The new HTML
1933     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
1934     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
1935     * @return {Ext.Element} this
1936      */
1937     update : function(html, loadScripts, callback) {
1938         if (!this.dom) {
1939             return this;
1940         }
1941         html = html || "";
1942
1943         if (loadScripts !== true) {
1944             this.dom.innerHTML = html;
1945             if (typeof callback == 'function') {
1946                 callback();
1947             }
1948             return this;
1949         }
1950
1951         var id  = Ext.id(),
1952             dom = this.dom;
1953
1954         html += '<span id="' + id + '"></span>';
1955
1956         Ext.lib.Event.onAvailable(id, function() {
1957             var DOC    = document,
1958                 hd     = DOC.getElementsByTagName("head")[0],
1959                 re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
1960                 srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
1961                 typeRe = /\stype=([\'\"])(.*?)\1/i,
1962                 match,
1963                 attrs,
1964                 srcMatch,
1965                 typeMatch,
1966                 el,
1967                 s;
1968
1969             while ((match = re.exec(html))) {
1970                 attrs = match[1];
1971                 srcMatch = attrs ? attrs.match(srcRe) : false;
1972                 if (srcMatch && srcMatch[2]) {
1973                    s = DOC.createElement("script");
1974                    s.src = srcMatch[2];
1975                    typeMatch = attrs.match(typeRe);
1976                    if (typeMatch && typeMatch[2]) {
1977                        s.type = typeMatch[2];
1978                    }
1979                    hd.appendChild(s);
1980                 } else if (match[2] && match[2].length > 0) {
1981                     if (window.execScript) {
1982                        window.execScript(match[2]);
1983                     } else {
1984                        window.eval(match[2]);
1985                     }
1986                 }
1987             }
1988             
1989             el = DOC.getElementById(id);
1990             if (el) {
1991                 Ext.removeNode(el);
1992             }
1993             
1994             if (typeof callback == 'function') {
1995                 callback();
1996             }
1997         });
1998         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
1999         return this;
2000     },
2001
2002     // inherit docs, overridden so we can add removeAnchor
2003     removeAllListeners : function() {
2004         this.removeAnchor();
2005         Ext.EventManager.removeAll(this.dom);
2006         return this;
2007     },
2008
2009     /**
2010      * Creates a proxy element of this element
2011      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
2012      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
2013      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
2014      * @return {Ext.Element} The new proxy element
2015      */
2016     createProxy : function(config, renderTo, matchBox) {
2017         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
2018
2019         var me = this,
2020             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
2021                                Ext.DomHelper.insertBefore(me.dom, config, true);
2022
2023         if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
2024            proxy.setBox(me.getBox());
2025         }
2026         return proxy;
2027     }
2028 });
2029
2030 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
2031 /**
2032  * @class Ext.Element
2033  */
2034 Ext.Element.addMethods({
2035     /**
2036      * Gets the x,y coordinates specified by the anchor position on the element.
2037      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
2038      * for details on supported anchor positions.
2039      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
2040      * of page coordinates
2041      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
2042      * {width: (target width), height: (target height)} (defaults to the element's current size)
2043      * @return {Array} [x, y] An array containing the element's x and y coordinates
2044      */
2045     getAnchorXY : function(anchor, local, s){
2046         //Passing a different size is useful for pre-calculating anchors,
2047         //especially for anchored animations that change the el size.
2048                 anchor = (anchor || "tl").toLowerCase();
2049         s = s || {};
2050         
2051         var me = this,        
2052                 vp = me.dom == document.body || me.dom == document,
2053                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
2054                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
2055                 xy,             
2056                 r = Math.round,
2057                 o = me.getXY(),
2058                 scroll = me.getScroll(),
2059                 extraX = vp ? scroll.left : !local ? o[0] : 0,
2060                 extraY = vp ? scroll.top : !local ? o[1] : 0,
2061                 hash = {
2062                         c  : [r(w * 0.5), r(h * 0.5)],
2063                         t  : [r(w * 0.5), 0],
2064                         l  : [0, r(h * 0.5)],
2065                         r  : [w, r(h * 0.5)],
2066                         b  : [r(w * 0.5), h],
2067                         tl : [0, 0],    
2068                         bl : [0, h],
2069                         br : [w, h],
2070                         tr : [w, 0]
2071                 };
2072         
2073         xy = hash[anchor];      
2074         return [xy[0] + extraX, xy[1] + extraY]; 
2075     },
2076
2077     /**
2078      * Anchors an element to another element and realigns it when the window is resized.
2079      * @param {Mixed} element The element to align to.
2080      * @param {String} position The position to align to.
2081      * @param {Array} offsets (optional) Offset the positioning by [x, y]
2082      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
2083      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
2084      * is a number, it is used as the buffer delay (defaults to 50ms).
2085      * @param {Function} callback The function to call after the animation finishes
2086      * @return {Ext.Element} this
2087      */
2088     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
2089             var me = this,
2090             dom = me.dom,
2091             scroll = !Ext.isEmpty(monitorScroll),
2092             action = function(){
2093                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
2094                 Ext.callback(callback, Ext.fly(dom));
2095             },
2096             anchor = this.getAnchor();
2097             
2098         // previous listener anchor, remove it
2099         this.removeAnchor();
2100         Ext.apply(anchor, {
2101             fn: action,
2102             scroll: scroll
2103         });
2104
2105         Ext.EventManager.onWindowResize(action, null);
2106         
2107         if(scroll){
2108             Ext.EventManager.on(window, 'scroll', action, null,
2109                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
2110         }
2111         action.call(me); // align immediately
2112         return me;
2113     },
2114     
2115     /**
2116      * Remove any anchor to this element. See {@link #anchorTo}.
2117      * @return {Ext.Element} this
2118      */
2119     removeAnchor : function(){
2120         var me = this,
2121             anchor = this.getAnchor();
2122             
2123         if(anchor && anchor.fn){
2124             Ext.EventManager.removeResizeListener(anchor.fn);
2125             if(anchor.scroll){
2126                 Ext.EventManager.un(window, 'scroll', anchor.fn);
2127             }
2128             delete anchor.fn;
2129         }
2130         return me;
2131     },
2132     
2133     // private
2134     getAnchor : function(){
2135         var data = Ext.Element.data,
2136             dom = this.dom;
2137             if (!dom) {
2138                 return;
2139             }
2140             var anchor = data(dom, '_anchor');
2141             
2142         if(!anchor){
2143             anchor = data(dom, '_anchor', {});
2144         }
2145         return anchor;
2146     },
2147
2148     /**
2149      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
2150      * supported position values.
2151      * @param {Mixed} element The element to align to.
2152      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
2153      * @param {Array} offsets (optional) Offset the positioning by [x, y]
2154      * @return {Array} [x, y]
2155      */
2156     getAlignToXY : function(el, p, o){      
2157         el = Ext.get(el);
2158         
2159         if(!el || !el.dom){
2160             throw "Element.alignToXY with an element that doesn't exist";
2161         }
2162         
2163         o = o || [0,0];
2164         p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
2165                 
2166         var me = this,
2167                 d = me.dom,
2168                 a1,
2169                 a2,
2170                 x,
2171                 y,
2172                 //constrain the aligned el to viewport if necessary
2173                 w,
2174                 h,
2175                 r,
2176                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
2177                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
2178                 p1y,
2179                 p1x,            
2180                 p2y,
2181                 p2x,
2182                 swapY,
2183                 swapX,
2184                 doc = document,
2185                 docElement = doc.documentElement,
2186                 docBody = doc.body,
2187                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
2188                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
2189                 c = false, //constrain to viewport
2190                 p1 = "", 
2191                 p2 = "",
2192                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
2193         
2194         if(!m){
2195            throw "Element.alignTo with an invalid alignment " + p;
2196         }
2197         
2198         p1 = m[1]; 
2199         p2 = m[2]; 
2200         c = !!m[3];
2201
2202         //Subtract the aligned el's internal xy from the target's offset xy
2203         //plus custom offset to get the aligned el's new offset xy
2204         a1 = me.getAnchorXY(p1, true);
2205         a2 = el.getAnchorXY(p2, false);
2206
2207         x = a2[0] - a1[0] + o[0];
2208         y = a2[1] - a1[1] + o[1];
2209
2210         if(c){    
2211                w = me.getWidth();
2212            h = me.getHeight();
2213            r = el.getRegion();       
2214            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
2215            //perpendicular to the vp border, allow the aligned el to slide on that border,
2216            //otherwise swap the aligned el to the opposite border of the target.
2217            p1y = p1.charAt(0);
2218            p1x = p1.charAt(p1.length-1);
2219            p2y = p2.charAt(0);
2220            p2x = p2.charAt(p2.length-1);
2221            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
2222            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
2223            
2224
2225            if (x + w > dw + scrollX) {
2226                 x = swapX ? r.left-w : dw+scrollX-w;
2227            }
2228            if (x < scrollX) {
2229                x = swapX ? r.right : scrollX;
2230            }
2231            if (y + h > dh + scrollY) {
2232                 y = swapY ? r.top-h : dh+scrollY-h;
2233             }
2234            if (y < scrollY){
2235                y = swapY ? r.bottom : scrollY;
2236            }
2237         }
2238         return [x,y];
2239     },
2240
2241     /**
2242      * Aligns this element with another element relative to the specified anchor points. If the other element is the
2243      * document it aligns it to the viewport.
2244      * The position parameter is optional, and can be specified in any one of the following formats:
2245      * <ul>
2246      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
2247      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
2248      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
2249      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
2250      *   <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
2251      *       element's anchor point, and the second value is used as the target's anchor point.</li>
2252      * </ul>
2253      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
2254      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
2255      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
2256      * that specified in order to enforce the viewport constraints.
2257      * Following are all of the supported anchor positions:
2258 <pre>
2259 Value  Description
2260 -----  -----------------------------
2261 tl     The top left corner (default)
2262 t      The center of the top edge
2263 tr     The top right corner
2264 l      The center of the left edge
2265 c      In the center of the element
2266 r      The center of the right edge
2267 bl     The bottom left corner
2268 b      The center of the bottom edge
2269 br     The bottom right corner
2270 </pre>
2271 Example Usage:
2272 <pre><code>
2273 // align el to other-el using the default positioning ("tl-bl", non-constrained)
2274 el.alignTo("other-el");
2275
2276 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
2277 el.alignTo("other-el", "tr?");
2278
2279 // align the bottom right corner of el with the center left edge of other-el
2280 el.alignTo("other-el", "br-l?");
2281
2282 // align the center of el with the bottom left corner of other-el and
2283 // adjust the x position by -6 pixels (and the y position by 0)
2284 el.alignTo("other-el", "c-bl", [-6, 0]);
2285 </code></pre>
2286      * @param {Mixed} element The element to align to.
2287      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
2288      * @param {Array} offsets (optional) Offset the positioning by [x, y]
2289      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2290      * @return {Ext.Element} this
2291      */
2292     alignTo : function(element, position, offsets, animate){
2293             var me = this;
2294         return me.setXY(me.getAlignToXY(element, position, offsets),
2295                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
2296     },
2297     
2298     // private ==>  used outside of core
2299     adjustForConstraints : function(xy, parent, offsets){
2300         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
2301     },
2302
2303     // private ==>  used outside of core
2304     getConstrainToXY : function(el, local, offsets, proposedXY){   
2305             var os = {top:0, left:0, bottom:0, right: 0};
2306
2307         return function(el, local, offsets, proposedXY){
2308             el = Ext.get(el);
2309             offsets = offsets ? Ext.applyIf(offsets, os) : os;
2310
2311             var vw, vh, vx = 0, vy = 0;
2312             if(el.dom == document.body || el.dom == document){
2313                 vw =Ext.lib.Dom.getViewWidth();
2314                 vh = Ext.lib.Dom.getViewHeight();
2315             }else{
2316                 vw = el.dom.clientWidth;
2317                 vh = el.dom.clientHeight;
2318                 if(!local){
2319                     var vxy = el.getXY();
2320                     vx = vxy[0];
2321                     vy = vxy[1];
2322                 }
2323             }
2324
2325             var s = el.getScroll();
2326
2327             vx += offsets.left + s.left;
2328             vy += offsets.top + s.top;
2329
2330             vw -= offsets.right;
2331             vh -= offsets.bottom;
2332
2333             var vr = vx + vw,
2334                 vb = vy + vh,
2335                 xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]),
2336                 x = xy[0], y = xy[1],
2337                 offset = this.getConstrainOffset(),
2338                 w = this.dom.offsetWidth + offset, 
2339                 h = this.dom.offsetHeight + offset;
2340
2341             // only move it if it needs it
2342             var moved = false;
2343
2344             // first validate right/bottom
2345             if((x + w) > vr){
2346                 x = vr - w;
2347                 moved = true;
2348             }
2349             if((y + h) > vb){
2350                 y = vb - h;
2351                 moved = true;
2352             }
2353             // then make sure top/left isn't negative
2354             if(x < vx){
2355                 x = vx;
2356                 moved = true;
2357             }
2358             if(y < vy){
2359                 y = vy;
2360                 moved = true;
2361             }
2362             return moved ? [x, y] : false;
2363         };
2364     }(),
2365             
2366             
2367                 
2368 //         el = Ext.get(el);
2369 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
2370
2371 //         var  me = this,
2372 //              doc = document,
2373 //              s = el.getScroll(),
2374 //              vxy = el.getXY(),
2375 //              vx = offsets.left + s.left, 
2376 //              vy = offsets.top + s.top,               
2377 //              vw = -offsets.right, 
2378 //              vh = -offsets.bottom, 
2379 //              vr,
2380 //              vb,
2381 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
2382 //              x = xy[0],
2383 //              y = xy[1],
2384 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
2385 //              moved = false; // only move it if it needs it
2386 //       
2387 //              
2388 //         if(el.dom == doc.body || el.dom == doc){
2389 //             vw += Ext.lib.Dom.getViewWidth();
2390 //             vh += Ext.lib.Dom.getViewHeight();
2391 //         }else{
2392 //             vw += el.dom.clientWidth;
2393 //             vh += el.dom.clientHeight;
2394 //             if(!local){                    
2395 //                 vx += vxy[0];
2396 //                 vy += vxy[1];
2397 //             }
2398 //         }
2399
2400 //         // first validate right/bottom
2401 //         if(x + w > vx + vw){
2402 //             x = vx + vw - w;
2403 //             moved = true;
2404 //         }
2405 //         if(y + h > vy + vh){
2406 //             y = vy + vh - h;
2407 //             moved = true;
2408 //         }
2409 //         // then make sure top/left isn't negative
2410 //         if(x < vx){
2411 //             x = vx;
2412 //             moved = true;
2413 //         }
2414 //         if(y < vy){
2415 //             y = vy;
2416 //             moved = true;
2417 //         }
2418 //         return moved ? [x, y] : false;
2419 //    },
2420
2421     // private, used internally
2422     getConstrainOffset : function(){
2423         return 0;
2424     },
2425     
2426     /**
2427     * Calculates the x, y to center this element on the screen
2428     * @return {Array} The x, y values [x, y]
2429     */
2430     getCenterXY : function(){
2431         return this.getAlignToXY(document, 'c-c');
2432     },
2433
2434     /**
2435     * Centers the Element in either the viewport, or another Element.
2436     * @param {Mixed} centerIn (optional) The element in which to center the element.
2437     */
2438     center : function(centerIn){
2439         return this.alignTo(centerIn || document, 'c-c');        
2440     }    
2441 });
2442 /**
2443  * @class Ext.Element
2444  */
2445 Ext.Element.addMethods({
2446     /**
2447      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2448      * @param {String} selector The CSS selector
2449      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
2450      * @return {CompositeElement/CompositeElementLite} The composite element
2451      */
2452     select : function(selector, unique){
2453         return Ext.Element.select(selector, unique, this.dom);
2454     }
2455 });/**
2456  * @class Ext.Element
2457  */
2458 Ext.apply(Ext.Element.prototype, function() {
2459         var GETDOM = Ext.getDom,
2460                 GET = Ext.get,
2461                 DH = Ext.DomHelper;
2462         
2463         return {        
2464                 /**
2465              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2466              * @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.
2467              * @param {String} where (optional) 'before' or 'after' defaults to before
2468              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
2469              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2470              */
2471             insertSibling: function(el, where, returnDom){
2472                 var me = this,
2473                         rt,
2474                 isAfter = (where || 'before').toLowerCase() == 'after',
2475                 insertEl;
2476                         
2477                 if(Ext.isArray(el)){
2478                 insertEl = me;
2479                     Ext.each(el, function(e) {
2480                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2481                     if(isAfter){
2482                         insertEl = rt;
2483                     }
2484                     });
2485                     return rt;
2486                 }
2487                         
2488                 el = el || {};
2489                 
2490             if(el.nodeType || el.dom){
2491                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
2492                 if (!returnDom) {
2493                     rt = GET(rt);
2494                 }
2495             }else{
2496                 if (isAfter && !me.dom.nextSibling) {
2497                     rt = DH.append(me.dom.parentNode, el, !returnDom);
2498                 } else {                    
2499                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2500                 }
2501             }
2502                 return rt;
2503             }
2504     };
2505 }());/**
2506  * @class Ext.Element
2507  */
2508
2509 // special markup used throughout Ext when box wrapping elements
2510 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>';
2511
2512 Ext.Element.addMethods(function(){
2513     var INTERNAL = "_internal",
2514         pxMatch = /(\d+\.?\d+)px/;
2515     return {
2516         /**
2517          * More flexible version of {@link #setStyle} for setting style properties.
2518          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
2519          * a function which returns such a specification.
2520          * @return {Ext.Element} this
2521          */
2522         applyStyles : function(style){
2523             Ext.DomHelper.applyStyles(this.dom, style);
2524             return this;
2525         },
2526
2527         /**
2528          * Returns an object with properties matching the styles requested.
2529          * For example, el.getStyles('color', 'font-size', 'width') might return
2530          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
2531          * @param {String} style1 A style name
2532          * @param {String} style2 A style name
2533          * @param {String} etc.
2534          * @return {Object} The style object
2535          */
2536         getStyles : function(){
2537             var ret = {};
2538             Ext.each(arguments, function(v) {
2539                ret[v] = this.getStyle(v);
2540             },
2541             this);
2542             return ret;
2543         },
2544
2545         // private  ==> used by ext full
2546         setOverflow : function(v){
2547             var dom = this.dom;
2548             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
2549                 dom.style.overflow = 'hidden';
2550                 (function(){dom.style.overflow = 'auto';}).defer(1);
2551             }else{
2552                 dom.style.overflow = v;
2553             }
2554         },
2555
2556        /**
2557         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
2558         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
2559         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
2560         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
2561         * is of this form:</p>
2562         * <pre><code>
2563     Ext.Element.boxMarkup =
2564     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
2565      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
2566      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
2567         * </code></pre>
2568         * <p>Example usage:</p>
2569         * <pre><code>
2570     // Basic box wrap
2571     Ext.get("foo").boxWrap();
2572
2573     // You can also add a custom class and use CSS inheritance rules to customize the box look.
2574     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
2575     // for how to create a custom box wrap style.
2576     Ext.get("foo").boxWrap().addClass("x-box-blue");
2577         * </code></pre>
2578         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
2579         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
2580         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
2581         * also supply all of the necessary rules.
2582         * @return {Ext.Element} The outermost wrapping element of the created box structure.
2583         */
2584         boxWrap : function(cls){
2585             cls = cls || 'x-box';
2586             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)));
2587             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
2588             return el;
2589         },
2590
2591         /**
2592          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
2593          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
2594          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
2595          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
2596          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
2597          * </ul></div>
2598          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
2599          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
2600          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
2601          * </ul></div>
2602          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2603          * @return {Ext.Element} this
2604          */
2605         setSize : function(width, height, animate){
2606             var me = this;
2607             if(typeof width == 'object'){ // in case of object from getSize()
2608                 height = width.height;
2609                 width = width.width;
2610             }
2611             width = me.adjustWidth(width);
2612             height = me.adjustHeight(height);
2613             if(!animate || !me.anim){
2614                 me.dom.style.width = me.addUnits(width);
2615                 me.dom.style.height = me.addUnits(height);
2616             }else{
2617                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
2618             }
2619             return me;
2620         },
2621
2622         /**
2623          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
2624          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
2625          * if a height has not been set using CSS.
2626          * @return {Number}
2627          */
2628         getComputedHeight : function(){
2629             var me = this,
2630                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
2631             if(!h){
2632                 h = parseFloat(me.getStyle('height')) || 0;
2633                 if(!me.isBorderBox()){
2634                     h += me.getFrameWidth('tb');
2635                 }
2636             }
2637             return h;
2638         },
2639
2640         /**
2641          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
2642          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
2643          * if a width has not been set using CSS.
2644          * @return {Number}
2645          */
2646         getComputedWidth : function(){
2647             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
2648             if(!w){
2649                 w = parseFloat(this.getStyle('width')) || 0;
2650                 if(!this.isBorderBox()){
2651                     w += this.getFrameWidth('lr');
2652                 }
2653             }
2654             return w;
2655         },
2656
2657         /**
2658          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
2659          for more information about the sides.
2660          * @param {String} sides
2661          * @return {Number}
2662          */
2663         getFrameWidth : function(sides, onlyContentBox){
2664             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
2665         },
2666
2667         /**
2668          * Sets up event handlers to add and remove a css class when the mouse is over this element
2669          * @param {String} className
2670          * @return {Ext.Element} this
2671          */
2672         addClassOnOver : function(className){
2673             this.hover(
2674                 function(){
2675                     Ext.fly(this, INTERNAL).addClass(className);
2676                 },
2677                 function(){
2678                     Ext.fly(this, INTERNAL).removeClass(className);
2679                 }
2680             );
2681             return this;
2682         },
2683
2684         /**
2685          * Sets up event handlers to add and remove a css class when this element has the focus
2686          * @param {String} className
2687          * @return {Ext.Element} this
2688          */
2689         addClassOnFocus : function(className){
2690             this.on("focus", function(){
2691                 Ext.fly(this, INTERNAL).addClass(className);
2692             }, this.dom);
2693             this.on("blur", function(){
2694                 Ext.fly(this, INTERNAL).removeClass(className);
2695             }, this.dom);
2696             return this;
2697         },
2698
2699         /**
2700          * 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)
2701          * @param {String} className
2702          * @return {Ext.Element} this
2703          */
2704         addClassOnClick : function(className){
2705             var dom = this.dom;
2706             this.on("mousedown", function(){
2707                 Ext.fly(dom, INTERNAL).addClass(className);
2708                 var d = Ext.getDoc(),
2709                     fn = function(){
2710                         Ext.fly(dom, INTERNAL).removeClass(className);
2711                         d.removeListener("mouseup", fn);
2712                     };
2713                 d.on("mouseup", fn);
2714             });
2715             return this;
2716         },
2717
2718         /**
2719          * <p>Returns the dimensions of the element available to lay content out in.<p>
2720          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
2721          * example:<pre><code>
2722         var vpSize = Ext.getBody().getViewSize();
2723
2724         // all Windows created afterwards will have a default value of 90% height and 95% width
2725         Ext.Window.override({
2726             width: vpSize.width * 0.9,
2727             height: vpSize.height * 0.95
2728         });
2729         // To handle window resizing you would have to hook onto onWindowResize.
2730         * </code></pre>
2731         *
2732         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
2733         * To obtain the size including scrollbars, use getStyleSize
2734         *
2735         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
2736         */
2737
2738         getViewSize : function(){
2739             var doc = document,
2740                 d = this.dom,
2741                 isDoc = (d == doc || d == doc.body);
2742
2743             // If the body, use Ext.lib.Dom
2744             if (isDoc) {
2745                 var extdom = Ext.lib.Dom;
2746                 return {
2747                     width : extdom.getViewWidth(),
2748                     height : extdom.getViewHeight()
2749                 };
2750
2751             // Else use clientHeight/clientWidth
2752             } else {
2753                 return {
2754                     width : d.clientWidth,
2755                     height : d.clientHeight
2756                 };
2757             }
2758         },
2759
2760         /**
2761         * <p>Returns the dimensions of the element available to lay content out in.<p>
2762         *
2763         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
2764         * To obtain the size excluding scrollbars, use getViewSize
2765         *
2766         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
2767         */
2768
2769         getStyleSize : function(){
2770             var me = this,
2771                 w, h,
2772                 doc = document,
2773                 d = this.dom,
2774                 isDoc = (d == doc || d == doc.body),
2775                 s = d.style;
2776
2777             // If the body, use Ext.lib.Dom
2778             if (isDoc) {
2779                 var extdom = Ext.lib.Dom;
2780                 return {
2781                     width : extdom.getViewWidth(),
2782                     height : extdom.getViewHeight()
2783                 };
2784             }
2785             // Use Styles if they are set
2786             if(s.width && s.width != 'auto'){
2787                 w = parseFloat(s.width);
2788                 if(me.isBorderBox()){
2789                    w -= me.getFrameWidth('lr');
2790                 }
2791             }
2792             // Use Styles if they are set
2793             if(s.height && s.height != 'auto'){
2794                 h = parseFloat(s.height);
2795                 if(me.isBorderBox()){
2796                    h -= me.getFrameWidth('tb');
2797                 }
2798             }
2799             // Use getWidth/getHeight if style not set.
2800             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
2801         },
2802
2803         /**
2804          * Returns the size of the element.
2805          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
2806          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
2807          */
2808         getSize : function(contentSize){
2809             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
2810         },
2811
2812         /**
2813          * Forces the browser to repaint this element
2814          * @return {Ext.Element} this
2815          */
2816         repaint : function(){
2817             var dom = this.dom;
2818             this.addClass("x-repaint");
2819             setTimeout(function(){
2820                 Ext.fly(dom).removeClass("x-repaint");
2821             }, 1);
2822             return this;
2823         },
2824
2825         /**
2826          * Disables text selection for this element (normalized across browsers)
2827          * @return {Ext.Element} this
2828          */
2829         unselectable : function(){
2830             this.dom.unselectable = "on";
2831             return this.swallowEvent("selectstart", true).
2832                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
2833                         addClass("x-unselectable");
2834         },
2835
2836         /**
2837          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
2838          * then it returns the calculated width of the sides (see getPadding)
2839          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
2840          * @return {Object/Number}
2841          */
2842         getMargins : function(side){
2843             var me = this,
2844                 key,
2845                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
2846                 o = {};
2847
2848             if (!side) {
2849                 for (key in me.margins){
2850                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
2851                 }
2852                 return o;
2853             } else {
2854                 return me.addStyles.call(me, side, me.margins);
2855             }
2856         }
2857     };
2858 }());
2859 /**
2860  * @class Ext.Element
2861  */
2862 Ext.Element.addMethods({
2863     /**
2864      * 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.
2865      * @param {Object} box The box to fill {x, y, width, height}
2866      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
2867      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2868      * @return {Ext.Element} this
2869      */
2870     setBox : function(box, adjust, animate){
2871         var me = this,
2872                 w = box.width, 
2873                 h = box.height;
2874         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
2875            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2876            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2877         }
2878         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
2879         return me;
2880     },
2881
2882     /**
2883      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
2884      * set another Element's size/location to match this element.
2885      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
2886      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
2887      * @return {Object} box An object in the format<pre><code>
2888 {
2889     x: &lt;Element's X position>,
2890     y: &lt;Element's Y position>,
2891     width: &lt;Element's width>,
2892     height: &lt;Element's height>,
2893     bottom: &lt;Element's lower bound>,
2894     right: &lt;Element's rightmost bound>
2895 }
2896 </code></pre>
2897      * The returned object may also be addressed as an Array where index 0 contains the X position
2898      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
2899      */
2900         getBox : function(contentBox, local) {      
2901             var me = this,
2902                 xy,
2903                 left,
2904                 top,
2905                 getBorderWidth = me.getBorderWidth,
2906                 getPadding = me.getPadding, 
2907                 l,
2908                 r,
2909                 t,
2910                 b;
2911         if(!local){
2912             xy = me.getXY();
2913         }else{
2914             left = parseInt(me.getStyle("left"), 10) || 0;
2915             top = parseInt(me.getStyle("top"), 10) || 0;
2916             xy = [left, top];
2917         }
2918         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
2919         if(!contentBox){
2920             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
2921         }else{
2922             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
2923             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
2924             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
2925             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
2926             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)};
2927         }
2928         bx.right = bx.x + bx.width;
2929         bx.bottom = bx.y + bx.height;
2930         return bx;
2931         },
2932         
2933     /**
2934      * Move this element relative to its current position.
2935      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
2936      * @param {Number} distance How far to move the element in pixels
2937      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2938      * @return {Ext.Element} this
2939      */
2940      move : function(direction, distance, animate){
2941         var me = this,          
2942                 xy = me.getXY(),
2943                 x = xy[0],
2944                 y = xy[1],              
2945                 left = [x - distance, y],
2946                 right = [x + distance, y],
2947                 top = [x, y - distance],
2948                 bottom = [x, y + distance],
2949                 hash = {
2950                         l :     left,
2951                         left : left,
2952                         r : right,
2953                         right : right,
2954                         t : top,
2955                         top : top,
2956                         up : top,
2957                         b : bottom, 
2958                         bottom : bottom,
2959                         down : bottom                           
2960                 };
2961         
2962             direction = direction.toLowerCase();    
2963             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
2964     },
2965     
2966     /**
2967      * Quick set left and top adding default units
2968      * @param {String} left The left CSS property value
2969      * @param {String} top The top CSS property value
2970      * @return {Ext.Element} this
2971      */
2972      setLeftTop : function(left, top){
2973             var me = this,
2974                 style = me.dom.style;
2975         style.left = me.addUnits(left);
2976         style.top = me.addUnits(top);
2977         return me;
2978     },
2979     
2980     /**
2981      * Returns the region of the given element.
2982      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
2983      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
2984      */
2985     getRegion : function(){
2986         return Ext.lib.Dom.getRegion(this.dom);
2987     },
2988     
2989     /**
2990      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
2991      * @param {Number} x X value for new position (coordinates are page-based)
2992      * @param {Number} y Y value for new position (coordinates are page-based)
2993      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
2994      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
2995      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
2996      * </ul></div>
2997      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
2998      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
2999      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3000      * </ul></div>
3001      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3002      * @return {Ext.Element} this
3003      */
3004     setBounds : function(x, y, width, height, animate){
3005             var me = this;
3006         if (!animate || !me.anim) {
3007             me.setSize(width, height);
3008             me.setLocation(x, y);
3009         } else {
3010             me.anim({points: {to: [x, y]}, 
3011                          width: {to: me.adjustWidth(width)}, 
3012                          height: {to: me.adjustHeight(height)}},
3013                      me.preanim(arguments, 4), 
3014                      'motion');
3015         }
3016         return me;
3017     },
3018
3019     /**
3020      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
3021      * @param {Ext.lib.Region} region The region to fill
3022      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3023      * @return {Ext.Element} this
3024      */
3025     setRegion : function(region, animate) {
3026         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
3027     }
3028 });/**
3029  * @class Ext.Element
3030  */
3031 Ext.Element.addMethods({
3032     /**
3033      * 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().
3034      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
3035      * @param {Number} value The new scroll value
3036      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3037      * @return {Element} this
3038      */
3039     scrollTo : function(side, value, animate) {
3040         //check if we're scrolling top or left
3041         var top = /top/i.test(side),
3042             me = this,
3043             dom = me.dom,
3044             prop;
3045         if (!animate || !me.anim) {
3046             // just setting the value, so grab the direction
3047             prop = 'scroll' + (top ? 'Top' : 'Left');
3048             dom[prop] = value;
3049         }
3050         else {
3051             // if scrolling top, we need to grab scrollLeft, if left, scrollTop
3052             prop = 'scroll' + (top ? 'Left' : 'Top');
3053             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
3054         }
3055         return me;
3056     },
3057     
3058     /**
3059      * Scrolls this element into view within the passed container.
3060      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
3061      * string (id), dom node, or Ext.Element.
3062      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
3063      * @return {Ext.Element} this
3064      */
3065     scrollIntoView : function(container, hscroll) {
3066         var c = Ext.getDom(container) || Ext.getBody().dom,
3067             el = this.dom,
3068             o = this.getOffsetsTo(c),
3069             l = o[0] + c.scrollLeft,
3070             t = o[1] + c.scrollTop,
3071             b = t + el.offsetHeight,
3072             r = l + el.offsetWidth,
3073             ch = c.clientHeight,
3074             ct = parseInt(c.scrollTop, 10),
3075             cl = parseInt(c.scrollLeft, 10),
3076             cb = ct + ch,
3077             cr = cl + c.clientWidth;
3078
3079         if (el.offsetHeight > ch || t < ct) {
3080             c.scrollTop = t;
3081         }
3082         else if (b > cb) {
3083             c.scrollTop = b-ch;
3084         }
3085         // corrects IE, other browsers will ignore
3086         c.scrollTop = c.scrollTop;
3087
3088         if (hscroll !== false) {
3089             if (el.offsetWidth > c.clientWidth || l < cl) {
3090                 c.scrollLeft = l;
3091             }
3092             else if (r > cr) {
3093                 c.scrollLeft = r - c.clientWidth;
3094             }
3095             c.scrollLeft = c.scrollLeft;
3096         }
3097         return this;
3098     },
3099
3100     // private
3101     scrollChildIntoView : function(child, hscroll) {
3102         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
3103     },
3104     
3105     /**
3106      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
3107      * within this element's scrollable range.
3108      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
3109      * @param {Number} distance How far to scroll the element in pixels
3110      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3111      * @return {Boolean} Returns true if a scroll was triggered or false if the element
3112      * was scrolled as far as it could go.
3113      */
3114      scroll : function(direction, distance, animate) {
3115         if (!this.isScrollable()) {
3116             return false;
3117         }
3118         var el = this.dom,
3119             l = el.scrollLeft, t = el.scrollTop,
3120             w = el.scrollWidth, h = el.scrollHeight,
3121             cw = el.clientWidth, ch = el.clientHeight,
3122             scrolled = false, v,
3123             hash = {
3124                 l: Math.min(l + distance, w-cw),
3125                 r: v = Math.max(l - distance, 0),
3126                 t: Math.max(t - distance, 0),
3127                 b: Math.min(t + distance, h-ch)
3128             };
3129             hash.d = hash.b;
3130             hash.u = hash.t;
3131         
3132         direction = direction.substr(0, 1);
3133         if ((v = hash[direction]) > -1) {
3134             scrolled = true;
3135             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
3136         }
3137         return scrolled;
3138     }
3139 });/**
3140  * @class Ext.Element
3141  */
3142 Ext.Element.addMethods(
3143     function() {
3144         var VISIBILITY      = "visibility",
3145             DISPLAY         = "display",
3146             HIDDEN          = "hidden",
3147             NONE            = "none",
3148             XMASKED         = "x-masked",
3149             XMASKEDRELATIVE = "x-masked-relative",
3150             data            = Ext.Element.data;
3151
3152         return {
3153             /**
3154              * Checks whether the element is currently visible using both visibility and display properties.
3155              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
3156              * @return {Boolean} True if the element is currently visible, else false
3157              */
3158             isVisible : function(deep) {
3159                 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
3160                     p   = this.dom.parentNode;
3161                 
3162                 if (deep !== true || !vis) {
3163                     return vis;
3164                 }
3165                 
3166                 while (p && !(/^body/i.test(p.tagName))) {
3167                     if (!Ext.fly(p, '_isVisible').isVisible()) {
3168                         return false;
3169                     }
3170                     p = p.parentNode;
3171                 }
3172                 return true;
3173             },
3174
3175             /**
3176              * Returns true if display is not "none"
3177              * @return {Boolean}
3178              */
3179             isDisplayed : function() {
3180                 return !this.isStyle(DISPLAY, NONE);
3181             },
3182
3183             /**
3184              * Convenience method for setVisibilityMode(Element.DISPLAY)
3185              * @param {String} display (optional) What to set display to when visible
3186              * @return {Ext.Element} this
3187              */
3188             enableDisplayMode : function(display) {
3189                 this.setVisibilityMode(Ext.Element.DISPLAY);
3190                 
3191                 if (!Ext.isEmpty(display)) {
3192                     data(this.dom, 'originalDisplay', display);
3193                 }
3194                 
3195                 return this;
3196             },
3197
3198             /**
3199              * Puts a mask over this element to disable user interaction. Requires core.css.
3200              * This method can only be applied to elements which accept child nodes.
3201              * @param {String} msg (optional) A message to display in the mask
3202              * @param {String} msgCls (optional) A css class to apply to the msg element
3203              * @return {Element} The mask element
3204              */
3205             mask : function(msg, msgCls) {
3206                 var me  = this,
3207                     dom = me.dom,
3208                     dh  = Ext.DomHelper,
3209                     EXTELMASKMSG = "ext-el-mask-msg",
3210                     el,
3211                     mask;
3212
3213                 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
3214                     me.addClass(XMASKEDRELATIVE);
3215                 }
3216                 if (el = data(dom, 'maskMsg')) {
3217                     el.remove();
3218                 }
3219                 if (el = data(dom, 'mask')) {
3220                     el.remove();
3221                 }
3222
3223                 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
3224                 data(dom, 'mask', mask);
3225
3226                 me.addClass(XMASKED);
3227                 mask.setDisplayed(true);
3228                 
3229                 if (typeof msg == 'string') {
3230                     var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
3231                     data(dom, 'maskMsg', mm);
3232                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
3233                     mm.dom.firstChild.innerHTML = msg;
3234                     mm.setDisplayed(true);
3235                     mm.center(me);
3236                 }
3237                 
3238                 // ie will not expand full height automatically
3239                 if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
3240                     mask.setSize(undefined, me.getHeight());
3241                 }
3242                 
3243                 return mask;
3244             },
3245
3246             /**
3247              * Removes a previously applied mask.
3248              */
3249             unmask : function() {
3250                 var me      = this,
3251                     dom     = me.dom,
3252                     mask    = data(dom, 'mask'),
3253                     maskMsg = data(dom, 'maskMsg');
3254
3255                 if (mask) {
3256                     if (maskMsg) {
3257                         maskMsg.remove();
3258                         data(dom, 'maskMsg', undefined);
3259                     }
3260                     
3261                     mask.remove();
3262                     data(dom, 'mask', undefined);
3263                     me.removeClass([XMASKED, XMASKEDRELATIVE]);
3264                 }
3265             },
3266
3267             /**
3268              * Returns true if this element is masked
3269              * @return {Boolean}
3270              */
3271             isMasked : function() {
3272                 var m = data(this.dom, 'mask');
3273                 return m && m.isVisible();
3274             },
3275
3276             /**
3277              * Creates an iframe shim for this element to keep selects and other windowed objects from
3278              * showing through.
3279              * @return {Ext.Element} The new shim element
3280              */
3281             createShim : function() {
3282                 var el = document.createElement('iframe'),
3283                     shim;
3284                 
3285                 el.frameBorder = '0';
3286                 el.className = 'ext-shim';
3287                 el.src = Ext.SSL_SECURE_URL;
3288                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
3289                 shim.autoBoxAdjust = false;
3290                 return shim;
3291             }
3292         };
3293     }()
3294 );/**
3295  * @class Ext.Element
3296  */
3297 Ext.Element.addMethods({
3298     /**
3299      * Convenience method for constructing a KeyMap
3300      * @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:
3301      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
3302      * @param {Function} fn The function to call
3303      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
3304      * @return {Ext.KeyMap} The KeyMap created
3305      */
3306     addKeyListener : function(key, fn, scope){
3307         var config;
3308         if(typeof key != 'object' || Ext.isArray(key)){
3309             config = {
3310                 key: key,
3311                 fn: fn,
3312                 scope: scope
3313             };
3314         }else{
3315             config = {
3316                 key : key.key,
3317                 shift : key.shift,
3318                 ctrl : key.ctrl,
3319                 alt : key.alt,
3320                 fn: fn,
3321                 scope: scope
3322             };
3323         }
3324         return new Ext.KeyMap(this, config);
3325     },
3326
3327     /**
3328      * Creates a KeyMap for this element
3329      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
3330      * @return {Ext.KeyMap} The KeyMap created
3331      */
3332     addKeyMap : function(config){
3333         return new Ext.KeyMap(this, config);
3334     }
3335 });
3336
3337 //Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because
3338 //Element.keys.js is the last extra Ext.Element include in the ext-all.js build
3339 Ext.CompositeElementLite.importElementMethods();/**
3340  * @class Ext.CompositeElementLite
3341  */
3342 Ext.apply(Ext.CompositeElementLite.prototype, {
3343     addElements : function(els, root){
3344         if(!els){
3345             return this;
3346         }
3347         if(typeof els == "string"){
3348             els = Ext.Element.selectorFunction(els, root);
3349         }
3350         var yels = this.elements;
3351         Ext.each(els, function(e) {
3352             yels.push(Ext.get(e));
3353         });
3354         return this;
3355     },
3356
3357     /**
3358      * Returns the first Element
3359      * @return {Ext.Element}
3360      */
3361     first : function(){
3362         return this.item(0);
3363     },
3364
3365     /**
3366      * Returns the last Element
3367      * @return {Ext.Element}
3368      */
3369     last : function(){
3370         return this.item(this.getCount()-1);
3371     },
3372
3373     /**
3374      * Returns true if this composite contains the passed element
3375      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
3376      * @return Boolean
3377      */
3378     contains : function(el){
3379         return this.indexOf(el) != -1;
3380     },
3381
3382     /**
3383     * Removes the specified element(s).
3384     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
3385     * or an array of any of those.
3386     * @param {Boolean} removeDom (optional) True to also remove the element from the document
3387     * @return {CompositeElement} this
3388     */
3389     removeElement : function(keys, removeDom){
3390         var me = this,
3391             els = this.elements,
3392             el;
3393         Ext.each(keys, function(val){
3394             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
3395                 if(removeDom){
3396                     if(el.dom){
3397                         el.remove();
3398                     }else{
3399                         Ext.removeNode(el);
3400                     }
3401                 }
3402                 els.splice(val, 1);
3403             }
3404         });
3405         return this;
3406     }
3407 });
3408 /**
3409  * @class Ext.CompositeElement
3410  * @extends Ext.CompositeElementLite
3411  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
3412  * members, or to perform collective actions upon the whole set.</p>
3413  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
3414  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
3415  * <p>All methods return <i>this</i> and can be chained.</p>
3416  * Usage:
3417 <pre><code>
3418 var els = Ext.select("#some-el div.some-class", true);
3419 // or select directly from an existing element
3420 var el = Ext.get('some-el');
3421 el.select('div.some-class', true);
3422
3423 els.setWidth(100); // all elements become 100 width
3424 els.hide(true); // all elements fade out and hide
3425 // or
3426 els.setWidth(100).hide(true);
3427 </code></pre>
3428  */
3429 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
3430     
3431     constructor : function(els, root){
3432         this.elements = [];
3433         this.add(els, root);
3434     },
3435     
3436     // private
3437     getElement : function(el){
3438         // In this case just return it, since we already have a reference to it
3439         return el;
3440     },
3441     
3442     // private
3443     transformElement : function(el){
3444         return Ext.get(el);
3445     }
3446
3447     /**
3448     * Adds elements to this composite.
3449     * @param {String/Array} els A string CSS selector, an array of elements or an element
3450     * @return {CompositeElement} this
3451     */
3452
3453     /**
3454      * Returns the Element object at the specified index
3455      * @param {Number} index
3456      * @return {Ext.Element}
3457      */
3458
3459     /**
3460      * Iterates each <code>element</code> in this <code>composite</code>
3461      * calling the supplied function using {@link Ext#each}.
3462      * @param {Function} fn The function to be called with each
3463      * <code>element</code>. If the supplied function returns <tt>false</tt>,
3464      * iteration stops. This function is called with the following arguments:
3465      * <div class="mdetail-params"><ul>
3466      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
3467      * in the <code>composite</code></div></li>
3468      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
3469      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
3470      * </ul></div>
3471      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
3472      * Defaults to the <code>element</code> at the current <code>index</code>
3473      * within the composite.
3474      * @return {CompositeElement} this
3475      */
3476 });
3477
3478 /**
3479  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
3480  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
3481  * {@link Ext.CompositeElementLite CompositeElementLite} object.
3482  * @param {String/Array} selector The CSS selector or an array of elements
3483  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
3484  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
3485  * @return {CompositeElementLite/CompositeElement}
3486  * @member Ext.Element
3487  * @method select
3488  */
3489 Ext.Element.select = function(selector, unique, root){
3490     var els;
3491     if(typeof selector == "string"){
3492         els = Ext.Element.selectorFunction(selector, root);
3493     }else if(selector.length !== undefined){
3494         els = selector;
3495     }else{
3496         throw "Invalid selector";
3497     }
3498
3499     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
3500 };
3501
3502 /**
3503  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
3504  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
3505  * {@link Ext.CompositeElementLite CompositeElementLite} object.
3506  * @param {String/Array} selector The CSS selector or an array of elements
3507  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
3508  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
3509  * @return {CompositeElementLite/CompositeElement}
3510  * @member Ext
3511  * @method select
3512  */
3513 Ext.select = Ext.Element.select;/**
3514  * @class Ext.Updater
3515  * @extends Ext.util.Observable
3516  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
3517  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
3518  * {@link Ext.Element Element} on a specific interval.<br><br>
3519  * Usage:<br>
3520  * <pre><code>
3521  * var el = Ext.get("foo"); // Get Ext.Element object
3522  * var mgr = el.getUpdater();
3523  * mgr.update({
3524         url: "http://myserver.com/index.php",
3525         params: {
3526             param1: "foo",
3527             param2: "bar"
3528         }
3529  * });
3530  * ...
3531  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
3532  * <br>
3533  * // or directly (returns the same Updater instance)
3534  * var mgr = new Ext.Updater("myElementId");
3535  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
3536  * mgr.on("update", myFcnNeedsToKnow);
3537  * <br>
3538  * // short handed call directly from the element object
3539  * Ext.get("foo").load({
3540         url: "bar.php",
3541         scripts: true,
3542         params: "param1=foo&amp;param2=bar",
3543         text: "Loading Foo..."
3544  * });
3545  * </code></pre>
3546  * @constructor
3547  * Create new Updater directly.
3548  * @param {Mixed} el The element to update
3549  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
3550  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
3551  */
3552 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
3553 function() {
3554     var BEFOREUPDATE = "beforeupdate",
3555         UPDATE = "update",
3556         FAILURE = "failure";
3557
3558     // private
3559     function processSuccess(response){
3560         var me = this;
3561         me.transaction = null;
3562         if (response.argument.form && response.argument.reset) {
3563             try { // put in try/catch since some older FF releases had problems with this
3564                 response.argument.form.reset();
3565             } catch(e){}
3566         }
3567         if (me.loadScripts) {
3568             me.renderer.render(me.el, response, me,
3569                updateComplete.createDelegate(me, [response]));
3570         } else {
3571             me.renderer.render(me.el, response, me);
3572             updateComplete.call(me, response);
3573         }
3574     }
3575
3576     // private
3577     function updateComplete(response, type, success){
3578         this.fireEvent(type || UPDATE, this.el, response);
3579         if(Ext.isFunction(response.argument.callback)){
3580             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
3581         }
3582     }
3583
3584     // private
3585     function processFailure(response){
3586         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
3587     }
3588
3589     return {
3590         constructor: function(el, forceNew){
3591             var me = this;
3592             el = Ext.get(el);
3593             if(!forceNew && el.updateManager){
3594                 return el.updateManager;
3595             }
3596             /**
3597              * The Element object
3598              * @type Ext.Element
3599              */
3600             me.el = el;
3601             /**
3602              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
3603              * @type String
3604              */
3605             me.defaultUrl = null;
3606
3607             me.addEvents(
3608                 /**
3609                  * @event beforeupdate
3610                  * Fired before an update is made, return false from your handler and the update is cancelled.
3611                  * @param {Ext.Element} el
3612                  * @param {String/Object/Function} url
3613                  * @param {String/Object} params
3614                  */
3615                 BEFOREUPDATE,
3616                 /**
3617                  * @event update
3618                  * Fired after successful update is made.
3619                  * @param {Ext.Element} el
3620                  * @param {Object} oResponseObject The response Object
3621                  */
3622                 UPDATE,
3623                 /**
3624                  * @event failure
3625                  * Fired on update failure.
3626                  * @param {Ext.Element} el
3627                  * @param {Object} oResponseObject The response Object
3628                  */
3629                 FAILURE
3630             );
3631
3632             Ext.apply(me, Ext.Updater.defaults);
3633             /**
3634              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
3635              * @property sslBlankUrl
3636              * @type String
3637              */
3638             /**
3639              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
3640              * @property disableCaching
3641              * @type Boolean
3642              */
3643             /**
3644              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
3645              * @property indicatorText
3646              * @type String
3647              */
3648             /**
3649              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
3650              * @property showLoadIndicator
3651              * @type String
3652              */
3653             /**
3654              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
3655              * @property timeout
3656              * @type Number
3657              */
3658             /**
3659              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
3660              * @property loadScripts
3661              * @type Boolean
3662              */
3663
3664             /**
3665              * Transaction object of the current executing transaction, or null if there is no active transaction.
3666              */
3667             me.transaction = null;
3668             /**
3669              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
3670              * @type Function
3671              */
3672             me.refreshDelegate = me.refresh.createDelegate(me);
3673             /**
3674              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
3675              * @type Function
3676              */
3677             me.updateDelegate = me.update.createDelegate(me);
3678             /**
3679              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
3680              * @type Function
3681              */
3682             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
3683
3684             /**
3685              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
3686              */
3687             me.renderer = me.renderer || me.getDefaultRenderer();
3688
3689             Ext.Updater.superclass.constructor.call(me);
3690         },
3691
3692         /**
3693          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
3694          * @param {Object} renderer The object implementing the render() method
3695          */
3696         setRenderer : function(renderer){
3697             this.renderer = renderer;
3698         },
3699
3700         /**
3701          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
3702          * @return {Object}
3703          */
3704         getRenderer : function(){
3705            return this.renderer;
3706         },
3707
3708         /**
3709          * This is an overrideable method which returns a reference to a default
3710          * renderer class if none is specified when creating the Ext.Updater.
3711          * Defaults to {@link Ext.Updater.BasicRenderer}
3712          */
3713         getDefaultRenderer: function() {
3714             return new Ext.Updater.BasicRenderer();
3715         },
3716
3717         /**
3718          * Sets the default URL used for updates.
3719          * @param {String/Function} defaultUrl The url or a function to call to get the url
3720          */
3721         setDefaultUrl : function(defaultUrl){
3722             this.defaultUrl = defaultUrl;
3723         },
3724
3725         /**
3726          * Get the Element this Updater is bound to
3727          * @return {Ext.Element} The element
3728          */
3729         getEl : function(){
3730             return this.el;
3731         },
3732
3733         /**
3734          * Performs an <b>asynchronous</b> request, updating this element with the response.
3735          * If params are specified it uses POST, otherwise it uses GET.<br><br>
3736          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
3737          * will not have been fully updated when the function returns. To post-process the returned
3738          * data, use the callback option, or an <b><code>update</code></b> event handler.
3739          * @param {Object} options A config object containing any of the following options:<ul>
3740          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
3741          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
3742          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
3743          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
3744          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
3745          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
3746          * string, or as an object containing properties which represent parameters,
3747          * or as a function, which returns such an object.</p></li>
3748          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
3749          * any &lt;script&gt; tags embedded in the response text will be extracted
3750          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
3751          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
3752          * <li>callback : <b>Function</b><p class="sub-desc">A function to
3753          * be called when the response from the server arrives. The following
3754          * parameters are passed:<ul>
3755          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
3756          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
3757          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
3758          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
3759          * </p></li>
3760          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
3761          * to execute the callback (The callback's <code>this</code> reference.) If the
3762          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
3763          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
3764          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
3765          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
3766          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
3767          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
3768          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
3769          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
3770          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
3771          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
3772          * requests, this option causes an extra, auto-generated parameter to be appended to the request
3773          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
3774          * <p>
3775          * For example:
3776     <pre><code>
3777     um.update({
3778         url: "your-url.php",
3779         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
3780         callback: yourFunction,
3781         scope: yourObject, //(optional scope)
3782         discardUrl: true,
3783         nocache: true,
3784         text: "Loading...",
3785         timeout: 60,
3786         scripts: false // Save time by avoiding RegExp execution.
3787     });
3788     </code></pre>
3789          */
3790         update : function(url, params, callback, discardUrl){
3791             var me = this,
3792                 cfg,
3793                 callerScope;
3794
3795             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
3796                 if(Ext.isObject(url)){ // must be config object
3797                     cfg = url;
3798                     url = cfg.url;
3799                     params = params || cfg.params;
3800                     callback = callback || cfg.callback;
3801                     discardUrl = discardUrl || cfg.discardUrl;
3802                     callerScope = cfg.scope;
3803                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
3804                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
3805                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
3806                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
3807                 }
3808                 me.showLoading();
3809
3810                 if(!discardUrl){
3811                     me.defaultUrl = url;
3812                 }
3813                 if(Ext.isFunction(url)){
3814                     url = url.call(me);
3815                 }
3816
3817                 var o = Ext.apply({}, {
3818                     url : url,
3819                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
3820                     success: processSuccess,
3821                     failure: processFailure,
3822                     scope: me,
3823                     callback: undefined,
3824                     timeout: (me.timeout*1000),
3825                     disableCaching: me.disableCaching,
3826                     argument: {
3827                         "options": cfg,
3828                         "url": url,
3829                         "form": null,
3830                         "callback": callback,
3831                         "scope": callerScope || window,
3832                         "params": params
3833                     }
3834                 }, cfg);
3835
3836                 me.transaction = Ext.Ajax.request(o);
3837             }
3838         },
3839
3840         /**
3841          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
3842          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
3843          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
3844          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
3845          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
3846          * DOM <code>&lt;form></code> element temporarily modified to have its
3847          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
3848          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
3849          * but removed after the return data has been gathered.</p>
3850          * <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>
3851          * and some server technologies (notably JEE) may require some custom processing in order to
3852          * retrieve parameter names and parameter values from the packet content.</p>
3853          * @param {String/HTMLElement} form The form Id or form element
3854          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
3855          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
3856          * @param {Function} callback (optional) Callback when transaction is complete. The following
3857          * parameters are passed:<ul>
3858          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
3859          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
3860          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
3861          */
3862         formUpdate : function(form, url, reset, callback){
3863             var me = this;
3864             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
3865                 if(Ext.isFunction(url)){
3866                     url = url.call(me);
3867                 }
3868                 form = Ext.getDom(form);
3869                 me.transaction = Ext.Ajax.request({
3870                     form: form,
3871                     url:url,
3872                     success: processSuccess,
3873                     failure: processFailure,
3874                     scope: me,
3875                     timeout: (me.timeout*1000),
3876                     argument: {
3877                         "url": url,
3878                         "form": form,
3879                         "callback": callback,
3880                         "reset": reset
3881                     }
3882                 });
3883                 me.showLoading.defer(1, me);
3884             }
3885         },
3886
3887         /**
3888          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
3889          * @param {Number} interval How often to update (in seconds).
3890          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
3891          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
3892          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
3893          * sepcified as part of a config object passed as this paramter if needed.
3894          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
3895          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
3896          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
3897          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
3898          */
3899         startAutoRefresh : function(interval, url, params, callback, refreshNow){
3900             var me = this;
3901             if(refreshNow){
3902                 me.update(url || me.defaultUrl, params, callback, true);
3903             }
3904             if(me.autoRefreshProcId){
3905                 clearInterval(me.autoRefreshProcId);
3906             }
3907             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
3908         },
3909
3910         /**
3911          * Stop auto refresh on this element.
3912          */
3913         stopAutoRefresh : function(){
3914             if(this.autoRefreshProcId){
3915                 clearInterval(this.autoRefreshProcId);
3916                 delete this.autoRefreshProcId;
3917             }
3918         },
3919
3920         /**
3921          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
3922          */
3923         isAutoRefreshing : function(){
3924            return !!this.autoRefreshProcId;
3925         },
3926
3927         /**
3928          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
3929          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
3930          */
3931         showLoading : function(){
3932             if(this.showLoadIndicator){
3933                 this.el.dom.innerHTML = this.indicatorText;
3934             }
3935         },
3936
3937         /**
3938          * Aborts the currently executing transaction, if any.
3939          */
3940         abort : function(){
3941             if(this.transaction){
3942                 Ext.Ajax.abort(this.transaction);
3943             }
3944         },
3945
3946         /**
3947          * Returns true if an update is in progress, otherwise false.
3948          * @return {Boolean}
3949          */
3950         isUpdating : function(){
3951             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
3952         },
3953
3954         /**
3955          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
3956          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
3957          */
3958         refresh : function(callback){
3959             if(this.defaultUrl){
3960                 this.update(this.defaultUrl, null, callback, true);
3961             }
3962         }
3963     };
3964 }());
3965
3966 /**
3967  * @class Ext.Updater.defaults
3968  * The defaults collection enables customizing the default properties of Updater
3969  */
3970 Ext.Updater.defaults = {
3971    /**
3972      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
3973      * @type Number
3974      */
3975     timeout : 30,
3976     /**
3977      * True to append a unique parameter to GET requests to disable caching (defaults to false).
3978      * @type Boolean
3979      */
3980     disableCaching : false,
3981     /**
3982      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
3983      * @type Boolean
3984      */
3985     showLoadIndicator : true,
3986     /**
3987      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
3988      * @type String
3989      */
3990     indicatorText : '<div class="loading-indicator">Loading...</div>',
3991      /**
3992      * True to process scripts by default (defaults to false).
3993      * @type Boolean
3994      */
3995     loadScripts : false,
3996     /**
3997     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
3998     * @type String
3999     */
4000     sslBlankUrl : Ext.SSL_SECURE_URL
4001 };
4002
4003
4004 /**
4005  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
4006  * Usage:
4007  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
4008  * @param {Mixed} el The element to update
4009  * @param {String} url The url
4010  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
4011  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
4012  * example: {disableCaching:true, indicatorText: "Loading data..."}
4013  * @static
4014  * @deprecated
4015  * @member Ext.Updater
4016  */
4017 Ext.Updater.updateElement = function(el, url, params, options){
4018     var um = Ext.get(el).getUpdater();
4019     Ext.apply(um, options);
4020     um.update(url, params, options ? options.callback : null);
4021 };
4022
4023 /**
4024  * @class Ext.Updater.BasicRenderer
4025  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
4026  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
4027  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
4028  */
4029 Ext.Updater.BasicRenderer = function(){};
4030
4031 Ext.Updater.BasicRenderer.prototype = {
4032     /**
4033      * This method is called when an Ajax response is received, and an Element needs updating.
4034      * @param {Ext.Element} el The element being rendered
4035      * @param {Object} xhr The XMLHttpRequest object
4036      * @param {Updater} updateManager The calling update manager
4037      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
4038      */
4039      render : function(el, response, updateManager, callback){
4040         el.update(response.responseText, updateManager.loadScripts, callback);
4041     }
4042 };/**
4043  * @class Date
4044  *
4045  * The date parsing and formatting syntax contains a subset of
4046  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
4047  * supported will provide results equivalent to their PHP versions.
4048  *
4049  * The following is a list of all currently supported formats:
4050  * <pre>
4051 Format  Description                                                               Example returned values
4052 ------  -----------------------------------------------------------------------   -----------------------
4053   d     Day of the month, 2 digits with leading zeros                             01 to 31
4054   D     A short textual representation of the day of the week                     Mon to Sun
4055   j     Day of the month without leading zeros                                    1 to 31
4056   l     A full textual representation of the day of the week                      Sunday to Saturday
4057   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
4058   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
4059   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
4060   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
4061   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
4062   F     A full textual representation of a month, such as January or March        January to December
4063   m     Numeric representation of a month, with leading zeros                     01 to 12
4064   M     A short textual representation of a month                                 Jan to Dec
4065   n     Numeric representation of a month, without leading zeros                  1 to 12
4066   t     Number of days in the given month                                         28 to 31
4067   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
4068   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
4069         belongs to the previous or next year, that year is used instead)
4070   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
4071   y     A two digit representation of a year                                      Examples: 99 or 03
4072   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
4073   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
4074   g     12-hour format of an hour without leading zeros                           1 to 12
4075   G     24-hour format of an hour without leading zeros                           0 to 23
4076   h     12-hour format of an hour with leading zeros                              01 to 12
4077   H     24-hour format of an hour with leading zeros                              00 to 23
4078   i     Minutes, with leading zeros                                               00 to 59
4079   s     Seconds, with leading zeros                                               00 to 59
4080   u     Decimal fraction of a second                                              Examples:
4081         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
4082                                                                                   100 (i.e. 0.100s) or
4083                                                                                   999 (i.e. 0.999s) or
4084                                                                                   999876543210 (i.e. 0.999876543210s)
4085   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
4086   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
4087   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
4088   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
4089   c     ISO 8601 date
4090         Notes:                                                                    Examples:
4091         1) If unspecified, the month / day defaults to the current month / day,   1991 or
4092            the time defaults to midnight, while the timezone defaults to the      1992-10 or
4093            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
4094            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
4095            are optional.                                                          1995-07-18T17:21:28-02:00 or
4096         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
4097            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
4098            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
4099         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
4100         date-time granularity which are supported, or see                         2000-02-13T21:25:33
4101         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
4102   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
4103   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
4104                                                                                   \/Date(1238606590509+0800)\/
4105 </pre>
4106  *
4107  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
4108  * <pre><code>
4109 // Sample date:
4110 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
4111
4112 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
4113 document.write(dt.format('Y-m-d'));                           // 2007-01-10
4114 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
4115 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
4116 </code></pre>
4117  *
4118  * Here are some standard date/time patterns that you might find helpful.  They
4119  * are not part of the source of Date.js, but to use them you can simply copy this
4120  * block of code into any script that is included after Date.js and they will also become
4121  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
4122  * <pre><code>
4123 Date.patterns = {
4124     ISO8601Long:"Y-m-d H:i:s",
4125     ISO8601Short:"Y-m-d",
4126     ShortDate: "n/j/Y",
4127     LongDate: "l, F d, Y",
4128     FullDateTime: "l, F d, Y g:i:s A",
4129     MonthDay: "F d",
4130     ShortTime: "g:i A",
4131     LongTime: "g:i:s A",
4132     SortableDateTime: "Y-m-d\\TH:i:s",
4133     UniversalSortableDateTime: "Y-m-d H:i:sO",
4134     YearMonth: "F, Y"
4135 };
4136 </code></pre>
4137  *
4138  * Example usage:
4139  * <pre><code>
4140 var dt = new Date();
4141 document.write(dt.format(Date.patterns.ShortDate));
4142 </code></pre>
4143  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
4144  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
4145  */
4146
4147 /*
4148  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
4149  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
4150  * They generate precompiled functions from format patterns instead of parsing and
4151  * processing each pattern every time a date is formatted. These functions are available
4152  * on every Date object.
4153  */
4154
4155 (function() {
4156
4157 /**
4158  * Global flag which determines if strict date parsing should be used.
4159  * Strict date parsing will not roll-over invalid dates, which is the
4160  * default behaviour of javascript Date objects.
4161  * (see {@link #parseDate} for more information)
4162  * Defaults to <tt>false</tt>.
4163  * @static
4164  * @type Boolean
4165 */
4166 Date.useStrict = false;
4167
4168
4169 // create private copy of Ext's String.format() method
4170 // - to remove unnecessary dependency
4171 // - to resolve namespace conflict with M$-Ajax's implementation
4172 function xf(format) {
4173     var args = Array.prototype.slice.call(arguments, 1);
4174     return format.replace(/\{(\d+)\}/g, function(m, i) {
4175         return args[i];
4176     });
4177 }
4178
4179
4180 // private
4181 Date.formatCodeToRegex = function(character, currentGroup) {
4182     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
4183     var p = Date.parseCodes[character];
4184
4185     if (p) {
4186       p = typeof p == 'function'? p() : p;
4187       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
4188     }
4189
4190     return p ? Ext.applyIf({
4191       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
4192     }, p) : {
4193         g:0,
4194         c:null,
4195         s:Ext.escapeRe(character) // treat unrecognised characters as literals
4196     };
4197 };
4198
4199 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
4200 var $f = Date.formatCodeToRegex;
4201
4202 Ext.apply(Date, {
4203     /**
4204      * <p>An object hash in which each property is a date parsing function. The property name is the
4205      * format string which that function parses.</p>
4206      * <p>This object is automatically populated with date parsing functions as
4207      * date formats are requested for Ext standard formatting strings.</p>
4208      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
4209      * may be used as a format string to {@link #parseDate}.<p>
4210      * <p>Example:</p><pre><code>
4211 Date.parseFunctions['x-date-format'] = myDateParser;
4212 </code></pre>
4213      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
4214      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
4215      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
4216      * (i.e. prevent javascript Date "rollover") (The default must be false).
4217      * Invalid date strings should return null when parsed.</div></li>
4218      * </ul></div></p>
4219      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
4220      * formatting function must be placed into the {@link #formatFunctions} property.
4221      * @property parseFunctions
4222      * @static
4223      * @type Object
4224      */
4225     parseFunctions: {
4226         "M$": function(input, strict) {
4227             // note: the timezone offset is ignored since the M$ Ajax server sends
4228             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
4229             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
4230             var r = (input || '').match(re);
4231             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
4232         }
4233     },
4234     parseRegexes: [],
4235
4236     /**
4237      * <p>An object hash in which each property is a date formatting function. The property name is the
4238      * format string which corresponds to the produced formatted date string.</p>
4239      * <p>This object is automatically populated with date formatting functions as
4240      * date formats are requested for Ext standard formatting strings.</p>
4241      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
4242      * may be used as a format string to {@link #format}. Example:</p><pre><code>
4243 Date.formatFunctions['x-date-format'] = myDateFormatter;
4244 </code></pre>
4245      * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
4246      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
4247      * </ul></div></p>
4248      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
4249      * parsing function must be placed into the {@link #parseFunctions} property.
4250      * @property formatFunctions
4251      * @static
4252      * @type Object
4253      */
4254     formatFunctions: {
4255         "M$": function() {
4256             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
4257             return '\\/Date(' + this.getTime() + ')\\/';
4258         }
4259     },
4260
4261     y2kYear : 50,
4262
4263     /**
4264      * Date interval constant
4265      * @static
4266      * @type String
4267      */
4268     MILLI : "ms",
4269
4270     /**
4271      * Date interval constant
4272      * @static
4273      * @type String
4274      */
4275     SECOND : "s",
4276
4277     /**
4278      * Date interval constant
4279      * @static
4280      * @type String
4281      */
4282     MINUTE : "mi",
4283
4284     /** Date interval constant
4285      * @static
4286      * @type String
4287      */
4288     HOUR : "h",
4289
4290     /**
4291      * Date interval constant
4292      * @static
4293      * @type String
4294      */
4295     DAY : "d",
4296
4297     /**
4298      * Date interval constant
4299      * @static
4300      * @type String
4301      */
4302     MONTH : "mo",
4303
4304     /**
4305      * Date interval constant
4306      * @static
4307      * @type String
4308      */
4309     YEAR : "y",
4310
4311     /**
4312      * <p>An object hash containing default date values used during date parsing.</p>
4313      * <p>The following properties are available:<div class="mdetail-params"><ul>
4314      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
4315      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
4316      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
4317      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
4318      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
4319      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
4320      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
4321      * </ul></div></p>
4322      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
4323      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
4324      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
4325      * It is the responsiblity of the developer to account for this.</b></p>
4326      * Example Usage:
4327      * <pre><code>
4328 // set default day value to the first day of the month
4329 Date.defaults.d = 1;
4330
4331 // parse a February date string containing only year and month values.
4332 // setting the default day value to 1 prevents weird date rollover issues
4333 // when attempting to parse the following date string on, for example, March 31st 2009.
4334 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
4335 </code></pre>
4336      * @property defaults
4337      * @static
4338      * @type Object
4339      */
4340     defaults: {},
4341
4342     /**
4343      * An array of textual day names.
4344      * Override these values for international dates.
4345      * Example:
4346      * <pre><code>
4347 Date.dayNames = [
4348     'SundayInYourLang',
4349     'MondayInYourLang',
4350     ...
4351 ];
4352 </code></pre>
4353      * @type Array
4354      * @static
4355      */
4356     dayNames : [
4357         "Sunday",
4358         "Monday",
4359         "Tuesday",
4360         "Wednesday",
4361         "Thursday",
4362         "Friday",
4363         "Saturday"
4364     ],
4365
4366     /**
4367      * An array of textual month names.
4368      * Override these values for international dates.
4369      * Example:
4370      * <pre><code>
4371 Date.monthNames = [
4372     'JanInYourLang',
4373     'FebInYourLang',
4374     ...
4375 ];
4376 </code></pre>
4377      * @type Array
4378      * @static
4379      */
4380     monthNames : [
4381         "January",
4382         "February",
4383         "March",
4384         "April",
4385         "May",
4386         "June",
4387         "July",
4388         "August",
4389         "September",
4390         "October",
4391         "November",
4392         "December"
4393     ],
4394
4395     /**
4396      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
4397      * Override these values for international dates.
4398      * Example:
4399      * <pre><code>
4400 Date.monthNumbers = {
4401     'ShortJanNameInYourLang':0,
4402     'ShortFebNameInYourLang':1,
4403     ...
4404 };
4405 </code></pre>
4406      * @type Object
4407      * @static
4408      */
4409     monthNumbers : {
4410         Jan:0,
4411         Feb:1,
4412         Mar:2,
4413         Apr:3,
4414         May:4,
4415         Jun:5,
4416         Jul:6,
4417         Aug:7,
4418         Sep:8,
4419         Oct:9,
4420         Nov:10,
4421         Dec:11
4422     },
4423
4424     /**
4425      * Get the short month name for the given month number.
4426      * Override this function for international dates.
4427      * @param {Number} month A zero-based javascript month number.
4428      * @return {String} The short month name.
4429      * @static
4430      */
4431     getShortMonthName : function(month) {
4432         return Date.monthNames[month].substring(0, 3);
4433     },
4434
4435     /**
4436      * Get the short day name for the given day number.
4437      * Override this function for international dates.
4438      * @param {Number} day A zero-based javascript day number.
4439      * @return {String} The short day name.
4440      * @static
4441      */
4442     getShortDayName : function(day) {
4443         return Date.dayNames[day].substring(0, 3);
4444     },
4445
4446     /**
4447      * Get the zero-based javascript month number for the given short/full month name.
4448      * Override this function for international dates.
4449      * @param {String} name The short/full month name.
4450      * @return {Number} The zero-based javascript month number.
4451      * @static
4452      */
4453     getMonthNumber : function(name) {
4454         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
4455         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
4456     },
4457
4458     /**
4459      * The base format-code to formatting-function hashmap used by the {@link #format} method.
4460      * Formatting functions are strings (or functions which return strings) which
4461      * will return the appropriate value when evaluated in the context of the Date object
4462      * from which the {@link #format} method is called.
4463      * Add to / override these mappings for custom date formatting.
4464      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
4465      * Example:
4466      * <pre><code>
4467 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
4468 (new Date()).format("X"); // returns the current day of the month
4469 </code></pre>
4470      * @type Object
4471      * @static
4472      */
4473     formatCodes : {
4474         d: "String.leftPad(this.getDate(), 2, '0')",
4475         D: "Date.getShortDayName(this.getDay())", // get localised short day name
4476         j: "this.getDate()",
4477         l: "Date.dayNames[this.getDay()]",
4478         N: "(this.getDay() ? this.getDay() : 7)",
4479         S: "this.getSuffix()",
4480         w: "this.getDay()",
4481         z: "this.getDayOfYear()",
4482         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
4483         F: "Date.monthNames[this.getMonth()]",
4484         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
4485         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
4486         n: "(this.getMonth() + 1)",
4487         t: "this.getDaysInMonth()",
4488         L: "(this.isLeapYear() ? 1 : 0)",
4489         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
4490         Y: "String.leftPad(this.getFullYear(), 4, '0')",
4491         y: "('' + this.getFullYear()).substring(2, 4)",
4492         a: "(this.getHours() < 12 ? 'am' : 'pm')",
4493         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
4494         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
4495         G: "this.getHours()",
4496         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
4497         H: "String.leftPad(this.getHours(), 2, '0')",
4498         i: "String.leftPad(this.getMinutes(), 2, '0')",
4499         s: "String.leftPad(this.getSeconds(), 2, '0')",
4500         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
4501         O: "this.getGMTOffset()",
4502         P: "this.getGMTOffset(true)",
4503         T: "this.getTimezone()",
4504         Z: "(this.getTimezoneOffset() * -60)",
4505
4506         c: function() { // ISO-8601 -- GMT format
4507             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
4508                 var e = c.charAt(i);
4509                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
4510             }
4511             return code.join(" + ");
4512         },
4513         /*
4514         c: function() { // ISO-8601 -- UTC format
4515             return [
4516               "this.getUTCFullYear()", "'-'",
4517               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
4518               "String.leftPad(this.getUTCDate(), 2, '0')",
4519               "'T'",
4520               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
4521               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
4522               "String.leftPad(this.getUTCSeconds(), 2, '0')",
4523               "'Z'"
4524             ].join(" + ");
4525         },
4526         */
4527
4528         U: "Math.round(this.getTime() / 1000)"
4529     },
4530
4531     /**
4532      * Checks if the passed Date parameters will cause a javascript Date "rollover".
4533      * @param {Number} year 4-digit year
4534      * @param {Number} month 1-based month-of-year
4535      * @param {Number} day Day of month
4536      * @param {Number} hour (optional) Hour
4537      * @param {Number} minute (optional) Minute
4538      * @param {Number} second (optional) Second
4539      * @param {Number} millisecond (optional) Millisecond
4540      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
4541      * @static
4542      */
4543     isValid : function(y, m, d, h, i, s, ms) {
4544         // setup defaults
4545         h = h || 0;
4546         i = i || 0;
4547         s = s || 0;
4548         ms = ms || 0;
4549
4550         // Special handling for year < 100
4551         var dt = new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);
4552
4553         return y == dt.getFullYear() &&
4554             m == dt.getMonth() + 1 &&
4555             d == dt.getDate() &&
4556             h == dt.getHours() &&
4557             i == dt.getMinutes() &&
4558             s == dt.getSeconds() &&
4559             ms == dt.getMilliseconds();
4560     },
4561
4562     /**
4563      * Parses the passed string using the specified date format.
4564      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
4565      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
4566      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
4567      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
4568      * Keep in mind that the input date string must precisely match the specified format string
4569      * in order for the parse operation to be successful (failed parse operations return a null value).
4570      * <p>Example:</p><pre><code>
4571 //dt = Fri May 25 2007 (current date)
4572 var dt = new Date();
4573
4574 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
4575 dt = Date.parseDate("2006", "Y");
4576
4577 //dt = Sun Jan 15 2006 (all date parts specified)
4578 dt = Date.parseDate("2006-01-15", "Y-m-d");
4579
4580 //dt = Sun Jan 15 2006 15:20:01
4581 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
4582
4583 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
4584 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
4585 </code></pre>
4586      * @param {String} input The raw date string.
4587      * @param {String} format The expected date string format.
4588      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
4589                         (defaults to false). Invalid date strings will return null when parsed.
4590      * @return {Date} The parsed Date.
4591      * @static
4592      */
4593     parseDate : function(input, format, strict) {
4594         var p = Date.parseFunctions;
4595         if (p[format] == null) {
4596             Date.createParser(format);
4597         }
4598         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
4599     },
4600
4601     // private
4602     getFormatCode : function(character) {
4603         var f = Date.formatCodes[character];
4604
4605         if (f) {
4606           f = typeof f == 'function'? f() : f;
4607           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
4608         }
4609
4610         // note: unknown characters are treated as literals
4611         return f || ("'" + String.escape(character) + "'");
4612     },
4613
4614     // private
4615     createFormat : function(format) {
4616         var code = [],
4617             special = false,
4618             ch = '';
4619
4620         for (var i = 0; i < format.length; ++i) {
4621             ch = format.charAt(i);
4622             if (!special && ch == "\\") {
4623                 special = true;
4624             } else if (special) {
4625                 special = false;
4626                 code.push("'" + String.escape(ch) + "'");
4627             } else {
4628                 code.push(Date.getFormatCode(ch));
4629             }
4630         }
4631         Date.formatFunctions[format] = new Function("return " + code.join('+'));
4632     },
4633
4634     // private
4635     createParser : function() {
4636         var code = [
4637             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
4638                 "def = Date.defaults,",
4639                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
4640
4641             "if(results){",
4642                 "{1}",
4643
4644                 "if(u != null){", // i.e. unix time is defined
4645                     "v = new Date(u * 1000);", // give top priority to UNIX time
4646                 "}else{",
4647                     // create Date object representing midnight of the current day;
4648                     // this will provide us with our date defaults
4649                     // (note: clearTime() handles Daylight Saving Time automatically)
4650                     "dt = (new Date()).clearTime();",
4651
4652                     // date calculations (note: these calculations create a dependency on Ext.num())
4653                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
4654                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
4655                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
4656
4657                     // time calculations (note: these calculations create a dependency on Ext.num())
4658                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
4659                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
4660                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
4661                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
4662
4663                     "if(z >= 0 && y >= 0){",
4664                         // both the year and zero-based day of year are defined and >= 0.
4665                         // these 2 values alone provide sufficient info to create a full date object
4666
4667                         // create Date object representing January 1st for the given year
4668                         // handle years < 100 appropriately
4669                         "v = new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
4670
4671                         // then add day of year, checking for Date "rollover" if necessary
4672                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
4673                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
4674                         "v = null;", // invalid date, so return null
4675                     "}else{",
4676                         // plain old Date object
4677                         // handle years < 100 properly
4678                         "v = new Date(y < 100 ? 100 : y, m, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
4679                     "}",
4680                 "}",
4681             "}",
4682
4683             "if(v){",
4684                 // favour UTC offset over GMT offset
4685                 "if(zz != null){",
4686                     // reset to UTC, then add offset
4687                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
4688                 "}else if(o){",
4689                     // reset to GMT, then add offset
4690                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
4691                 "}",
4692             "}",
4693
4694             "return v;"
4695         ].join('\n');
4696
4697         return function(format) {
4698             var regexNum = Date.parseRegexes.length,
4699                 currentGroup = 1,
4700                 calc = [],
4701                 regex = [],
4702                 special = false,
4703                 ch = "";
4704
4705             for (var i = 0; i < format.length; ++i) {
4706                 ch = format.charAt(i);
4707                 if (!special && ch == "\\") {
4708                     special = true;
4709                 } else if (special) {
4710                     special = false;
4711                     regex.push(String.escape(ch));
4712                 } else {
4713                     var obj = $f(ch, currentGroup);
4714                     currentGroup += obj.g;
4715                     regex.push(obj.s);
4716                     if (obj.g && obj.c) {
4717                         calc.push(obj.c);
4718                     }
4719                 }
4720             }
4721
4722             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4723             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
4724         };
4725     }(),
4726
4727     // private
4728     parseCodes : {
4729         /*
4730          * Notes:
4731          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4732          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4733          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4734          */
4735         d: {
4736             g:1,
4737             c:"d = parseInt(results[{0}], 10);\n",
4738             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4739         },
4740         j: {
4741             g:1,
4742             c:"d = parseInt(results[{0}], 10);\n",
4743             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4744         },
4745         D: function() {
4746             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
4747             return {
4748                 g:0,
4749                 c:null,
4750                 s:"(?:" + a.join("|") +")"
4751             };
4752         },
4753         l: function() {
4754             return {
4755                 g:0,
4756                 c:null,
4757                 s:"(?:" + Date.dayNames.join("|") + ")"
4758             };
4759         },
4760         N: {
4761             g:0,
4762             c:null,
4763             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4764         },
4765         S: {
4766             g:0,
4767             c:null,
4768             s:"(?:st|nd|rd|th)"
4769         },
4770         w: {
4771             g:0,
4772             c:null,
4773             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4774         },
4775         z: {
4776             g:1,
4777             c:"z = parseInt(results[{0}], 10);\n",
4778             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4779         },
4780         W: {
4781             g:0,
4782             c:null,
4783             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4784         },
4785         F: function() {
4786             return {
4787                 g:1,
4788                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4789                 s:"(" + Date.monthNames.join("|") + ")"
4790             };
4791         },
4792         M: function() {
4793             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
4794             return Ext.applyIf({
4795                 s:"(" + a.join("|") + ")"
4796             }, $f("F"));
4797         },
4798         m: {
4799             g:1,
4800             c:"m = parseInt(results[{0}], 10) - 1;\n",
4801             s:"(\\d{2})" // month number with leading zeros (01 - 12)
4802         },
4803         n: {
4804             g:1,
4805             c:"m = parseInt(results[{0}], 10) - 1;\n",
4806             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4807         },
4808         t: {
4809             g:0,
4810             c:null,
4811             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4812         },
4813         L: {
4814             g:0,
4815             c:null,
4816             s:"(?:1|0)"
4817         },
4818         o: function() {
4819             return $f("Y");
4820         },
4821         Y: {
4822             g:1,
4823             c:"y = parseInt(results[{0}], 10);\n",
4824             s:"(\\d{4})" // 4-digit year
4825         },
4826         y: {
4827             g:1,
4828             c:"var ty = parseInt(results[{0}], 10);\n"
4829                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4830             s:"(\\d{1,2})"
4831         },
4832         /**
4833          * In the am/pm parsing routines, we allow both upper and lower case 
4834          * even though it doesn't exactly match the spec. It gives much more flexibility
4835          * in being able to specify case insensitive regexes.
4836          */
4837         a: {
4838             g:1,
4839             c:"if (/(am)/i.test(results[{0}])) {\n"
4840                 + "if (!h || h == 12) { h = 0; }\n"
4841                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4842             s:"(am|pm|AM|PM)"
4843         },
4844         A: {
4845             g:1,
4846             c:"if (/(am)/i.test(results[{0}])) {\n"
4847                 + "if (!h || h == 12) { h = 0; }\n"
4848                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4849             s:"(AM|PM|am|pm)"
4850         },
4851         g: function() {
4852             return $f("G");
4853         },
4854         G: {
4855             g:1,
4856             c:"h = parseInt(results[{0}], 10);\n",
4857             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4858         },
4859         h: function() {
4860             return $f("H");
4861         },
4862         H: {
4863             g:1,
4864             c:"h = parseInt(results[{0}], 10);\n",
4865             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
4866         },
4867         i: {
4868             g:1,
4869             c:"i = parseInt(results[{0}], 10);\n",
4870             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4871         },
4872         s: {
4873             g:1,
4874             c:"s = parseInt(results[{0}], 10);\n",
4875             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4876         },
4877         u: {
4878             g:1,
4879             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4880             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4881         },
4882         O: {
4883             g:1,
4884             c:[
4885                 "o = results[{0}];",
4886                 "var sn = o.substring(0,1),", // get + / - sign
4887                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4888                     "mn = o.substring(3,5) % 60;", // get minutes
4889                 "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
4890             ].join("\n"),
4891             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4892         },
4893         P: {
4894             g:1,
4895             c:[
4896                 "o = results[{0}];",
4897                 "var sn = o.substring(0,1),", // get + / - sign
4898                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4899                     "mn = o.substring(4,6) % 60;", // get minutes
4900                 "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
4901             ].join("\n"),
4902             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4903         },
4904         T: {
4905             g:0,
4906             c:null,
4907             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4908         },
4909         Z: {
4910             g:1,
4911             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4912                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4913             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4914         },
4915         c: function() {
4916             var calc = [],
4917                 arr = [
4918                     $f("Y", 1), // year
4919                     $f("m", 2), // month
4920                     $f("d", 3), // day
4921                     $f("h", 4), // hour
4922                     $f("i", 5), // minute
4923                     $f("s", 6), // second
4924                     {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)
4925                     {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
4926                         "if(results[8]) {", // timezone specified
4927                             "if(results[8] == 'Z'){",
4928                                 "zz = 0;", // UTC
4929                             "}else if (results[8].indexOf(':') > -1){",
4930                                 $f("P", 8).c, // timezone offset with colon separator
4931                             "}else{",
4932                                 $f("O", 8).c, // timezone offset without colon separator
4933                             "}",
4934                         "}"
4935                     ].join('\n')}
4936                 ];
4937
4938             for (var i = 0, l = arr.length; i < l; ++i) {
4939                 calc.push(arr[i].c);
4940             }
4941
4942             return {
4943                 g:1,
4944                 c:calc.join(""),
4945                 s:[
4946                     arr[0].s, // year (required)
4947                     "(?:", "-", arr[1].s, // month (optional)
4948                         "(?:", "-", arr[2].s, // day (optional)
4949                             "(?:",
4950                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4951                                 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
4952                                 "(?::", arr[5].s, ")?", // seconds (optional)
4953                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4954                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4955                             ")?",
4956                         ")?",
4957                     ")?"
4958                 ].join("")
4959             };
4960         },
4961         U: {
4962             g:1,
4963             c:"u = parseInt(results[{0}], 10);\n",
4964             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4965         }
4966     }
4967 });
4968
4969 }());
4970
4971 Ext.apply(Date.prototype, {
4972     // private
4973     dateFormat : function(format) {
4974         if (Date.formatFunctions[format] == null) {
4975             Date.createFormat(format);
4976         }
4977         return Date.formatFunctions[format].call(this);
4978     },
4979
4980     /**
4981      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4982      *
4983      * Note: The date string returned by the javascript Date object's toString() method varies
4984      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4985      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4986      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4987      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4988      * from the GMT offset portion of the date string.
4989      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4990      */
4991     getTimezone : function() {
4992         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4993         //
4994         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4995         // 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)
4996         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4997         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4998         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4999         //
5000         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
5001         // step 1: (?:\((.*)\) -- find timezone in parentheses
5002         // 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
5003         // step 3: remove all non uppercase characters found in step 1 and 2
5004         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
5005     },
5006
5007     /**
5008      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
5009      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
5010      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
5011      */
5012     getGMTOffset : function(colon) {
5013         return (this.getTimezoneOffset() > 0 ? "-" : "+")
5014             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
5015             + (colon ? ":" : "")
5016             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
5017     },
5018
5019     /**
5020      * Get the numeric day number of the year, adjusted for leap year.
5021      * @return {Number} 0 to 364 (365 in leap years).
5022      */
5023     getDayOfYear: function() {
5024         var num = 0,
5025             d = this.clone(),
5026             m = this.getMonth(),
5027             i;
5028
5029         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
5030             num += d.getDaysInMonth();
5031         }
5032         return num + this.getDate() - 1;
5033     },
5034
5035     /**
5036      * Get the numeric ISO-8601 week number of the year.
5037      * (equivalent to the format specifier 'W', but without a leading zero).
5038      * @return {Number} 1 to 53
5039      */
5040     getWeekOfYear : function() {
5041         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
5042         var ms1d = 864e5, // milliseconds in a day
5043             ms7d = 7 * ms1d; // milliseconds in a week
5044
5045         return function() { // return a closure so constants get calculated only once
5046             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
5047                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
5048                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
5049
5050             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
5051         };
5052     }(),
5053
5054     /**
5055      * Checks if the current date falls within a leap year.
5056      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
5057      */
5058     isLeapYear : function() {
5059         var year = this.getFullYear();
5060         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
5061     },
5062
5063     /**
5064      * Get the first day of the current month, adjusted for leap year.  The returned value
5065      * is the numeric day index within the week (0-6) which can be used in conjunction with
5066      * the {@link #monthNames} array to retrieve the textual day name.
5067      * Example:
5068      * <pre><code>
5069 var dt = new Date('1/10/2007');
5070 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
5071 </code></pre>
5072      * @return {Number} The day number (0-6).
5073      */
5074     getFirstDayOfMonth : function() {
5075         var day = (this.getDay() - (this.getDate() - 1)) % 7;
5076         return (day < 0) ? (day + 7) : day;
5077     },
5078
5079     /**
5080      * Get the last day of the current month, adjusted for leap year.  The returned value
5081      * is the numeric day index within the week (0-6) which can be used in conjunction with
5082      * the {@link #monthNames} array to retrieve the textual day name.
5083      * Example:
5084      * <pre><code>
5085 var dt = new Date('1/10/2007');
5086 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
5087 </code></pre>
5088      * @return {Number} The day number (0-6).
5089      */
5090     getLastDayOfMonth : function() {
5091         return this.getLastDateOfMonth().getDay();
5092     },
5093
5094
5095     /**
5096      * Get the date of the first day of the month in which this date resides.
5097      * @return {Date}
5098      */
5099     getFirstDateOfMonth : function() {
5100         return new Date(this.getFullYear(), this.getMonth(), 1);
5101     },
5102
5103     /**
5104      * Get the date of the last day of the month in which this date resides.
5105      * @return {Date}
5106      */
5107     getLastDateOfMonth : function() {
5108         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
5109     },
5110
5111     /**
5112      * Get the number of days in the current month, adjusted for leap year.
5113      * @return {Number} The number of days in the month.
5114      */
5115     getDaysInMonth: function() {
5116         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5117
5118         return function() { // return a closure for efficiency
5119             var m = this.getMonth();
5120
5121             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
5122         };
5123     }(),
5124
5125     /**
5126      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
5127      * @return {String} 'st, 'nd', 'rd' or 'th'.
5128      */
5129     getSuffix : function() {
5130         switch (this.getDate()) {
5131             case 1:
5132             case 21:
5133             case 31:
5134                 return "st";
5135             case 2:
5136             case 22:
5137                 return "nd";
5138             case 3:
5139             case 23:
5140                 return "rd";
5141             default:
5142                 return "th";
5143         }
5144     },
5145
5146     /**
5147      * Creates and returns a new Date instance with the exact same date value as the called instance.
5148      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
5149      * variable will also be changed.  When the intention is to create a new variable that will not
5150      * modify the original instance, you should create a clone.
5151      *
5152      * Example of correctly cloning a date:
5153      * <pre><code>
5154 //wrong way:
5155 var orig = new Date('10/1/2006');
5156 var copy = orig;
5157 copy.setDate(5);
5158 document.write(orig);  //returns 'Thu Oct 05 2006'!
5159
5160 //correct way:
5161 var orig = new Date('10/1/2006');
5162 var copy = orig.clone();
5163 copy.setDate(5);
5164 document.write(orig);  //returns 'Thu Oct 01 2006'
5165 </code></pre>
5166      * @return {Date} The new Date instance.
5167      */
5168     clone : function() {
5169         return new Date(this.getTime());
5170     },
5171
5172     /**
5173      * Checks if the current date is affected by Daylight Saving Time (DST).
5174      * @return {Boolean} True if the current date is affected by DST.
5175      */
5176     isDST : function() {
5177         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
5178         // courtesy of @geoffrey.mcgill
5179         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
5180     },
5181
5182     /**
5183      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
5184      * automatically adjusting for Daylight Saving Time (DST) where applicable.
5185      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
5186      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
5187      * @return {Date} this or the clone.
5188      */
5189     clearTime : function(clone) {
5190         if (clone) {
5191             return this.clone().clearTime();
5192         }
5193
5194         // get current date before clearing time
5195         var d = this.getDate();
5196
5197         // clear time
5198         this.setHours(0);
5199         this.setMinutes(0);
5200         this.setSeconds(0);
5201         this.setMilliseconds(0);
5202
5203         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
5204             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
5205             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
5206
5207             // increment hour until cloned date == current date
5208             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
5209
5210             this.setDate(d);
5211             this.setHours(c.getHours());
5212         }
5213
5214         return this;
5215     },
5216
5217     /**
5218      * Provides a convenient method for performing basic date arithmetic. This method
5219      * does not modify the Date instance being called - it creates and returns
5220      * a new Date instance containing the resulting date value.
5221      *
5222      * Examples:
5223      * <pre><code>
5224 // Basic usage:
5225 var dt = new Date('10/29/2006').add(Date.DAY, 5);
5226 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
5227
5228 // Negative values will be subtracted:
5229 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
5230 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
5231
5232 // You can even chain several calls together in one line:
5233 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
5234 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
5235 </code></pre>
5236      *
5237      * @param {String} interval A valid date interval enum value.
5238      * @param {Number} value The amount to add to the current date.
5239      * @return {Date} The new Date instance.
5240      */
5241     add : function(interval, value) {
5242         var d = this.clone();
5243         if (!interval || value === 0) return d;
5244
5245         switch(interval.toLowerCase()) {
5246             case Date.MILLI:
5247                 d.setMilliseconds(this.getMilliseconds() + value);
5248                 break;
5249             case Date.SECOND:
5250                 d.setSeconds(this.getSeconds() + value);
5251                 break;
5252             case Date.MINUTE:
5253                 d.setMinutes(this.getMinutes() + value);
5254                 break;
5255             case Date.HOUR:
5256                 d.setHours(this.getHours() + value);
5257                 break;
5258             case Date.DAY:
5259                 d.setDate(this.getDate() + value);
5260                 break;
5261             case Date.MONTH:
5262                 var day = this.getDate();
5263                 if (day > 28) {
5264                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
5265                 }
5266                 d.setDate(day);
5267                 d.setMonth(this.getMonth() + value);
5268                 break;
5269             case Date.YEAR:
5270                 d.setFullYear(this.getFullYear() + value);
5271                 break;
5272         }
5273         return d;
5274     },
5275
5276     /**
5277      * Checks if this date falls on or between the given start and end dates.
5278      * @param {Date} start Start date
5279      * @param {Date} end End date
5280      * @return {Boolean} true if this date falls on or between the given start and end dates.
5281      */
5282     between : function(start, end) {
5283         var t = this.getTime();
5284         return start.getTime() <= t && t <= end.getTime();
5285     }
5286 });
5287
5288
5289 /**
5290  * Formats a date given the supplied format string.
5291  * @param {String} format The format string.
5292  * @return {String} The formatted date.
5293  * @method format
5294  */
5295 Date.prototype.format = Date.prototype.dateFormat;
5296
5297
5298 // private
5299 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
5300     Ext.apply(Date.prototype, {
5301         _xMonth : Date.prototype.setMonth,
5302         _xDate  : Date.prototype.setDate,
5303
5304         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
5305         // Date.setMonth does not work consistently if iMonth is not 0-11
5306         setMonth : function(num) {
5307             if (num <= -1) {
5308                 var n = Math.ceil(-num),
5309                     back_year = Math.ceil(n / 12),
5310                     month = (n % 12) ? 12 - n % 12 : 0;
5311
5312                 this.setFullYear(this.getFullYear() - back_year);
5313
5314                 return this._xMonth(month);
5315             } else {
5316                 return this._xMonth(num);
5317             }
5318         },
5319
5320         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
5321         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
5322         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
5323         setDate : function(d) {
5324             // use setTime() to workaround setDate() bug
5325             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
5326             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
5327         }
5328     });
5329 }
5330
5331
5332
5333 /* Some basic Date tests... (requires Firebug)
5334
5335 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
5336 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
5337
5338 // standard tests
5339 console.group('Standard Date.parseDate() Tests');
5340     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
5341     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
5342     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
5343     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
5344     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
5345 console.groupEnd();
5346
5347 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
5348 // -- accepts ALL 6 levels of date-time granularity
5349 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
5350     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
5351     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
5352     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
5353     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)
5354     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)
5355     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)
5356     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)
5357     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
5358 console.groupEnd();
5359
5360 */
5361 /**
5362  * @class Ext.util.MixedCollection
5363  * @extends Ext.util.Observable
5364  * A Collection class that maintains both numeric indexes and keys and exposes events.
5365  * @constructor
5366  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5367  * function should add function references to the collection. Defaults to
5368  * <tt>false</tt>.
5369  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
5370  * and return the key value for that item.  This is used when available to look up the key on items that
5371  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
5372  * equivalent to providing an implementation for the {@link #getKey} method.
5373  */
5374 Ext.util.MixedCollection = function(allowFunctions, keyFn){
5375     this.items = [];
5376     this.map = {};
5377     this.keys = [];
5378     this.length = 0;
5379     this.addEvents(
5380         /**
5381          * @event clear
5382          * Fires when the collection is cleared.
5383          */
5384         'clear',
5385         /**
5386          * @event add
5387          * Fires when an item is added to the collection.
5388          * @param {Number} index The index at which the item was added.
5389          * @param {Object} o The item added.
5390          * @param {String} key The key associated with the added item.
5391          */
5392         'add',
5393         /**
5394          * @event replace
5395          * Fires when an item is replaced in the collection.
5396          * @param {String} key he key associated with the new added.
5397          * @param {Object} old The item being replaced.
5398          * @param {Object} new The new item.
5399          */
5400         'replace',
5401         /**
5402          * @event remove
5403          * Fires when an item is removed from the collection.
5404          * @param {Object} o The item being removed.
5405          * @param {String} key (optional) The key associated with the removed item.
5406          */
5407         'remove',
5408         'sort'
5409     );
5410     this.allowFunctions = allowFunctions === true;
5411     if(keyFn){
5412         this.getKey = keyFn;
5413     }
5414     Ext.util.MixedCollection.superclass.constructor.call(this);
5415 };
5416
5417 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
5418
5419     /**
5420      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5421      * function should add function references to the collection. Defaults to
5422      * <tt>false</tt>.
5423      */
5424     allowFunctions : false,
5425
5426     /**
5427      * Adds an item to the collection. Fires the {@link #add} event when complete.
5428      * @param {String} key <p>The key to associate with the item, or the new item.</p>
5429      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
5430      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
5431      * the MixedCollection will be able to <i>derive</i> the key for the new item.
5432      * In this case just pass the new item in this parameter.</p>
5433      * @param {Object} o The item to add.
5434      * @return {Object} The item added.
5435      */
5436     add : function(key, o){
5437         if(arguments.length == 1){
5438             o = arguments[0];
5439             key = this.getKey(o);
5440         }
5441         if(typeof key != 'undefined' && key !== null){
5442             var old = this.map[key];
5443             if(typeof old != 'undefined'){
5444                 return this.replace(key, o);
5445             }
5446             this.map[key] = o;
5447         }
5448         this.length++;
5449         this.items.push(o);
5450         this.keys.push(key);
5451         this.fireEvent('add', this.length-1, o, key);
5452         return o;
5453     },
5454
5455     /**
5456       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
5457       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
5458       * to return a different value as in the following examples:<pre><code>
5459 // normal way
5460 var mc = new Ext.util.MixedCollection();
5461 mc.add(someEl.dom.id, someEl);
5462 mc.add(otherEl.dom.id, otherEl);
5463 //and so on
5464
5465 // using getKey
5466 var mc = new Ext.util.MixedCollection();
5467 mc.getKey = function(el){
5468    return el.dom.id;
5469 };
5470 mc.add(someEl);
5471 mc.add(otherEl);
5472
5473 // or via the constructor
5474 var mc = new Ext.util.MixedCollection(false, function(el){
5475    return el.dom.id;
5476 });
5477 mc.add(someEl);
5478 mc.add(otherEl);
5479      * </code></pre>
5480      * @param {Object} item The item for which to find the key.
5481      * @return {Object} The key for the passed item.
5482      */
5483     getKey : function(o){
5484          return o.id;
5485     },
5486
5487     /**
5488      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
5489      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
5490      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
5491      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
5492      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
5493      * with one having the same key value, then just pass the replacement item in this parameter.</p>
5494      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
5495      * with that key.
5496      * @return {Object}  The new item.
5497      */
5498     replace : function(key, o){
5499         if(arguments.length == 1){
5500             o = arguments[0];
5501             key = this.getKey(o);
5502         }
5503         var old = this.map[key];
5504         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
5505              return this.add(key, o);
5506         }
5507         var index = this.indexOfKey(key);
5508         this.items[index] = o;
5509         this.map[key] = o;
5510         this.fireEvent('replace', key, old, o);
5511         return o;
5512     },
5513
5514     /**
5515      * Adds all elements of an Array or an Object to the collection.
5516      * @param {Object/Array} objs An Object containing properties which will be added
5517      * to the collection, or an Array of values, each of which are added to the collection.
5518      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
5519      * has been set to <tt>true</tt>.
5520      */
5521     addAll : function(objs){
5522         if(arguments.length > 1 || Ext.isArray(objs)){
5523             var args = arguments.length > 1 ? arguments : objs;
5524             for(var i = 0, len = args.length; i < len; i++){
5525                 this.add(args[i]);
5526             }
5527         }else{
5528             for(var key in objs){
5529                 if(this.allowFunctions || typeof objs[key] != 'function'){
5530                     this.add(key, objs[key]);
5531                 }
5532             }
5533         }
5534     },
5535
5536     /**
5537      * Executes the specified function once for every item in the collection, passing the following arguments:
5538      * <div class="mdetail-params"><ul>
5539      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
5540      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
5541      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
5542      * </ul></div>
5543      * The function should return a boolean value. Returning false from the function will stop the iteration.
5544      * @param {Function} fn The function to execute for each item.
5545      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
5546      */
5547     each : function(fn, scope){
5548         var items = [].concat(this.items); // each safe for removal
5549         for(var i = 0, len = items.length; i < len; i++){
5550             if(fn.call(scope || items[i], items[i], i, len) === false){
5551                 break;
5552             }
5553         }
5554     },
5555
5556     /**
5557      * Executes the specified function once for every key in the collection, passing each
5558      * key, and its associated item as the first two parameters.
5559      * @param {Function} fn The function to execute for each item.
5560      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5561      */
5562     eachKey : function(fn, scope){
5563         for(var i = 0, len = this.keys.length; i < len; i++){
5564             fn.call(scope || window, this.keys[i], this.items[i], i, len);
5565         }
5566     },
5567
5568     /**
5569      * Returns the first item in the collection which elicits a true return value from the
5570      * passed selection function.
5571      * @param {Function} fn The selection function to execute for each item.
5572      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5573      * @return {Object} The first item in the collection which returned true from the selection function.
5574      */
5575     find : function(fn, scope){
5576         for(var i = 0, len = this.items.length; i < len; i++){
5577             if(fn.call(scope || window, this.items[i], this.keys[i])){
5578                 return this.items[i];
5579             }
5580         }
5581         return null;
5582     },
5583
5584     /**
5585      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
5586      * @param {Number} index The index to insert the item at.
5587      * @param {String} key The key to associate with the new item, or the item itself.
5588      * @param {Object} o (optional) If the second parameter was a key, the new item.
5589      * @return {Object} The item inserted.
5590      */
5591     insert : function(index, key, o){
5592         if(arguments.length == 2){
5593             o = arguments[1];
5594             key = this.getKey(o);
5595         }
5596         if(this.containsKey(key)){
5597             this.suspendEvents();
5598             this.removeKey(key);
5599             this.resumeEvents();
5600         }
5601         if(index >= this.length){
5602             return this.add(key, o);
5603         }
5604         this.length++;
5605         this.items.splice(index, 0, o);
5606         if(typeof key != 'undefined' && key !== null){
5607             this.map[key] = o;
5608         }
5609         this.keys.splice(index, 0, key);
5610         this.fireEvent('add', index, o, key);
5611         return o;
5612     },
5613
5614     /**
5615      * Remove an item from the collection.
5616      * @param {Object} o The item to remove.
5617      * @return {Object} The item removed or false if no item was removed.
5618      */
5619     remove : function(o){
5620         return this.removeAt(this.indexOf(o));
5621     },
5622
5623     /**
5624      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
5625      * @param {Number} index The index within the collection of the item to remove.
5626      * @return {Object} The item removed or false if no item was removed.
5627      */
5628     removeAt : function(index){
5629         if(index < this.length && index >= 0){
5630             this.length--;
5631             var o = this.items[index];
5632             this.items.splice(index, 1);
5633             var key = this.keys[index];
5634             if(typeof key != 'undefined'){
5635                 delete this.map[key];
5636             }
5637             this.keys.splice(index, 1);
5638             this.fireEvent('remove', o, key);
5639             return o;
5640         }
5641         return false;
5642     },
5643
5644     /**
5645      * Removed an item associated with the passed key fom the collection.
5646      * @param {String} key The key of the item to remove.
5647      * @return {Object} The item removed or false if no item was removed.
5648      */
5649     removeKey : function(key){
5650         return this.removeAt(this.indexOfKey(key));
5651     },
5652
5653     /**
5654      * Returns the number of items in the collection.
5655      * @return {Number} the number of items in the collection.
5656      */
5657     getCount : function(){
5658         return this.length;
5659     },
5660
5661     /**
5662      * Returns index within the collection of the passed Object.
5663      * @param {Object} o The item to find the index of.
5664      * @return {Number} index of the item. Returns -1 if not found.
5665      */
5666     indexOf : function(o){
5667         return this.items.indexOf(o);
5668     },
5669
5670     /**
5671      * Returns index within the collection of the passed key.
5672      * @param {String} key The key to find the index of.
5673      * @return {Number} index of the key.
5674      */
5675     indexOfKey : function(key){
5676         return this.keys.indexOf(key);
5677     },
5678
5679     /**
5680      * Returns the item associated with the passed key OR index.
5681      * Key has priority over index.  This is the equivalent
5682      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
5683      * @param {String/Number} key The key or index of the item.
5684      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
5685      * If an item was found, but is a Class, returns <tt>null</tt>.
5686      */
5687     item : function(key){
5688         var mk = this.map[key],
5689             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
5690         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
5691     },
5692
5693     /**
5694      * Returns the item at the specified index.
5695      * @param {Number} index The index of the item.
5696      * @return {Object} The item at the specified index.
5697      */
5698     itemAt : function(index){
5699         return this.items[index];
5700     },
5701
5702     /**
5703      * Returns the item associated with the passed key.
5704      * @param {String/Number} key The key of the item.
5705      * @return {Object} The item associated with the passed key.
5706      */
5707     key : function(key){
5708         return this.map[key];
5709     },
5710
5711     /**
5712      * Returns true if the collection contains the passed Object as an item.
5713      * @param {Object} o  The Object to look for in the collection.
5714      * @return {Boolean} True if the collection contains the Object as an item.
5715      */
5716     contains : function(o){
5717         return this.indexOf(o) != -1;
5718     },
5719
5720     /**
5721      * Returns true if the collection contains the passed Object as a key.
5722      * @param {String} key The key to look for in the collection.
5723      * @return {Boolean} True if the collection contains the Object as a key.
5724      */
5725     containsKey : function(key){
5726         return typeof this.map[key] != 'undefined';
5727     },
5728
5729     /**
5730      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
5731      */
5732     clear : function(){
5733         this.length = 0;
5734         this.items = [];
5735         this.keys = [];
5736         this.map = {};
5737         this.fireEvent('clear');
5738     },
5739
5740     /**
5741      * Returns the first item in the collection.
5742      * @return {Object} the first item in the collection..
5743      */
5744     first : function(){
5745         return this.items[0];
5746     },
5747
5748     /**
5749      * Returns the last item in the collection.
5750      * @return {Object} the last item in the collection..
5751      */
5752     last : function(){
5753         return this.items[this.length-1];
5754     },
5755
5756     /**
5757      * @private
5758      * Performs the actual sorting based on a direction and a sorting function. Internally,
5759      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
5760      * the sorted array data back into this.items and this.keys
5761      * @param {String} property Property to sort by ('key', 'value', or 'index')
5762      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
5763      * @param {Function} fn (optional) Comparison function that defines the sort order.
5764      * Defaults to sorting by numeric value.
5765      */
5766     _sort : function(property, dir, fn){
5767         var i, len,
5768             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
5769
5770             //this is a temporary array used to apply the sorting function
5771             c     = [],
5772             keys  = this.keys,
5773             items = this.items;
5774
5775         //default to a simple sorter function if one is not provided
5776         fn = fn || function(a, b) {
5777             return a - b;
5778         };
5779
5780         //copy all the items into a temporary array, which we will sort
5781         for(i = 0, len = items.length; i < len; i++){
5782             c[c.length] = {
5783                 key  : keys[i],
5784                 value: items[i],
5785                 index: i
5786             };
5787         }
5788
5789         //sort the temporary array
5790         c.sort(function(a, b){
5791             var v = fn(a[property], b[property]) * dsc;
5792             if(v === 0){
5793                 v = (a.index < b.index ? -1 : 1);
5794             }
5795             return v;
5796         });
5797
5798         //copy the temporary array back into the main this.items and this.keys objects
5799         for(i = 0, len = c.length; i < len; i++){
5800             items[i] = c[i].value;
5801             keys[i]  = c[i].key;
5802         }
5803
5804         this.fireEvent('sort', this);
5805     },
5806
5807     /**
5808      * Sorts this collection by <b>item</b> value with the passed comparison function.
5809      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5810      * @param {Function} fn (optional) Comparison function that defines the sort order.
5811      * Defaults to sorting by numeric value.
5812      */
5813     sort : function(dir, fn){
5814         this._sort('value', dir, fn);
5815     },
5816
5817     /**
5818      * Reorders each of the items based on a mapping from old index to new index. Internally this
5819      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
5820      * @param {Object} mapping Mapping from old item index to new item index
5821      */
5822     reorder: function(mapping) {
5823         this.suspendEvents();
5824
5825         var items = this.items,
5826             index = 0,
5827             length = items.length,
5828             order = [],
5829             remaining = [],
5830             oldIndex;
5831
5832         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
5833         for (oldIndex in mapping) {
5834             order[mapping[oldIndex]] = items[oldIndex];
5835         }
5836
5837         for (index = 0; index < length; index++) {
5838             if (mapping[index] == undefined) {
5839                 remaining.push(items[index]);
5840             }
5841         }
5842
5843         for (index = 0; index < length; index++) {
5844             if (order[index] == undefined) {
5845                 order[index] = remaining.shift();
5846             }
5847         }
5848
5849         this.clear();
5850         this.addAll(order);
5851
5852         this.resumeEvents();
5853         this.fireEvent('sort', this);
5854     },
5855
5856     /**
5857      * Sorts this collection by <b>key</b>s.
5858      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5859      * @param {Function} fn (optional) Comparison function that defines the sort order.
5860      * Defaults to sorting by case insensitive string.
5861      */
5862     keySort : function(dir, fn){
5863         this._sort('key', dir, fn || function(a, b){
5864             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
5865             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5866         });
5867     },
5868
5869     /**
5870      * Returns a range of items in this collection
5871      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
5872      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
5873      * @return {Array} An array of items
5874      */
5875     getRange : function(start, end){
5876         var items = this.items;
5877         if(items.length < 1){
5878             return [];
5879         }
5880         start = start || 0;
5881         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
5882         var i, r = [];
5883         if(start <= end){
5884             for(i = start; i <= end; i++) {
5885                 r[r.length] = items[i];
5886             }
5887         }else{
5888             for(i = start; i >= end; i--) {
5889                 r[r.length] = items[i];
5890             }
5891         }
5892         return r;
5893     },
5894
5895     /**
5896      * Filter the <i>objects</i> in this collection by a specific property.
5897      * Returns a new collection that has been filtered.
5898      * @param {String} property A property on your objects
5899      * @param {String/RegExp} value Either string that the property values
5900      * should start with or a RegExp to test against the property
5901      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
5902      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
5903      * @return {MixedCollection} The new filtered collection
5904      */
5905     filter : function(property, value, anyMatch, caseSensitive){
5906         if(Ext.isEmpty(value, false)){
5907             return this.clone();
5908         }
5909         value = this.createValueMatcher(value, anyMatch, caseSensitive);
5910         return this.filterBy(function(o){
5911             return o && value.test(o[property]);
5912         });
5913     },
5914
5915     /**
5916      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
5917      * The passed function will be called with each object in the collection.
5918      * If the function returns true, the value is included otherwise it is filtered.
5919      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
5920      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5921      * @return {MixedCollection} The new filtered collection
5922      */
5923     filterBy : function(fn, scope){
5924         var r = new Ext.util.MixedCollection();
5925         r.getKey = this.getKey;
5926         var k = this.keys, it = this.items;
5927         for(var i = 0, len = it.length; i < len; i++){
5928             if(fn.call(scope||this, it[i], k[i])){
5929                 r.add(k[i], it[i]);
5930             }
5931         }
5932         return r;
5933     },
5934
5935     /**
5936      * Finds the index of the first matching object in this collection by a specific property/value.
5937      * @param {String} property The name of a property on your objects.
5938      * @param {String/RegExp} value A string that the property values
5939      * should start with or a RegExp to test against the property.
5940      * @param {Number} start (optional) The index to start searching at (defaults to 0).
5941      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
5942      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
5943      * @return {Number} The matched index or -1
5944      */
5945     findIndex : function(property, value, start, anyMatch, caseSensitive){
5946         if(Ext.isEmpty(value, false)){
5947             return -1;
5948         }
5949         value = this.createValueMatcher(value, anyMatch, caseSensitive);
5950         return this.findIndexBy(function(o){
5951             return o && value.test(o[property]);
5952         }, null, start);
5953     },
5954
5955     /**
5956      * Find the index of the first matching object in this collection by a function.
5957      * If the function returns <i>true</i> it is considered a match.
5958      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
5959      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5960      * @param {Number} start (optional) The index to start searching at (defaults to 0).
5961      * @return {Number} The matched index or -1
5962      */
5963     findIndexBy : function(fn, scope, start){
5964         var k = this.keys, it = this.items;
5965         for(var i = (start||0), len = it.length; i < len; i++){
5966             if(fn.call(scope||this, it[i], k[i])){
5967                 return i;
5968             }
5969         }
5970         return -1;
5971     },
5972
5973     /**
5974      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
5975      * and by Ext.data.Store#filter
5976      * @private
5977      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
5978      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
5979      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
5980      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
5981      */
5982     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
5983         if (!value.exec) { // not a regex
5984             var er = Ext.escapeRe;
5985             value = String(value);
5986
5987             if (anyMatch === true) {
5988                 value = er(value);
5989             } else {
5990                 value = '^' + er(value);
5991                 if (exactMatch === true) {
5992                     value += '$';
5993                 }
5994             }
5995             value = new RegExp(value, caseSensitive ? '' : 'i');
5996          }
5997          return value;
5998     },
5999
6000     /**
6001      * Creates a shallow copy of this collection
6002      * @return {MixedCollection}
6003      */
6004     clone : function(){
6005         var r = new Ext.util.MixedCollection();
6006         var k = this.keys, it = this.items;
6007         for(var i = 0, len = it.length; i < len; i++){
6008             r.add(k[i], it[i]);
6009         }
6010         r.getKey = this.getKey;
6011         return r;
6012     }
6013 });
6014 /**
6015  * This method calls {@link #item item()}.
6016  * Returns the item associated with the passed key OR index. Key has priority
6017  * over index.  This is the equivalent of calling {@link #key} first, then if
6018  * nothing matched calling {@link #itemAt}.
6019  * @param {String/Number} key The key or index of the item.
6020  * @return {Object} If the item is found, returns the item.  If the item was
6021  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
6022  * returns <tt>null</tt>.
6023  */
6024 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
6025 /**
6026  * @class Ext.AbstractManager
6027  * @extends Object
6028  * Base Manager class - extended by ComponentMgr and PluginMgr
6029  */
6030 Ext.AbstractManager = Ext.extend(Object, {
6031     typeName: 'type',
6032     
6033     constructor: function(config) {
6034         Ext.apply(this, config || {});
6035         
6036         /**
6037          * Contains all of the items currently managed
6038          * @property all
6039          * @type Ext.util.MixedCollection
6040          */
6041         this.all = new Ext.util.MixedCollection();
6042         
6043         this.types = {};
6044     },
6045     
6046     /**
6047      * Returns a component by {@link Ext.Component#id id}.
6048      * For additional details see {@link Ext.util.MixedCollection#get}.
6049      * @param {String} id The component {@link Ext.Component#id id}
6050      * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
6051      * Class was found.
6052      */
6053     get : function(id){
6054         return this.all.get(id);
6055     },
6056     
6057     /**
6058      * Registers an item to be managed
6059      * @param {Mixed} item The item to register
6060      */
6061     register: function(item) {
6062         this.all.add(item);
6063     },
6064     
6065     /**
6066      * Unregisters a component by removing it from this manager
6067      * @param {Mixed} item The item to unregister
6068      */
6069     unregister: function(item) {
6070         this.all.remove(item);        
6071     },
6072     
6073     /**
6074      * <p>Registers a new Component constructor, keyed by a new
6075      * {@link Ext.Component#xtype}.</p>
6076      * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
6077      * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
6078      * child Components.
6079      * see {@link Ext.Container#items}</p>
6080      * @param {String} xtype The mnemonic string by which the Component class may be looked up.
6081      * @param {Constructor} cls The new Component class.
6082      */
6083     registerType : function(type, cls){
6084         this.types[type] = cls;
6085         cls[this.typeName] = type;
6086     },
6087     
6088     /**
6089      * Checks if a Component type is registered.
6090      * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
6091      * @return {Boolean} Whether the type is registered.
6092      */
6093     isRegistered : function(type){
6094         return this.types[type] !== undefined;    
6095     },
6096     
6097     /**
6098      * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
6099      * @param {Object} config The config object
6100      * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
6101      * @return {Mixed} The instance of whatever this manager is managing
6102      */
6103     create: function(config, defaultType) {
6104         var type        = config[this.typeName] || config.type || defaultType,
6105             Constructor = this.types[type];
6106         
6107         if (Constructor == undefined) {
6108             throw new Error(String.format("The '{0}' type has not been registered with this manager", type));
6109         }
6110         
6111         return new Constructor(config);
6112     },
6113     
6114     /**
6115      * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation.
6116      * @param {String} id The component {@link Ext.Component#id id}
6117      * @param {Function} fn The callback function
6118      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
6119      */
6120     onAvailable : function(id, fn, scope){
6121         var all = this.all;
6122         
6123         all.on("add", function(index, o){
6124             if (o.id == id) {
6125                 fn.call(scope || o, o);
6126                 all.un("add", fn, scope);
6127             }
6128         });
6129     }
6130 });/**
6131  * @class Ext.util.Format
6132  * Reusable data formatting functions
6133  * @singleton
6134  */
6135 Ext.util.Format = function() {
6136     var trimRe         = /^\s+|\s+$/g,
6137         stripTagsRE    = /<\/?[^>]+>/gi,
6138         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
6139         nl2brRe        = /\r?\n/g;
6140
6141     return {
6142         /**
6143          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
6144          * @param {String} value The string to truncate
6145          * @param {Number} length The maximum length to allow before truncating
6146          * @param {Boolean} word True to try to find a common work break
6147          * @return {String} The converted text
6148          */
6149         ellipsis : function(value, len, word) {
6150             if (value && value.length > len) {
6151                 if (word) {
6152                     var vs    = value.substr(0, len - 2),
6153                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
6154                     if (index == -1 || index < (len - 15)) {
6155                         return value.substr(0, len - 3) + "...";
6156                     } else {
6157                         return vs.substr(0, index) + "...";
6158                     }
6159                 } else {
6160                     return value.substr(0, len - 3) + "...";
6161                 }
6162             }
6163             return value;
6164         },
6165
6166         /**
6167          * Checks a reference and converts it to empty string if it is undefined
6168          * @param {Mixed} value Reference to check
6169          * @return {Mixed} Empty string if converted, otherwise the original value
6170          */
6171         undef : function(value) {
6172             return value !== undefined ? value : "";
6173         },
6174
6175         /**
6176          * Checks a reference and converts it to the default value if it's empty
6177          * @param {Mixed} value Reference to check
6178          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
6179          * @return {String}
6180          */
6181         defaultValue : function(value, defaultValue) {
6182             return value !== undefined && value !== '' ? value : defaultValue;
6183         },
6184
6185         /**
6186          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
6187          * @param {String} value The string to encode
6188          * @return {String} The encoded text
6189          */
6190         htmlEncode : function(value) {
6191             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
6192         },
6193
6194         /**
6195          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
6196          * @param {String} value The string to decode
6197          * @return {String} The decoded text
6198          */
6199         htmlDecode : function(value) {
6200             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
6201         },
6202
6203         /**
6204          * Trims any whitespace from either side of a string
6205          * @param {String} value The text to trim
6206          * @return {String} The trimmed text
6207          */
6208         trim : function(value) {
6209             return String(value).replace(trimRe, "");
6210         },
6211
6212         /**
6213          * Returns a substring from within an original string
6214          * @param {String} value The original text
6215          * @param {Number} start The start index of the substring
6216          * @param {Number} length The length of the substring
6217          * @return {String} The substring
6218          */
6219         substr : function(value, start, length) {
6220             return String(value).substr(start, length);
6221         },
6222
6223         /**
6224          * Converts a string to all lower case letters
6225          * @param {String} value The text to convert
6226          * @return {String} The converted text
6227          */
6228         lowercase : function(value) {
6229             return String(value).toLowerCase();
6230         },
6231
6232         /**
6233          * Converts a string to all upper case letters
6234          * @param {String} value The text to convert
6235          * @return {String} The converted text
6236          */
6237         uppercase : function(value) {
6238             return String(value).toUpperCase();
6239         },
6240
6241         /**
6242          * Converts the first character only of a string to upper case
6243          * @param {String} value The text to convert
6244          * @return {String} The converted text
6245          */
6246         capitalize : function(value) {
6247             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
6248         },
6249
6250         // private
6251         call : function(value, fn) {
6252             if (arguments.length > 2) {
6253                 var args = Array.prototype.slice.call(arguments, 2);
6254                 args.unshift(value);
6255                 return eval(fn).apply(window, args);
6256             } else {
6257                 return eval(fn).call(window, value);
6258             }
6259         },
6260
6261         /**
6262          * Format a number as US currency
6263          * @param {Number/String} value The numeric value to format
6264          * @return {String} The formatted currency string
6265          */
6266         usMoney : function(v) {
6267             v = (Math.round((v-0)*100))/100;
6268             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
6269             v = String(v);
6270             var ps = v.split('.'),
6271                 whole = ps[0],
6272                 sub = ps[1] ? '.'+ ps[1] : '.00',
6273                 r = /(\d+)(\d{3})/;
6274             while (r.test(whole)) {
6275                 whole = whole.replace(r, '$1' + ',' + '$2');
6276             }
6277             v = whole + sub;
6278             if (v.charAt(0) == '-') {
6279                 return '-$' + v.substr(1);
6280             }
6281             return "$" +  v;
6282         },
6283
6284         /**
6285          * Parse a value into a formatted date using the specified format pattern.
6286          * @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)
6287          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
6288          * @return {String} The formatted date string
6289          */
6290         date : function(v, format) {
6291             if (!v) {
6292                 return "";
6293             }
6294             if (!Ext.isDate(v)) {
6295                 v = new Date(Date.parse(v));
6296             }
6297             return v.dateFormat(format || "m/d/Y");
6298         },
6299
6300         /**
6301          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
6302          * @param {String} format Any valid date format string
6303          * @return {Function} The date formatting function
6304          */
6305         dateRenderer : function(format) {
6306             return function(v) {
6307                 return Ext.util.Format.date(v, format);
6308             };
6309         },
6310
6311         /**
6312          * Strips all HTML tags
6313          * @param {Mixed} value The text from which to strip tags
6314          * @return {String} The stripped text
6315          */
6316         stripTags : function(v) {
6317             return !v ? v : String(v).replace(stripTagsRE, "");
6318         },
6319
6320         /**
6321          * Strips all script tags
6322          * @param {Mixed} value The text from which to strip script tags
6323          * @return {String} The stripped text
6324          */
6325         stripScripts : function(v) {
6326             return !v ? v : String(v).replace(stripScriptsRe, "");
6327         },
6328
6329         /**
6330          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
6331          * @param {Number/String} size The numeric value to format
6332          * @return {String} The formatted file size
6333          */
6334         fileSize : function(size) {
6335             if (size < 1024) {
6336                 return size + " bytes";
6337             } else if (size < 1048576) {
6338                 return (Math.round(((size*10) / 1024))/10) + " KB";
6339             } else {
6340                 return (Math.round(((size*10) / 1048576))/10) + " MB";
6341             }
6342         },
6343
6344         /**
6345          * It does simple math for use in a template, for example:<pre><code>
6346          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
6347          * </code></pre>
6348          * @return {Function} A function that operates on the passed value.
6349          */
6350         math : function(){
6351             var fns = {};
6352             
6353             return function(v, a){
6354                 if (!fns[a]) {
6355                     fns[a] = new Function('v', 'return v ' + a + ';');
6356                 }
6357                 return fns[a](v);
6358             };
6359         }(),
6360
6361         /**
6362          * Rounds the passed number to the required decimal precision.
6363          * @param {Number/String} value The numeric value to round.
6364          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
6365          * @return {Number} The rounded value.
6366          */
6367         round : function(value, precision) {
6368             var result = Number(value);
6369             if (typeof precision == 'number') {
6370                 precision = Math.pow(10, precision);
6371                 result = Math.round(value * precision) / precision;
6372             }
6373             return result;
6374         },
6375
6376         /**
6377          * Formats the number according to the format string.
6378          * <div style="margin-left:40px">examples (123456.789):
6379          * <div style="margin-left:10px">
6380          * 0 - (123456) show only digits, no precision<br>
6381          * 0.00 - (123456.78) show only digits, 2 precision<br>
6382          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
6383          * 0,000 - (123,456) show comma and digits, no precision<br>
6384          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
6385          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
6386          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
6387          * For example: 0.000,00/i
6388          * </div></div>
6389          * @param {Number} v The number to format.
6390          * @param {String} format The way you would like to format this text.
6391          * @return {String} The formatted number.
6392          */
6393         number: function(v, format) {
6394             if (!format) {
6395                 return v;
6396             }
6397             v = Ext.num(v, NaN);
6398             if (isNaN(v)) {
6399                 return '';
6400             }
6401             var comma = ',',
6402                 dec   = '.',
6403                 i18n  = false,
6404                 neg   = v < 0;
6405
6406             v = Math.abs(v);
6407             if (format.substr(format.length - 2) == '/i') {
6408                 format = format.substr(0, format.length - 2);
6409                 i18n   = true;
6410                 comma  = '.';
6411                 dec    = ',';
6412             }
6413
6414             var hasComma = format.indexOf(comma) != -1,
6415                 psplit   = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
6416
6417             if (1 < psplit.length) {
6418                 v = v.toFixed(psplit[1].length);
6419             } else if(2 < psplit.length) {
6420                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
6421             } else {
6422                 v = v.toFixed(0);
6423             }
6424
6425             var fnum = v.toString();
6426
6427             psplit = fnum.split('.');
6428
6429             if (hasComma) {
6430                 var cnum = psplit[0], 
6431                     parr = [], 
6432                     j    = cnum.length, 
6433                     m    = Math.floor(j / 3),
6434                     n    = cnum.length % 3 || 3,
6435                     i;
6436
6437                 for (i = 0; i < j; i += n) {
6438                     if (i != 0) {
6439                         n = 3;
6440                     }
6441                     
6442                     parr[parr.length] = cnum.substr(i, n);
6443                     m -= 1;
6444                 }
6445                 fnum = parr.join(comma);
6446                 if (psplit[1]) {
6447                     fnum += dec + psplit[1];
6448                 }
6449             } else {
6450                 if (psplit[1]) {
6451                     fnum = psplit[0] + dec + psplit[1];
6452                 }
6453             }
6454
6455             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
6456         },
6457
6458         /**
6459          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
6460          * @param {String} format Any valid number format string for {@link #number}
6461          * @return {Function} The number formatting function
6462          */
6463         numberRenderer : function(format) {
6464             return function(v) {
6465                 return Ext.util.Format.number(v, format);
6466             };
6467         },
6468
6469         /**
6470          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
6471          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
6472          * if the value is 0 or greater than 1.
6473          * @param {Number} value The value to compare against
6474          * @param {String} singular The singular form of the word
6475          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
6476          */
6477         plural : function(v, s, p) {
6478             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
6479         },
6480
6481         /**
6482          * Converts newline characters to the HTML tag &lt;br/>
6483          * @param {String} The string value to format.
6484          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
6485          */
6486         nl2br : function(v) {
6487             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
6488         }
6489     };
6490 }();
6491 /**
6492  * @class Ext.XTemplate
6493  * @extends Ext.Template
6494  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
6495  * <li>Autofilling arrays using templates and sub-templates</li>
6496  * <li>Conditional processing with basic comparison operators</li>
6497  * <li>Basic math function support</li>
6498  * <li>Execute arbitrary inline code with special built-in template variables</li>
6499  * <li>Custom member functions</li>
6500  * <li>Many special tags and built-in operators that aren't defined as part of
6501  * the API, but are supported in the templates that can be created</li>
6502  * </ul></div></p>
6503  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
6504  * <li>{@link Ext.DataView}</li>
6505  * <li>{@link Ext.ListView}</li>
6506  * <li>{@link Ext.form.ComboBox}</li>
6507  * <li>{@link Ext.grid.TemplateColumn}</li>
6508  * <li>{@link Ext.grid.GroupingView}</li>
6509  * <li>{@link Ext.menu.Item}</li>
6510  * <li>{@link Ext.layout.MenuLayout}</li>
6511  * <li>{@link Ext.ColorPalette}</li>
6512  * </ul></div></p>
6513  *
6514  * <p>For example usage {@link #XTemplate see the constructor}.</p>
6515  *
6516  * @constructor
6517  * The {@link Ext.Template#Template Ext.Template constructor} describes
6518  * the acceptable parameters to pass to the constructor. The following
6519  * examples demonstrate all of the supported features.</p>
6520  *
6521  * <div class="mdetail-params"><ul>
6522  *
6523  * <li><b><u>Sample Data</u></b>
6524  * <div class="sub-desc">
6525  * <p>This is the data object used for reference in each code example:</p>
6526  * <pre><code>
6527 var data = {
6528     name: 'Jack Slocum',
6529     title: 'Lead Developer',
6530     company: 'Ext JS, LLC',
6531     email: 'jack@extjs.com',
6532     address: '4 Red Bulls Drive',
6533     city: 'Cleveland',
6534     state: 'Ohio',
6535     zip: '44102',
6536     drinks: ['Red Bull', 'Coffee', 'Water'],
6537     kids: [{
6538         name: 'Sara Grace',
6539         age:3
6540     },{
6541         name: 'Zachary',
6542         age:2
6543     },{
6544         name: 'John James',
6545         age:0
6546     }]
6547 };
6548  * </code></pre>
6549  * </div>
6550  * </li>
6551  *
6552  *
6553  * <li><b><u>Auto filling of arrays</u></b>
6554  * <div class="sub-desc">
6555  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
6556  * to process the provided data object:
6557  * <ul>
6558  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
6559  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
6560  * array.</li>
6561  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
6562  * <li>While processing an array, the special variable <tt>{#}</tt>
6563  * will provide the current array index + 1 (starts at 1, not 0).</li>
6564  * </ul>
6565  * </p>
6566  * <pre><code>
6567 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
6568 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
6569 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
6570  * </code></pre>
6571  * Using the sample data above:
6572  * <pre><code>
6573 var tpl = new Ext.XTemplate(
6574     '&lt;p>Kids: ',
6575     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
6576         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
6577     '&lt;/tpl>&lt;/p>'
6578 );
6579 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
6580  * </code></pre>
6581  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
6582  * to access specified members of the provided data object to populate the template:</p>
6583  * <pre><code>
6584 var tpl = new Ext.XTemplate(
6585     '&lt;p>Name: {name}&lt;/p>',
6586     '&lt;p>Title: {title}&lt;/p>',
6587     '&lt;p>Company: {company}&lt;/p>',
6588     '&lt;p>Kids: ',
6589     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
6590         '&lt;p>{name}&lt;/p>',
6591     '&lt;/tpl>&lt;/p>'
6592 );
6593 tpl.overwrite(panel.body, data);  // pass the root node of the data object
6594  * </code></pre>
6595  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
6596  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
6597  * will represent the value of the array at the current index:</p>
6598  * <pre><code>
6599 var tpl = new Ext.XTemplate(
6600     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
6601     '&lt;tpl for="drinks">',
6602        '&lt;div> - {.}&lt;/div>',
6603     '&lt;/tpl>'
6604 );
6605 tpl.overwrite(panel.body, data);
6606  * </code></pre>
6607  * <p>When processing a sub-template, for example while looping through a child array,
6608  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
6609  * <pre><code>
6610 var tpl = new Ext.XTemplate(
6611     '&lt;p>Name: {name}&lt;/p>',
6612     '&lt;p>Kids: ',
6613     '&lt;tpl for="kids">',
6614         '&lt;tpl if="age > 1">',
6615             '&lt;p>{name}&lt;/p>',
6616             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
6617         '&lt;/tpl>',
6618     '&lt;/tpl>&lt;/p>'
6619 );
6620 tpl.overwrite(panel.body, data);
6621  * </code></pre>
6622  * </div>
6623  * </li>
6624  *
6625  *
6626  * <li><b><u>Conditional processing with basic comparison operators</u></b>
6627  * <div class="sub-desc">
6628  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
6629  * to provide conditional checks for deciding whether or not to render specific
6630  * parts of the template. Notes:<div class="sub-desc"><ul>
6631  * <li>Double quotes must be encoded if used within the conditional</li>
6632  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
6633  * <tt>if</tt> statements should be used.</li>
6634  * </ul></div>
6635  * <pre><code>
6636 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
6637 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
6638 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
6639 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
6640 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
6641 // no good:
6642 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
6643 // encode &#34; if it is part of the condition, e.g.
6644 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
6645  * </code></pre>
6646  * Using the sample data above:
6647  * <pre><code>
6648 var tpl = new Ext.XTemplate(
6649     '&lt;p>Name: {name}&lt;/p>',
6650     '&lt;p>Kids: ',
6651     '&lt;tpl for="kids">',
6652         '&lt;tpl if="age > 1">',
6653             '&lt;p>{name}&lt;/p>',
6654         '&lt;/tpl>',
6655     '&lt;/tpl>&lt;/p>'
6656 );
6657 tpl.overwrite(panel.body, data);
6658  * </code></pre>
6659  * </div>
6660  * </li>
6661  *
6662  *
6663  * <li><b><u>Basic math support</u></b>
6664  * <div class="sub-desc">
6665  * <p>The following basic math operators may be applied directly on numeric
6666  * data values:</p><pre>
6667  * + - * /
6668  * </pre>
6669  * For example:
6670  * <pre><code>
6671 var tpl = new Ext.XTemplate(
6672     '&lt;p>Name: {name}&lt;/p>',
6673     '&lt;p>Kids: ',
6674     '&lt;tpl for="kids">',
6675         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
6676             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
6677             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
6678             '&lt;p>Dad: {parent.name}&lt;/p>',
6679         '&lt;/tpl>',
6680     '&lt;/tpl>&lt;/p>'
6681 );
6682 tpl.overwrite(panel.body, data);
6683 </code></pre>
6684  * </div>
6685  * </li>
6686  *
6687  *
6688  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
6689  * <div class="sub-desc">
6690  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
6691  * in the scope of the template. There are some special variables available in that code:
6692  * <ul>
6693  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
6694  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
6695  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
6696  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
6697  * loop you are in (1-based).</li>
6698  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
6699  * of the array you are looping.</li>
6700  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
6701  * </ul>
6702  * This example demonstrates basic row striping using an inline code block and the
6703  * <tt>xindex</tt> variable:</p>
6704  * <pre><code>
6705 var tpl = new Ext.XTemplate(
6706     '&lt;p>Name: {name}&lt;/p>',
6707     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
6708     '&lt;p>Kids: ',
6709     '&lt;tpl for="kids">',
6710        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
6711         '{name}',
6712         '&lt;/div>',
6713     '&lt;/tpl>&lt;/p>'
6714 );
6715 tpl.overwrite(panel.body, data);
6716  * </code></pre>
6717  * </div>
6718  * </li>
6719  *
6720  * <li><b><u>Template member functions</u></b>
6721  * <div class="sub-desc">
6722  * <p>One or more member functions can be specified in a configuration
6723  * object passed into the XTemplate constructor for more complex processing:</p>
6724  * <pre><code>
6725 var tpl = new Ext.XTemplate(
6726     '&lt;p>Name: {name}&lt;/p>',
6727     '&lt;p>Kids: ',
6728     '&lt;tpl for="kids">',
6729         '&lt;tpl if="this.isGirl(name)">',
6730             '&lt;p>Girl: {name} - {age}&lt;/p>',
6731         '&lt;/tpl>',
6732         // use opposite if statement to simulate 'else' processing:
6733         '&lt;tpl if="this.isGirl(name) == false">',
6734             '&lt;p>Boy: {name} - {age}&lt;/p>',
6735         '&lt;/tpl>',
6736         '&lt;tpl if="this.isBaby(age)">',
6737             '&lt;p>{name} is a baby!&lt;/p>',
6738         '&lt;/tpl>',
6739     '&lt;/tpl>&lt;/p>',
6740     {
6741         // XTemplate configuration:
6742         compiled: true,
6743         disableFormats: true,
6744         // member functions:
6745         isGirl: function(name){
6746             return name == 'Sara Grace';
6747         },
6748         isBaby: function(age){
6749             return age < 1;
6750         }
6751     }
6752 );
6753 tpl.overwrite(panel.body, data);
6754  * </code></pre>
6755  * </div>
6756  * </li>
6757  *
6758  * </ul></div>
6759  *
6760  * @param {Mixed} config
6761  */
6762 Ext.XTemplate = function(){
6763     Ext.XTemplate.superclass.constructor.apply(this, arguments);
6764
6765     var me = this,
6766         s = me.html,
6767         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
6768         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
6769         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
6770         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
6771         m,
6772         id = 0,
6773         tpls = [],
6774         VALUES = 'values',
6775         PARENT = 'parent',
6776         XINDEX = 'xindex',
6777         XCOUNT = 'xcount',
6778         RETURN = 'return ',
6779         WITHVALUES = 'with(values){ ';
6780
6781     s = ['<tpl>', s, '</tpl>'].join('');
6782
6783     while((m = s.match(re))){
6784         var m2 = m[0].match(nameRe),
6785             m3 = m[0].match(ifRe),
6786             m4 = m[0].match(execRe),
6787             exp = null,
6788             fn = null,
6789             exec = null,
6790             name = m2 && m2[1] ? m2[1] : '';
6791
6792        if (m3) {
6793            exp = m3 && m3[1] ? m3[1] : null;
6794            if(exp){
6795                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
6796            }
6797        }
6798        if (m4) {
6799            exp = m4 && m4[1] ? m4[1] : null;
6800            if(exp){
6801                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
6802            }
6803        }
6804        if(name){
6805            switch(name){
6806                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
6807                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
6808                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
6809            }
6810        }
6811        tpls.push({
6812             id: id,
6813             target: name,
6814             exec: exec,
6815             test: fn,
6816             body: m[1]||''
6817         });
6818        s = s.replace(m[0], '{xtpl'+ id + '}');
6819        ++id;
6820     }
6821     for(var i = tpls.length-1; i >= 0; --i){
6822         me.compileTpl(tpls[i]);
6823     }
6824     me.master = tpls[tpls.length-1];
6825     me.tpls = tpls;
6826 };
6827 Ext.extend(Ext.XTemplate, Ext.Template, {
6828     // private
6829     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
6830     // private
6831     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
6832
6833     // private
6834     applySubTemplate : function(id, values, parent, xindex, xcount){
6835         var me = this,
6836             len,
6837             t = me.tpls[id],
6838             vs,
6839             buf = [];
6840         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
6841             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
6842             return '';
6843         }
6844         vs = t.target ? t.target.call(me, values, parent) : values;
6845         len = vs.length;
6846         parent = t.target ? values : parent;
6847         if(t.target && Ext.isArray(vs)){
6848             for(var i = 0, len = vs.length; i < len; i++){
6849                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
6850             }
6851             return buf.join('');
6852         }
6853         return t.compiled.call(me, vs, parent, xindex, xcount);
6854     },
6855
6856     // private
6857     compileTpl : function(tpl){
6858         var fm = Ext.util.Format,
6859             useF = this.disableFormats !== true,
6860             sep = Ext.isGecko ? "+" : ",",
6861             body;
6862
6863         function fn(m, name, format, args, math){
6864             if(name.substr(0, 4) == 'xtpl'){
6865                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
6866             }
6867             var v;
6868             if(name === '.'){
6869                 v = 'values';
6870             }else if(name === '#'){
6871                 v = 'xindex';
6872             }else if(name.indexOf('.') != -1){
6873                 v = name;
6874             }else{
6875                 v = "values['" + name + "']";
6876             }
6877             if(math){
6878                 v = '(' + v + math + ')';
6879             }
6880             if (format && useF) {
6881                 args = args ? ',' + args : "";
6882                 if(format.substr(0, 5) != "this."){
6883                     format = "fm." + format + '(';
6884                 }else{
6885                     format = 'this.call("'+ format.substr(5) + '", ';
6886                     args = ", values";
6887                 }
6888             } else {
6889                 args= ''; format = "("+v+" === undefined ? '' : ";
6890             }
6891             return "'"+ sep + format + v + args + ")"+sep+"'";
6892         }
6893
6894         function codeFn(m, code){
6895             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
6896             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
6897         }
6898
6899         // branched to use + in gecko and [].join() in others
6900         if(Ext.isGecko){
6901             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
6902                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
6903                     "';};";
6904         }else{
6905             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
6906             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
6907             body.push("'].join('');};");
6908             body = body.join('');
6909         }
6910         eval(body);
6911         return this;
6912     },
6913
6914     /**
6915      * Returns an HTML fragment of this template with the specified values applied.
6916      * @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'})
6917      * @return {String} The HTML fragment
6918      */
6919     applyTemplate : function(values){
6920         return this.master.compiled.call(this, values, {}, 1, 1);
6921     },
6922
6923     /**
6924      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
6925      * @return {Function} The compiled function
6926      */
6927     compile : function(){return this;}
6928
6929     /**
6930      * @property re
6931      * @hide
6932      */
6933     /**
6934      * @property disableFormats
6935      * @hide
6936      */
6937     /**
6938      * @method set
6939      * @hide
6940      */
6941
6942 });
6943 /**
6944  * Alias for {@link #applyTemplate}
6945  * Returns an HTML fragment of this template with the specified values applied.
6946  * @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'})
6947  * @return {String} The HTML fragment
6948  * @member Ext.XTemplate
6949  * @method apply
6950  */
6951 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
6952
6953 /**
6954  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6955  * @param {String/HTMLElement} el A DOM element or its id
6956  * @return {Ext.Template} The created template
6957  * @static
6958  */
6959 Ext.XTemplate.from = function(el){
6960     el = Ext.getDom(el);
6961     return new Ext.XTemplate(el.value || el.innerHTML);
6962 };
6963 /**
6964  * @class Ext.util.CSS
6965  * Utility class for manipulating CSS rules
6966  * @singleton
6967  */
6968 Ext.util.CSS = function(){
6969         var rules = null;
6970         var doc = document;
6971
6972     var camelRe = /(-[a-z])/gi;
6973     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6974
6975    return {
6976    /**
6977     * Creates a stylesheet from a text blob of rules.
6978     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
6979     * @param {String} cssText The text containing the css rules
6980     * @param {String} id An id to add to the stylesheet for later removal
6981     * @return {StyleSheet}
6982     */
6983    createStyleSheet : function(cssText, id){
6984        var ss;
6985        var head = doc.getElementsByTagName("head")[0];
6986        var rules = doc.createElement("style");
6987        rules.setAttribute("type", "text/css");
6988        if(id){
6989            rules.setAttribute("id", id);
6990        }
6991        if(Ext.isIE){
6992            head.appendChild(rules);
6993            ss = rules.styleSheet;
6994            ss.cssText = cssText;
6995        }else{
6996            try{
6997                 rules.appendChild(doc.createTextNode(cssText));
6998            }catch(e){
6999                rules.cssText = cssText;
7000            }
7001            head.appendChild(rules);
7002            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
7003        }
7004        this.cacheStyleSheet(ss);
7005        return ss;
7006    },
7007
7008    /**
7009     * Removes a style or link tag by id
7010     * @param {String} id The id of the tag
7011     */
7012    removeStyleSheet : function(id){
7013        var existing = doc.getElementById(id);
7014        if(existing){
7015            existing.parentNode.removeChild(existing);
7016        }
7017    },
7018
7019    /**
7020     * Dynamically swaps an existing stylesheet reference for a new one
7021     * @param {String} id The id of an existing link tag to remove
7022     * @param {String} url The href of the new stylesheet to include
7023     */
7024    swapStyleSheet : function(id, url){
7025        this.removeStyleSheet(id);
7026        var ss = doc.createElement("link");
7027        ss.setAttribute("rel", "stylesheet");
7028        ss.setAttribute("type", "text/css");
7029        ss.setAttribute("id", id);
7030        ss.setAttribute("href", url);
7031        doc.getElementsByTagName("head")[0].appendChild(ss);
7032    },
7033    
7034    /**
7035     * Refresh the rule cache if you have dynamically added stylesheets
7036     * @return {Object} An object (hash) of rules indexed by selector
7037     */
7038    refreshCache : function(){
7039        return this.getRules(true);
7040    },
7041
7042    // private
7043    cacheStyleSheet : function(ss){
7044        if(!rules){
7045            rules = {};
7046        }
7047        try{// try catch for cross domain access issue
7048            var ssRules = ss.cssRules || ss.rules;
7049            for(var j = ssRules.length-1; j >= 0; --j){
7050                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
7051            }
7052        }catch(e){}
7053    },
7054    
7055    /**
7056     * Gets all css rules for the document
7057     * @param {Boolean} refreshCache true to refresh the internal cache
7058     * @return {Object} An object (hash) of rules indexed by selector
7059     */
7060    getRules : function(refreshCache){
7061                 if(rules === null || refreshCache){
7062                         rules = {};
7063                         var ds = doc.styleSheets;
7064                         for(var i =0, len = ds.length; i < len; i++){
7065                             try{
7066                         this.cacheStyleSheet(ds[i]);
7067                     }catch(e){} 
7068                 }
7069                 }
7070                 return rules;
7071         },
7072         
7073         /**
7074     * Gets an an individual CSS rule by selector(s)
7075     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
7076     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
7077     * @return {CSSRule} The CSS rule or null if one is not found
7078     */
7079    getRule : function(selector, refreshCache){
7080                 var rs = this.getRules(refreshCache);
7081                 if(!Ext.isArray(selector)){
7082                     return rs[selector.toLowerCase()];
7083                 }
7084                 for(var i = 0; i < selector.length; i++){
7085                         if(rs[selector[i]]){
7086                                 return rs[selector[i].toLowerCase()];
7087                         }
7088                 }
7089                 return null;
7090         },
7091         
7092         
7093         /**
7094     * Updates a rule property
7095     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
7096     * @param {String} property The css property
7097     * @param {String} value The new value for the property
7098     * @return {Boolean} true If a rule was found and updated
7099     */
7100    updateRule : function(selector, property, value){
7101                 if(!Ext.isArray(selector)){
7102                         var rule = this.getRule(selector);
7103                         if(rule){
7104                                 rule.style[property.replace(camelRe, camelFn)] = value;
7105                                 return true;
7106                         }
7107                 }else{
7108                         for(var i = 0; i < selector.length; i++){
7109                                 if(this.updateRule(selector[i], property, value)){
7110                                         return true;
7111                                 }
7112                         }
7113                 }
7114                 return false;
7115         }
7116    };   
7117 }();/**
7118  @class Ext.util.ClickRepeater
7119  @extends Ext.util.Observable
7120
7121  A wrapper class which can be applied to any element. Fires a "click" event while the
7122  mouse is pressed. The interval between firings may be specified in the config but
7123  defaults to 20 milliseconds.
7124
7125  Optionally, a CSS class may be applied to the element during the time it is pressed.
7126
7127  @cfg {Mixed} el The element to act as a button.
7128  @cfg {Number} delay The initial delay before the repeating event begins firing.
7129  Similar to an autorepeat key delay.
7130  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
7131  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
7132  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
7133            "interval" and "delay" are ignored.
7134  @cfg {Boolean} preventDefault True to prevent the default click event
7135  @cfg {Boolean} stopDefault True to stop the default click event
7136
7137  @history
7138     2007-02-02 jvs Original code contributed by Nige "Animal" White
7139     2007-02-02 jvs Renamed to ClickRepeater
7140     2007-02-03 jvs Modifications for FF Mac and Safari
7141
7142  @constructor
7143  @param {Mixed} el The element to listen on
7144  @param {Object} config
7145  */
7146 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
7147     
7148     constructor : function(el, config){
7149         this.el = Ext.get(el);
7150         this.el.unselectable();
7151
7152         Ext.apply(this, config);
7153
7154         this.addEvents(
7155         /**
7156          * @event mousedown
7157          * Fires when the mouse button is depressed.
7158          * @param {Ext.util.ClickRepeater} this
7159          * @param {Ext.EventObject} e
7160          */
7161         "mousedown",
7162         /**
7163          * @event click
7164          * Fires on a specified interval during the time the element is pressed.
7165          * @param {Ext.util.ClickRepeater} this
7166          * @param {Ext.EventObject} e
7167          */
7168         "click",
7169         /**
7170          * @event mouseup
7171          * Fires when the mouse key is released.
7172          * @param {Ext.util.ClickRepeater} this
7173          * @param {Ext.EventObject} e
7174          */
7175         "mouseup"
7176         );
7177
7178         if(!this.disabled){
7179             this.disabled = true;
7180             this.enable();
7181         }
7182
7183         // allow inline handler
7184         if(this.handler){
7185             this.on("click", this.handler,  this.scope || this);
7186         }
7187
7188         Ext.util.ClickRepeater.superclass.constructor.call(this);        
7189     },
7190     
7191     interval : 20,
7192     delay: 250,
7193     preventDefault : true,
7194     stopDefault : false,
7195     timer : 0,
7196
7197     /**
7198      * Enables the repeater and allows events to fire.
7199      */
7200     enable: function(){
7201         if(this.disabled){
7202             this.el.on('mousedown', this.handleMouseDown, this);
7203             if (Ext.isIE){
7204                 this.el.on('dblclick', this.handleDblClick, this);
7205             }
7206             if(this.preventDefault || this.stopDefault){
7207                 this.el.on('click', this.eventOptions, this);
7208             }
7209         }
7210         this.disabled = false;
7211     },
7212
7213     /**
7214      * Disables the repeater and stops events from firing.
7215      */
7216     disable: function(/* private */ force){
7217         if(force || !this.disabled){
7218             clearTimeout(this.timer);
7219             if(this.pressClass){
7220                 this.el.removeClass(this.pressClass);
7221             }
7222             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
7223             this.el.removeAllListeners();
7224         }
7225         this.disabled = true;
7226     },
7227
7228     /**
7229      * Convenience function for setting disabled/enabled by boolean.
7230      * @param {Boolean} disabled
7231      */
7232     setDisabled: function(disabled){
7233         this[disabled ? 'disable' : 'enable']();
7234     },
7235
7236     eventOptions: function(e){
7237         if(this.preventDefault){
7238             e.preventDefault();
7239         }
7240         if(this.stopDefault){
7241             e.stopEvent();
7242         }
7243     },
7244
7245     // private
7246     destroy : function() {
7247         this.disable(true);
7248         Ext.destroy(this.el);
7249         this.purgeListeners();
7250     },
7251
7252     handleDblClick : function(e){
7253         clearTimeout(this.timer);
7254         this.el.blur();
7255
7256         this.fireEvent("mousedown", this, e);
7257         this.fireEvent("click", this, e);
7258     },
7259
7260     // private
7261     handleMouseDown : function(e){
7262         clearTimeout(this.timer);
7263         this.el.blur();
7264         if(this.pressClass){
7265             this.el.addClass(this.pressClass);
7266         }
7267         this.mousedownTime = new Date();
7268
7269         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
7270         this.el.on("mouseout", this.handleMouseOut, this);
7271
7272         this.fireEvent("mousedown", this, e);
7273         this.fireEvent("click", this, e);
7274
7275         // Do not honor delay or interval if acceleration wanted.
7276         if (this.accelerate) {
7277             this.delay = 400;
7278         }
7279         this.timer = this.click.defer(this.delay || this.interval, this, [e]);
7280     },
7281
7282     // private
7283     click : function(e){
7284         this.fireEvent("click", this, e);
7285         this.timer = this.click.defer(this.accelerate ?
7286             this.easeOutExpo(this.mousedownTime.getElapsed(),
7287                 400,
7288                 -390,
7289                 12000) :
7290             this.interval, this, [e]);
7291     },
7292
7293     easeOutExpo : function (t, b, c, d) {
7294         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
7295     },
7296
7297     // private
7298     handleMouseOut : function(){
7299         clearTimeout(this.timer);
7300         if(this.pressClass){
7301             this.el.removeClass(this.pressClass);
7302         }
7303         this.el.on("mouseover", this.handleMouseReturn, this);
7304     },
7305
7306     // private
7307     handleMouseReturn : function(){
7308         this.el.un("mouseover", this.handleMouseReturn, this);
7309         if(this.pressClass){
7310             this.el.addClass(this.pressClass);
7311         }
7312         this.click();
7313     },
7314
7315     // private
7316     handleMouseUp : function(e){
7317         clearTimeout(this.timer);
7318         this.el.un("mouseover", this.handleMouseReturn, this);
7319         this.el.un("mouseout", this.handleMouseOut, this);
7320         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
7321         this.el.removeClass(this.pressClass);
7322         this.fireEvent("mouseup", this, e);
7323     }
7324 });/**
7325  * @class Ext.KeyNav
7326  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
7327  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
7328  * way to implement custom navigation schemes for any UI component.</p>
7329  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
7330  * pageUp, pageDown, del, home, end.  Usage:</p>
7331  <pre><code>
7332 var nav = new Ext.KeyNav("my-element", {
7333     "left" : function(e){
7334         this.moveLeft(e.ctrlKey);
7335     },
7336     "right" : function(e){
7337         this.moveRight(e.ctrlKey);
7338     },
7339     "enter" : function(e){
7340         this.save();
7341     },
7342     scope : this
7343 });
7344 </code></pre>
7345  * @constructor
7346  * @param {Mixed} el The element to bind to
7347  * @param {Object} config The config
7348  */
7349 Ext.KeyNav = function(el, config){
7350     this.el = Ext.get(el);
7351     Ext.apply(this, config);
7352     if(!this.disabled){
7353         this.disabled = true;
7354         this.enable();
7355     }
7356 };
7357
7358 Ext.KeyNav.prototype = {
7359     /**
7360      * @cfg {Boolean} disabled
7361      * True to disable this KeyNav instance (defaults to false)
7362      */
7363     disabled : false,
7364     /**
7365      * @cfg {String} defaultEventAction
7366      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
7367      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
7368      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
7369      */
7370     defaultEventAction: "stopEvent",
7371     /**
7372      * @cfg {Boolean} forceKeyDown
7373      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
7374      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
7375      * handle keydown instead of keypress.
7376      */
7377     forceKeyDown : false,
7378
7379     // private
7380     relay : function(e){
7381         var k = e.getKey(),
7382             h = this.keyToHandler[k];
7383         if(h && this[h]){
7384             if(this.doRelay(e, this[h], h) !== true){
7385                 e[this.defaultEventAction]();
7386             }
7387         }
7388     },
7389
7390     // private
7391     doRelay : function(e, h, hname){
7392         return h.call(this.scope || this, e, hname);
7393     },
7394
7395     // possible handlers
7396     enter : false,
7397     left : false,
7398     right : false,
7399     up : false,
7400     down : false,
7401     tab : false,
7402     esc : false,
7403     pageUp : false,
7404     pageDown : false,
7405     del : false,
7406     home : false,
7407     end : false,
7408
7409     // quick lookup hash
7410     keyToHandler : {
7411         37 : "left",
7412         39 : "right",
7413         38 : "up",
7414         40 : "down",
7415         33 : "pageUp",
7416         34 : "pageDown",
7417         46 : "del",
7418         36 : "home",
7419         35 : "end",
7420         13 : "enter",
7421         27 : "esc",
7422         9  : "tab"
7423     },
7424     
7425     stopKeyUp: function(e) {
7426         var k = e.getKey();
7427
7428         if (k >= 37 && k <= 40) {
7429             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
7430             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
7431             e.stopEvent();
7432         }
7433     },
7434     
7435     /**
7436      * Destroy this KeyNav (this is the same as calling disable).
7437      */
7438     destroy: function(){
7439         this.disable();    
7440     },
7441
7442         /**
7443          * Enable this KeyNav
7444          */
7445         enable: function() {
7446         if (this.disabled) {
7447             if (Ext.isSafari2) {
7448                 // call stopKeyUp() on "keyup" event
7449                 this.el.on('keyup', this.stopKeyUp, this);
7450             }
7451
7452             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7453             this.disabled = false;
7454         }
7455     },
7456
7457         /**
7458          * Disable this KeyNav
7459          */
7460         disable: function() {
7461         if (!this.disabled) {
7462             if (Ext.isSafari2) {
7463                 // remove "keyup" event handler
7464                 this.el.un('keyup', this.stopKeyUp, this);
7465             }
7466
7467             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7468             this.disabled = true;
7469         }
7470     },
7471     
7472     /**
7473      * Convenience function for setting disabled/enabled by boolean.
7474      * @param {Boolean} disabled
7475      */
7476     setDisabled : function(disabled){
7477         this[disabled ? "disable" : "enable"]();
7478     },
7479     
7480     // private
7481     isKeydown: function(){
7482         return this.forceKeyDown || Ext.EventManager.useKeydown;
7483     }
7484 };
7485 /**
7486  * @class Ext.KeyMap
7487  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
7488  * The constructor accepts the same config object as defined by {@link #addBinding}.
7489  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
7490  * combination it will call the function with this signature (if the match is a multi-key
7491  * combination the callback will still be called only once): (String key, Ext.EventObject e)
7492  * A KeyMap can also handle a string representation of keys.<br />
7493  * Usage:
7494  <pre><code>
7495 // map one key by key code
7496 var map = new Ext.KeyMap("my-element", {
7497     key: 13, // or Ext.EventObject.ENTER
7498     fn: myHandler,
7499     scope: myObject
7500 });
7501
7502 // map multiple keys to one action by string
7503 var map = new Ext.KeyMap("my-element", {
7504     key: "a\r\n\t",
7505     fn: myHandler,
7506     scope: myObject
7507 });
7508
7509 // map multiple keys to multiple actions by strings and array of codes
7510 var map = new Ext.KeyMap("my-element", [
7511     {
7512         key: [10,13],
7513         fn: function(){ alert("Return was pressed"); }
7514     }, {
7515         key: "abc",
7516         fn: function(){ alert('a, b or c was pressed'); }
7517     }, {
7518         key: "\t",
7519         ctrl:true,
7520         shift:true,
7521         fn: function(){ alert('Control + shift + tab was pressed.'); }
7522     }
7523 ]);
7524 </code></pre>
7525  * <b>Note: A KeyMap starts enabled</b>
7526  * @constructor
7527  * @param {Mixed} el The element to bind to
7528  * @param {Object} config The config (see {@link #addBinding})
7529  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
7530  */
7531 Ext.KeyMap = function(el, config, eventName){
7532     this.el  = Ext.get(el);
7533     this.eventName = eventName || "keydown";
7534     this.bindings = [];
7535     if(config){
7536         this.addBinding(config);
7537     }
7538     this.enable();
7539 };
7540
7541 Ext.KeyMap.prototype = {
7542     /**
7543      * True to stop the event from bubbling and prevent the default browser action if the
7544      * key was handled by the KeyMap (defaults to false)
7545      * @type Boolean
7546      */
7547     stopEvent : false,
7548
7549     /**
7550      * Add a new binding to this KeyMap. The following config object properties are supported:
7551      * <pre>
7552 Property    Type             Description
7553 ----------  ---------------  ----------------------------------------------------------------------
7554 key         String/Array     A single keycode or an array of keycodes to handle
7555 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)
7556 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)
7557 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)
7558 handler     Function         The function to call when KeyMap finds the expected key combination
7559 fn          Function         Alias of handler (for backwards-compatibility)
7560 scope       Object           The scope of the callback function
7561 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)
7562 </pre>
7563      *
7564      * Usage:
7565      * <pre><code>
7566 // Create a KeyMap
7567 var map = new Ext.KeyMap(document, {
7568     key: Ext.EventObject.ENTER,
7569     fn: handleKey,
7570     scope: this
7571 });
7572
7573 //Add a new binding to the existing KeyMap later
7574 map.addBinding({
7575     key: 'abc',
7576     shift: true,
7577     fn: handleKey,
7578     scope: this
7579 });
7580 </code></pre>
7581      * @param {Object/Array} config A single KeyMap config or an array of configs
7582      */
7583         addBinding : function(config){
7584         if(Ext.isArray(config)){
7585             Ext.each(config, function(c){
7586                 this.addBinding(c);
7587             }, this);
7588             return;
7589         }
7590         var keyCode = config.key,
7591             fn = config.fn || config.handler,
7592             scope = config.scope;
7593
7594         if (config.stopEvent) {
7595             this.stopEvent = config.stopEvent;    
7596         }       
7597
7598         if(typeof keyCode == "string"){
7599             var ks = [];
7600             var keyString = keyCode.toUpperCase();
7601             for(var j = 0, len = keyString.length; j < len; j++){
7602                 ks.push(keyString.charCodeAt(j));
7603             }
7604             keyCode = ks;
7605         }
7606         var keyArray = Ext.isArray(keyCode);
7607         
7608         var handler = function(e){
7609             if(this.checkModifiers(config, e)){
7610                 var k = e.getKey();
7611                 if(keyArray){
7612                     for(var i = 0, len = keyCode.length; i < len; i++){
7613                         if(keyCode[i] == k){
7614                           if(this.stopEvent){
7615                               e.stopEvent();
7616                           }
7617                           fn.call(scope || window, k, e);
7618                           return;
7619                         }
7620                     }
7621                 }else{
7622                     if(k == keyCode){
7623                         if(this.stopEvent){
7624                            e.stopEvent();
7625                         }
7626                         fn.call(scope || window, k, e);
7627                     }
7628                 }
7629             }
7630         };
7631         this.bindings.push(handler);
7632         },
7633     
7634     // private
7635     checkModifiers: function(config, e){
7636         var val, key, keys = ['shift', 'ctrl', 'alt'];
7637         for (var i = 0, len = keys.length; i < len; ++i){
7638             key = keys[i];
7639             val = config[key];
7640             if(!(val === undefined || (val === e[key + 'Key']))){
7641                 return false;
7642             }
7643         }
7644         return true;
7645     },
7646
7647     /**
7648      * Shorthand for adding a single key listener
7649      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
7650      * following options:
7651      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
7652      * @param {Function} fn The function to call
7653      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
7654      */
7655     on : function(key, fn, scope){
7656         var keyCode, shift, ctrl, alt;
7657         if(typeof key == "object" && !Ext.isArray(key)){
7658             keyCode = key.key;
7659             shift = key.shift;
7660             ctrl = key.ctrl;
7661             alt = key.alt;
7662         }else{
7663             keyCode = key;
7664         }
7665         this.addBinding({
7666             key: keyCode,
7667             shift: shift,
7668             ctrl: ctrl,
7669             alt: alt,
7670             fn: fn,
7671             scope: scope
7672         });
7673     },
7674
7675     // private
7676     handleKeyDown : function(e){
7677             if(this.enabled){ //just in case
7678             var b = this.bindings;
7679             for(var i = 0, len = b.length; i < len; i++){
7680                 b[i].call(this, e);
7681             }
7682             }
7683         },
7684
7685         /**
7686          * Returns true if this KeyMap is enabled
7687          * @return {Boolean}
7688          */
7689         isEnabled : function(){
7690             return this.enabled;
7691         },
7692
7693         /**
7694          * Enables this KeyMap
7695          */
7696         enable: function(){
7697                 if(!this.enabled){
7698                     this.el.on(this.eventName, this.handleKeyDown, this);
7699                     this.enabled = true;
7700                 }
7701         },
7702
7703         /**
7704          * Disable this KeyMap
7705          */
7706         disable: function(){
7707                 if(this.enabled){
7708                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
7709                     this.enabled = false;
7710                 }
7711         },
7712     
7713     /**
7714      * Convenience function for setting disabled/enabled by boolean.
7715      * @param {Boolean} disabled
7716      */
7717     setDisabled : function(disabled){
7718         this[disabled ? "disable" : "enable"]();
7719     }
7720 };/**
7721  * @class Ext.util.TextMetrics
7722  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
7723  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
7724  * should not contain any HTML, otherwise it may not be measured correctly.
7725  * @singleton
7726  */
7727 Ext.util.TextMetrics = function(){
7728     var shared;
7729     return {
7730         /**
7731          * Measures the size of the specified text
7732          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
7733          * that can affect the size of the rendered text
7734          * @param {String} text The text to measure
7735          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7736          * in order to accurately measure the text height
7737          * @return {Object} An object containing the text's size {width: (width), height: (height)}
7738          */
7739         measure : function(el, text, fixedWidth){
7740             if(!shared){
7741                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
7742             }
7743             shared.bind(el);
7744             shared.setFixedWidth(fixedWidth || 'auto');
7745             return shared.getSize(text);
7746         },
7747
7748         /**
7749          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
7750          * the overhead of multiple calls to initialize the style properties on each measurement.
7751          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
7752          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7753          * in order to accurately measure the text height
7754          * @return {Ext.util.TextMetrics.Instance} instance The new instance
7755          */
7756         createInstance : function(el, fixedWidth){
7757             return Ext.util.TextMetrics.Instance(el, fixedWidth);
7758         }
7759     };
7760 }();
7761
7762 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
7763     var ml = new Ext.Element(document.createElement('div'));
7764     document.body.appendChild(ml.dom);
7765     ml.position('absolute');
7766     ml.setLeftTop(-1000, -1000);
7767     ml.hide();
7768
7769     if(fixedWidth){
7770         ml.setWidth(fixedWidth);
7771     }
7772
7773     var instance = {
7774         /**
7775          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7776          * Returns the size of the specified text based on the internal element's style and width properties
7777          * @param {String} text The text to measure
7778          * @return {Object} An object containing the text's size {width: (width), height: (height)}
7779          */
7780         getSize : function(text){
7781             ml.update(text);
7782             var s = ml.getSize();
7783             ml.update('');
7784             return s;
7785         },
7786
7787         /**
7788          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7789          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
7790          * that can affect the size of the rendered text
7791          * @param {String/HTMLElement} el The element, dom node or id
7792          */
7793         bind : function(el){
7794             ml.setStyle(
7795                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
7796             );
7797         },
7798
7799         /**
7800          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7801          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
7802          * to set a fixed width in order to accurately measure the text height.
7803          * @param {Number} width The width to set on the element
7804          */
7805         setFixedWidth : function(width){
7806             ml.setWidth(width);
7807         },
7808
7809         /**
7810          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7811          * Returns the measured width of the specified text
7812          * @param {String} text The text to measure
7813          * @return {Number} width The width in pixels
7814          */
7815         getWidth : function(text){
7816             ml.dom.style.width = 'auto';
7817             return this.getSize(text).width;
7818         },
7819
7820         /**
7821          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7822          * Returns the measured height of the specified text.  For multiline text, be sure to call
7823          * {@link #setFixedWidth} if necessary.
7824          * @param {String} text The text to measure
7825          * @return {Number} height The height in pixels
7826          */
7827         getHeight : function(text){
7828             return this.getSize(text).height;
7829         }
7830     };
7831
7832     instance.bind(bindTo);
7833
7834     return instance;
7835 };
7836
7837 Ext.Element.addMethods({
7838     /**
7839      * Returns the width in pixels of the passed text, or the width of the text in this Element.
7840      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
7841      * @param {Number} min (Optional) The minumum value to return.
7842      * @param {Number} max (Optional) The maximum value to return.
7843      * @return {Number} The text width in pixels.
7844      * @member Ext.Element getTextWidth
7845      */
7846     getTextWidth : function(text, min, max){
7847         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
7848     }
7849 });
7850 /**
7851  * @class Ext.util.Cookies
7852  * Utility class for managing and interacting with cookies.
7853  * @singleton
7854  */
7855 Ext.util.Cookies = {
7856     /**
7857      * Create a cookie with the specified name and value. Additional settings
7858      * for the cookie may be optionally specified (for example: expiration,
7859      * access restriction, SSL).
7860      * @param {String} name The name of the cookie to set. 
7861      * @param {Mixed} value The value to set for the cookie.
7862      * @param {Object} expires (Optional) Specify an expiration date the
7863      * cookie is to persist until.  Note that the specified Date object will
7864      * be converted to Greenwich Mean Time (GMT). 
7865      * @param {String} path (Optional) Setting a path on the cookie restricts
7866      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
7867      * @param {String} domain (Optional) Setting a domain restricts access to
7868      * pages on a given domain (typically used to allow cookie access across
7869      * subdomains). For example, "extjs.com" will create a cookie that can be
7870      * accessed from any subdomain of extjs.com, including www.extjs.com,
7871      * support.extjs.com, etc.
7872      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
7873      * should only be accessible via SSL on a page using the HTTPS protocol.
7874      * Defaults to <tt>false</tt>. Note that this will only work if the page
7875      * calling this code uses the HTTPS protocol, otherwise the cookie will be
7876      * created with default options.
7877      */
7878     set : function(name, value){
7879         var argv = arguments;
7880         var argc = arguments.length;
7881         var expires = (argc > 2) ? argv[2] : null;
7882         var path = (argc > 3) ? argv[3] : '/';
7883         var domain = (argc > 4) ? argv[4] : null;
7884         var secure = (argc > 5) ? argv[5] : false;
7885         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
7886     },
7887
7888     /**
7889      * Retrieves cookies that are accessible by the current page. If a cookie
7890      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
7891      * example retrieves the cookie called "valid" and stores the String value
7892      * in the variable <tt>validStatus</tt>.
7893      * <pre><code>
7894      * var validStatus = Ext.util.Cookies.get("valid");
7895      * </code></pre>
7896      * @param {String} name The name of the cookie to get
7897      * @return {Mixed} Returns the cookie value for the specified name;
7898      * null if the cookie name does not exist.
7899      */
7900     get : function(name){
7901         var arg = name + "=";
7902         var alen = arg.length;
7903         var clen = document.cookie.length;
7904         var i = 0;
7905         var j = 0;
7906         while(i < clen){
7907             j = i + alen;
7908             if(document.cookie.substring(i, j) == arg){
7909                 return Ext.util.Cookies.getCookieVal(j);
7910             }
7911             i = document.cookie.indexOf(" ", i) + 1;
7912             if(i === 0){
7913                 break;
7914             }
7915         }
7916         return null;
7917     },
7918
7919     /**
7920      * Removes a cookie with the provided name from the browser
7921      * if found by setting its expiration date to sometime in the past. 
7922      * @param {String} name The name of the cookie to remove
7923      */
7924     clear : function(name){
7925         if(Ext.util.Cookies.get(name)){
7926             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
7927         }
7928     },
7929     /**
7930      * @private
7931      */
7932     getCookieVal : function(offset){
7933         var endstr = document.cookie.indexOf(";", offset);
7934         if(endstr == -1){
7935             endstr = document.cookie.length;
7936         }
7937         return unescape(document.cookie.substring(offset, endstr));
7938     }
7939 };/**
7940  * Framework-wide error-handler.  Developers can override this method to provide
7941  * custom exception-handling.  Framework errors will often extend from the base
7942  * Ext.Error class.
7943  * @param {Object/Error} e The thrown exception object.
7944  */
7945 Ext.handleError = function(e) {
7946     throw e;
7947 };
7948
7949 /**
7950  * @class Ext.Error
7951  * @extends Error
7952  * <p>A base error class. Future implementations are intended to provide more
7953  * robust error handling throughout the framework (<b>in the debug build only</b>)
7954  * to check for common errors and problems. The messages issued by this class
7955  * will aid error checking. Error checks will be automatically removed in the
7956  * production build so that performance is not negatively impacted.</p>
7957  * <p>Some sample messages currently implemented:</p><pre>
7958 "DataProxy attempted to execute an API-action but found an undefined
7959 url / function. Please review your Proxy url/api-configuration."
7960  * </pre><pre>
7961 "Could not locate your "root" property in your server response.
7962 Please review your JsonReader config to ensure the config-property
7963 "root" matches the property your server-response.  See the JsonReader
7964 docs for additional assistance."
7965  * </pre>
7966  * <p>An example of the code used for generating error messages:</p><pre><code>
7967 try {
7968     generateError({
7969         foo: 'bar'
7970     });
7971 }
7972 catch (e) {
7973     console.error(e);
7974 }
7975 function generateError(data) {
7976     throw new Ext.Error('foo-error', data);
7977 }
7978  * </code></pre>
7979  * @param {String} message
7980  */
7981 Ext.Error = function(message) {
7982     // Try to read the message from Ext.Error.lang
7983     this.message = (this.lang[message]) ? this.lang[message] : message;
7984 };
7985
7986 Ext.Error.prototype = new Error();
7987 Ext.apply(Ext.Error.prototype, {
7988     // protected.  Extensions place their error-strings here.
7989     lang: {},
7990
7991     name: 'Ext.Error',
7992     /**
7993      * getName
7994      * @return {String}
7995      */
7996     getName : function() {
7997         return this.name;
7998     },
7999     /**
8000      * getMessage
8001      * @return {String}
8002      */
8003     getMessage : function() {
8004         return this.message;
8005     },
8006     /**
8007      * toJson
8008      * @return {String}
8009      */
8010     toJson : function() {
8011         return Ext.encode(this);
8012     }
8013 });