Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / pkgs / ext-foundation-debug.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.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                 i = 0,
4705                 obj,
4706                 last;
4707
4708             for (; i < format.length; ++i) {
4709                 ch = format.charAt(i);
4710                 if (!special && ch == "\\") {
4711                     special = true;
4712                 } else if (special) {
4713                     special = false;
4714                     regex.push(String.escape(ch));
4715                 } else {
4716                     obj = $f(ch, currentGroup);
4717                     currentGroup += obj.g;
4718                     regex.push(obj.s);
4719                     if (obj.g && obj.c) {
4720                         if (obj.calcLast) {
4721                             last = obj.c;
4722                         } else {
4723                             calc.push(obj.c);
4724                         }
4725                     }
4726                 }
4727             }
4728             
4729             if (last) {
4730                 calc.push(last);
4731             }
4732
4733             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4734             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
4735         };
4736     }(),
4737
4738     // private
4739     parseCodes : {
4740         /*
4741          * Notes:
4742          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4743          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4744          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4745          */
4746         d: {
4747             g:1,
4748             c:"d = parseInt(results[{0}], 10);\n",
4749             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4750         },
4751         j: {
4752             g:1,
4753             c:"d = parseInt(results[{0}], 10);\n",
4754             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4755         },
4756         D: function() {
4757             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
4758             return {
4759                 g:0,
4760                 c:null,
4761                 s:"(?:" + a.join("|") +")"
4762             };
4763         },
4764         l: function() {
4765             return {
4766                 g:0,
4767                 c:null,
4768                 s:"(?:" + Date.dayNames.join("|") + ")"
4769             };
4770         },
4771         N: {
4772             g:0,
4773             c:null,
4774             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4775         },
4776         S: {
4777             g:0,
4778             c:null,
4779             s:"(?:st|nd|rd|th)"
4780         },
4781         w: {
4782             g:0,
4783             c:null,
4784             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4785         },
4786         z: {
4787             g:1,
4788             c:"z = parseInt(results[{0}], 10);\n",
4789             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4790         },
4791         W: {
4792             g:0,
4793             c:null,
4794             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4795         },
4796         F: function() {
4797             return {
4798                 g:1,
4799                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4800                 s:"(" + Date.monthNames.join("|") + ")"
4801             };
4802         },
4803         M: function() {
4804             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
4805             return Ext.applyIf({
4806                 s:"(" + a.join("|") + ")"
4807             }, $f("F"));
4808         },
4809         m: {
4810             g:1,
4811             c:"m = parseInt(results[{0}], 10) - 1;\n",
4812             s:"(\\d{2})" // month number with leading zeros (01 - 12)
4813         },
4814         n: {
4815             g:1,
4816             c:"m = parseInt(results[{0}], 10) - 1;\n",
4817             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4818         },
4819         t: {
4820             g:0,
4821             c:null,
4822             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4823         },
4824         L: {
4825             g:0,
4826             c:null,
4827             s:"(?:1|0)"
4828         },
4829         o: function() {
4830             return $f("Y");
4831         },
4832         Y: {
4833             g:1,
4834             c:"y = parseInt(results[{0}], 10);\n",
4835             s:"(\\d{4})" // 4-digit year
4836         },
4837         y: {
4838             g:1,
4839             c:"var ty = parseInt(results[{0}], 10);\n"
4840                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4841             s:"(\\d{1,2})"
4842         },
4843         /**
4844          * In the am/pm parsing routines, we allow both upper and lower case 
4845          * even though it doesn't exactly match the spec. It gives much more flexibility
4846          * in being able to specify case insensitive regexes.
4847          */
4848         a: function(){
4849             return $f("A");
4850         },
4851         A: {
4852             // We need to calculate the hour before we apply AM/PM when parsing
4853             calcLast: true,
4854             g:1,
4855             c:"if (/(am)/i.test(results[{0}])) {\n"
4856                 + "if (!h || h == 12) { h = 0; }\n"
4857                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4858             s:"(AM|PM|am|pm)"
4859         },
4860         g: function() {
4861             return $f("G");
4862         },
4863         G: {
4864             g:1,
4865             c:"h = parseInt(results[{0}], 10);\n",
4866             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4867         },
4868         h: function() {
4869             return $f("H");
4870         },
4871         H: {
4872             g:1,
4873             c:"h = parseInt(results[{0}], 10);\n",
4874             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
4875         },
4876         i: {
4877             g:1,
4878             c:"i = parseInt(results[{0}], 10);\n",
4879             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4880         },
4881         s: {
4882             g:1,
4883             c:"s = parseInt(results[{0}], 10);\n",
4884             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4885         },
4886         u: {
4887             g:1,
4888             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4889             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4890         },
4891         O: {
4892             g:1,
4893             c:[
4894                 "o = results[{0}];",
4895                 "var sn = o.substring(0,1),", // get + / - sign
4896                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4897                     "mn = o.substring(3,5) % 60;", // get minutes
4898                 "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
4899             ].join("\n"),
4900             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4901         },
4902         P: {
4903             g:1,
4904             c:[
4905                 "o = results[{0}];",
4906                 "var sn = o.substring(0,1),", // get + / - sign
4907                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4908                     "mn = o.substring(4,6) % 60;", // get minutes
4909                 "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
4910             ].join("\n"),
4911             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4912         },
4913         T: {
4914             g:0,
4915             c:null,
4916             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4917         },
4918         Z: {
4919             g:1,
4920             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4921                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4922             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4923         },
4924         c: function() {
4925             var calc = [],
4926                 arr = [
4927                     $f("Y", 1), // year
4928                     $f("m", 2), // month
4929                     $f("d", 3), // day
4930                     $f("h", 4), // hour
4931                     $f("i", 5), // minute
4932                     $f("s", 6), // second
4933                     {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)
4934                     {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
4935                         "if(results[8]) {", // timezone specified
4936                             "if(results[8] == 'Z'){",
4937                                 "zz = 0;", // UTC
4938                             "}else if (results[8].indexOf(':') > -1){",
4939                                 $f("P", 8).c, // timezone offset with colon separator
4940                             "}else{",
4941                                 $f("O", 8).c, // timezone offset without colon separator
4942                             "}",
4943                         "}"
4944                     ].join('\n')}
4945                 ];
4946
4947             for (var i = 0, l = arr.length; i < l; ++i) {
4948                 calc.push(arr[i].c);
4949             }
4950
4951             return {
4952                 g:1,
4953                 c:calc.join(""),
4954                 s:[
4955                     arr[0].s, // year (required)
4956                     "(?:", "-", arr[1].s, // month (optional)
4957                         "(?:", "-", arr[2].s, // day (optional)
4958                             "(?:",
4959                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4960                                 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
4961                                 "(?::", arr[5].s, ")?", // seconds (optional)
4962                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4963                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4964                             ")?",
4965                         ")?",
4966                     ")?"
4967                 ].join("")
4968             };
4969         },
4970         U: {
4971             g:1,
4972             c:"u = parseInt(results[{0}], 10);\n",
4973             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4974         }
4975     }
4976 });
4977
4978 }());
4979
4980 Ext.apply(Date.prototype, {
4981     // private
4982     dateFormat : function(format) {
4983         if (Date.formatFunctions[format] == null) {
4984             Date.createFormat(format);
4985         }
4986         return Date.formatFunctions[format].call(this);
4987     },
4988
4989     /**
4990      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4991      *
4992      * Note: The date string returned by the javascript Date object's toString() method varies
4993      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4994      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4995      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4996      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4997      * from the GMT offset portion of the date string.
4998      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4999      */
5000     getTimezone : function() {
5001         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
5002         //
5003         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
5004         // 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)
5005         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
5006         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
5007         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
5008         //
5009         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
5010         // step 1: (?:\((.*)\) -- find timezone in parentheses
5011         // 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
5012         // step 3: remove all non uppercase characters found in step 1 and 2
5013         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
5014     },
5015
5016     /**
5017      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
5018      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
5019      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
5020      */
5021     getGMTOffset : function(colon) {
5022         return (this.getTimezoneOffset() > 0 ? "-" : "+")
5023             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
5024             + (colon ? ":" : "")
5025             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
5026     },
5027
5028     /**
5029      * Get the numeric day number of the year, adjusted for leap year.
5030      * @return {Number} 0 to 364 (365 in leap years).
5031      */
5032     getDayOfYear: function() {
5033         var num = 0,
5034             d = this.clone(),
5035             m = this.getMonth(),
5036             i;
5037
5038         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
5039             num += d.getDaysInMonth();
5040         }
5041         return num + this.getDate() - 1;
5042     },
5043
5044     /**
5045      * Get the numeric ISO-8601 week number of the year.
5046      * (equivalent to the format specifier 'W', but without a leading zero).
5047      * @return {Number} 1 to 53
5048      */
5049     getWeekOfYear : function() {
5050         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
5051         var ms1d = 864e5, // milliseconds in a day
5052             ms7d = 7 * ms1d; // milliseconds in a week
5053
5054         return function() { // return a closure so constants get calculated only once
5055             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
5056                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
5057                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
5058
5059             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
5060         };
5061     }(),
5062
5063     /**
5064      * Checks if the current date falls within a leap year.
5065      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
5066      */
5067     isLeapYear : function() {
5068         var year = this.getFullYear();
5069         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
5070     },
5071
5072     /**
5073      * Get the first day of the current month, adjusted for leap year.  The returned value
5074      * is the numeric day index within the week (0-6) which can be used in conjunction with
5075      * the {@link #monthNames} array to retrieve the textual day name.
5076      * Example:
5077      * <pre><code>
5078 var dt = new Date('1/10/2007');
5079 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
5080 </code></pre>
5081      * @return {Number} The day number (0-6).
5082      */
5083     getFirstDayOfMonth : function() {
5084         var day = (this.getDay() - (this.getDate() - 1)) % 7;
5085         return (day < 0) ? (day + 7) : day;
5086     },
5087
5088     /**
5089      * Get the last day of the current month, adjusted for leap year.  The returned value
5090      * is the numeric day index within the week (0-6) which can be used in conjunction with
5091      * the {@link #monthNames} array to retrieve the textual day name.
5092      * Example:
5093      * <pre><code>
5094 var dt = new Date('1/10/2007');
5095 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
5096 </code></pre>
5097      * @return {Number} The day number (0-6).
5098      */
5099     getLastDayOfMonth : function() {
5100         return this.getLastDateOfMonth().getDay();
5101     },
5102
5103
5104     /**
5105      * Get the date of the first day of the month in which this date resides.
5106      * @return {Date}
5107      */
5108     getFirstDateOfMonth : function() {
5109         return new Date(this.getFullYear(), this.getMonth(), 1);
5110     },
5111
5112     /**
5113      * Get the date of the last day of the month in which this date resides.
5114      * @return {Date}
5115      */
5116     getLastDateOfMonth : function() {
5117         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
5118     },
5119
5120     /**
5121      * Get the number of days in the current month, adjusted for leap year.
5122      * @return {Number} The number of days in the month.
5123      */
5124     getDaysInMonth: function() {
5125         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5126
5127         return function() { // return a closure for efficiency
5128             var m = this.getMonth();
5129
5130             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
5131         };
5132     }(),
5133
5134     /**
5135      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
5136      * @return {String} 'st, 'nd', 'rd' or 'th'.
5137      */
5138     getSuffix : function() {
5139         switch (this.getDate()) {
5140             case 1:
5141             case 21:
5142             case 31:
5143                 return "st";
5144             case 2:
5145             case 22:
5146                 return "nd";
5147             case 3:
5148             case 23:
5149                 return "rd";
5150             default:
5151                 return "th";
5152         }
5153     },
5154
5155     /**
5156      * Creates and returns a new Date instance with the exact same date value as the called instance.
5157      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
5158      * variable will also be changed.  When the intention is to create a new variable that will not
5159      * modify the original instance, you should create a clone.
5160      *
5161      * Example of correctly cloning a date:
5162      * <pre><code>
5163 //wrong way:
5164 var orig = new Date('10/1/2006');
5165 var copy = orig;
5166 copy.setDate(5);
5167 document.write(orig);  //returns 'Thu Oct 05 2006'!
5168
5169 //correct way:
5170 var orig = new Date('10/1/2006');
5171 var copy = orig.clone();
5172 copy.setDate(5);
5173 document.write(orig);  //returns 'Thu Oct 01 2006'
5174 </code></pre>
5175      * @return {Date} The new Date instance.
5176      */
5177     clone : function() {
5178         return new Date(this.getTime());
5179     },
5180
5181     /**
5182      * Checks if the current date is affected by Daylight Saving Time (DST).
5183      * @return {Boolean} True if the current date is affected by DST.
5184      */
5185     isDST : function() {
5186         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
5187         // courtesy of @geoffrey.mcgill
5188         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
5189     },
5190
5191     /**
5192      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
5193      * automatically adjusting for Daylight Saving Time (DST) where applicable.
5194      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
5195      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
5196      * @return {Date} this or the clone.
5197      */
5198     clearTime : function(clone) {
5199         if (clone) {
5200             return this.clone().clearTime();
5201         }
5202
5203         // get current date before clearing time
5204         var d = this.getDate();
5205
5206         // clear time
5207         this.setHours(0);
5208         this.setMinutes(0);
5209         this.setSeconds(0);
5210         this.setMilliseconds(0);
5211
5212         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
5213             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
5214             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
5215
5216             // increment hour until cloned date == current date
5217             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
5218
5219             this.setDate(d);
5220             this.setHours(c.getHours());
5221         }
5222
5223         return this;
5224     },
5225
5226     /**
5227      * Provides a convenient method for performing basic date arithmetic. This method
5228      * does not modify the Date instance being called - it creates and returns
5229      * a new Date instance containing the resulting date value.
5230      *
5231      * Examples:
5232      * <pre><code>
5233 // Basic usage:
5234 var dt = new Date('10/29/2006').add(Date.DAY, 5);
5235 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
5236
5237 // Negative values will be subtracted:
5238 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
5239 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
5240
5241 // You can even chain several calls together in one line:
5242 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
5243 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
5244 </code></pre>
5245      *
5246      * @param {String} interval A valid date interval enum value.
5247      * @param {Number} value The amount to add to the current date.
5248      * @return {Date} The new Date instance.
5249      */
5250     add : function(interval, value) {
5251         var d = this.clone();
5252         if (!interval || value === 0) return d;
5253
5254         switch(interval.toLowerCase()) {
5255             case Date.MILLI:
5256                 d.setMilliseconds(this.getMilliseconds() + value);
5257                 break;
5258             case Date.SECOND:
5259                 d.setSeconds(this.getSeconds() + value);
5260                 break;
5261             case Date.MINUTE:
5262                 d.setMinutes(this.getMinutes() + value);
5263                 break;
5264             case Date.HOUR:
5265                 d.setHours(this.getHours() + value);
5266                 break;
5267             case Date.DAY:
5268                 d.setDate(this.getDate() + value);
5269                 break;
5270             case Date.MONTH:
5271                 var day = this.getDate();
5272                 if (day > 28) {
5273                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
5274                 }
5275                 d.setDate(day);
5276                 d.setMonth(this.getMonth() + value);
5277                 break;
5278             case Date.YEAR:
5279                 d.setFullYear(this.getFullYear() + value);
5280                 break;
5281         }
5282         return d;
5283     },
5284
5285     /**
5286      * Checks if this date falls on or between the given start and end dates.
5287      * @param {Date} start Start date
5288      * @param {Date} end End date
5289      * @return {Boolean} true if this date falls on or between the given start and end dates.
5290      */
5291     between : function(start, end) {
5292         var t = this.getTime();
5293         return start.getTime() <= t && t <= end.getTime();
5294     }
5295 });
5296
5297
5298 /**
5299  * Formats a date given the supplied format string.
5300  * @param {String} format The format string.
5301  * @return {String} The formatted date.
5302  * @method format
5303  */
5304 Date.prototype.format = Date.prototype.dateFormat;
5305
5306
5307 // private
5308 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
5309     Ext.apply(Date.prototype, {
5310         _xMonth : Date.prototype.setMonth,
5311         _xDate  : Date.prototype.setDate,
5312
5313         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
5314         // Date.setMonth does not work consistently if iMonth is not 0-11
5315         setMonth : function(num) {
5316             if (num <= -1) {
5317                 var n = Math.ceil(-num),
5318                     back_year = Math.ceil(n / 12),
5319                     month = (n % 12) ? 12 - n % 12 : 0;
5320
5321                 this.setFullYear(this.getFullYear() - back_year);
5322
5323                 return this._xMonth(month);
5324             } else {
5325                 return this._xMonth(num);
5326             }
5327         },
5328
5329         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
5330         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
5331         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
5332         setDate : function(d) {
5333             // use setTime() to workaround setDate() bug
5334             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
5335             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
5336         }
5337     });
5338 }
5339
5340
5341
5342 /* Some basic Date tests... (requires Firebug)
5343
5344 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
5345 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
5346
5347 // standard tests
5348 console.group('Standard Date.parseDate() Tests');
5349     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
5350     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
5351     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
5352     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
5353     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
5354 console.groupEnd();
5355
5356 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
5357 // -- accepts ALL 6 levels of date-time granularity
5358 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
5359     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
5360     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
5361     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
5362     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)
5363     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)
5364     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)
5365     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)
5366     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
5367 console.groupEnd();
5368
5369 */
5370 /**
5371  * @class Ext.util.MixedCollection
5372  * @extends Ext.util.Observable
5373  * A Collection class that maintains both numeric indexes and keys and exposes events.
5374  * @constructor
5375  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5376  * function should add function references to the collection. Defaults to
5377  * <tt>false</tt>.
5378  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
5379  * and return the key value for that item.  This is used when available to look up the key on items that
5380  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
5381  * equivalent to providing an implementation for the {@link #getKey} method.
5382  */
5383 Ext.util.MixedCollection = function(allowFunctions, keyFn){
5384     this.items = [];
5385     this.map = {};
5386     this.keys = [];
5387     this.length = 0;
5388     this.addEvents(
5389         /**
5390          * @event clear
5391          * Fires when the collection is cleared.
5392          */
5393         'clear',
5394         /**
5395          * @event add
5396          * Fires when an item is added to the collection.
5397          * @param {Number} index The index at which the item was added.
5398          * @param {Object} o The item added.
5399          * @param {String} key The key associated with the added item.
5400          */
5401         'add',
5402         /**
5403          * @event replace
5404          * Fires when an item is replaced in the collection.
5405          * @param {String} key he key associated with the new added.
5406          * @param {Object} old The item being replaced.
5407          * @param {Object} new The new item.
5408          */
5409         'replace',
5410         /**
5411          * @event remove
5412          * Fires when an item is removed from the collection.
5413          * @param {Object} o The item being removed.
5414          * @param {String} key (optional) The key associated with the removed item.
5415          */
5416         'remove',
5417         'sort'
5418     );
5419     this.allowFunctions = allowFunctions === true;
5420     if(keyFn){
5421         this.getKey = keyFn;
5422     }
5423     Ext.util.MixedCollection.superclass.constructor.call(this);
5424 };
5425
5426 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
5427
5428     /**
5429      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5430      * function should add function references to the collection. Defaults to
5431      * <tt>false</tt>.
5432      */
5433     allowFunctions : false,
5434
5435     /**
5436      * Adds an item to the collection. Fires the {@link #add} event when complete.
5437      * @param {String} key <p>The key to associate with the item, or the new item.</p>
5438      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
5439      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
5440      * the MixedCollection will be able to <i>derive</i> the key for the new item.
5441      * In this case just pass the new item in this parameter.</p>
5442      * @param {Object} o The item to add.
5443      * @return {Object} The item added.
5444      */
5445     add : function(key, o){
5446         if(arguments.length == 1){
5447             o = arguments[0];
5448             key = this.getKey(o);
5449         }
5450         if(typeof key != 'undefined' && key !== null){
5451             var old = this.map[key];
5452             if(typeof old != 'undefined'){
5453                 return this.replace(key, o);
5454             }
5455             this.map[key] = o;
5456         }
5457         this.length++;
5458         this.items.push(o);
5459         this.keys.push(key);
5460         this.fireEvent('add', this.length-1, o, key);
5461         return o;
5462     },
5463
5464     /**
5465       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
5466       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
5467       * to return a different value as in the following examples:<pre><code>
5468 // normal way
5469 var mc = new Ext.util.MixedCollection();
5470 mc.add(someEl.dom.id, someEl);
5471 mc.add(otherEl.dom.id, otherEl);
5472 //and so on
5473
5474 // using getKey
5475 var mc = new Ext.util.MixedCollection();
5476 mc.getKey = function(el){
5477    return el.dom.id;
5478 };
5479 mc.add(someEl);
5480 mc.add(otherEl);
5481
5482 // or via the constructor
5483 var mc = new Ext.util.MixedCollection(false, function(el){
5484    return el.dom.id;
5485 });
5486 mc.add(someEl);
5487 mc.add(otherEl);
5488      * </code></pre>
5489      * @param {Object} item The item for which to find the key.
5490      * @return {Object} The key for the passed item.
5491      */
5492     getKey : function(o){
5493          return o.id;
5494     },
5495
5496     /**
5497      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
5498      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
5499      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
5500      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
5501      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
5502      * with one having the same key value, then just pass the replacement item in this parameter.</p>
5503      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
5504      * with that key.
5505      * @return {Object}  The new item.
5506      */
5507     replace : function(key, o){
5508         if(arguments.length == 1){
5509             o = arguments[0];
5510             key = this.getKey(o);
5511         }
5512         var old = this.map[key];
5513         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
5514              return this.add(key, o);
5515         }
5516         var index = this.indexOfKey(key);
5517         this.items[index] = o;
5518         this.map[key] = o;
5519         this.fireEvent('replace', key, old, o);
5520         return o;
5521     },
5522
5523     /**
5524      * Adds all elements of an Array or an Object to the collection.
5525      * @param {Object/Array} objs An Object containing properties which will be added
5526      * to the collection, or an Array of values, each of which are added to the collection.
5527      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
5528      * has been set to <tt>true</tt>.
5529      */
5530     addAll : function(objs){
5531         if(arguments.length > 1 || Ext.isArray(objs)){
5532             var args = arguments.length > 1 ? arguments : objs;
5533             for(var i = 0, len = args.length; i < len; i++){
5534                 this.add(args[i]);
5535             }
5536         }else{
5537             for(var key in objs){
5538                 if(this.allowFunctions || typeof objs[key] != 'function'){
5539                     this.add(key, objs[key]);
5540                 }
5541             }
5542         }
5543     },
5544
5545     /**
5546      * Executes the specified function once for every item in the collection, passing the following arguments:
5547      * <div class="mdetail-params"><ul>
5548      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
5549      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
5550      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
5551      * </ul></div>
5552      * The function should return a boolean value. Returning false from the function will stop the iteration.
5553      * @param {Function} fn The function to execute for each item.
5554      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
5555      */
5556     each : function(fn, scope){
5557         var items = [].concat(this.items); // each safe for removal
5558         for(var i = 0, len = items.length; i < len; i++){
5559             if(fn.call(scope || items[i], items[i], i, len) === false){
5560                 break;
5561             }
5562         }
5563     },
5564
5565     /**
5566      * Executes the specified function once for every key in the collection, passing each
5567      * key, and its associated item as the first two parameters.
5568      * @param {Function} fn The function to execute for each item.
5569      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5570      */
5571     eachKey : function(fn, scope){
5572         for(var i = 0, len = this.keys.length; i < len; i++){
5573             fn.call(scope || window, this.keys[i], this.items[i], i, len);
5574         }
5575     },
5576
5577     /**
5578      * Returns the first item in the collection which elicits a true return value from the
5579      * passed selection function.
5580      * @param {Function} fn The selection function to execute for each item.
5581      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5582      * @return {Object} The first item in the collection which returned true from the selection function.
5583      */
5584     find : function(fn, scope){
5585         for(var i = 0, len = this.items.length; i < len; i++){
5586             if(fn.call(scope || window, this.items[i], this.keys[i])){
5587                 return this.items[i];
5588             }
5589         }
5590         return null;
5591     },
5592
5593     /**
5594      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
5595      * @param {Number} index The index to insert the item at.
5596      * @param {String} key The key to associate with the new item, or the item itself.
5597      * @param {Object} o (optional) If the second parameter was a key, the new item.
5598      * @return {Object} The item inserted.
5599      */
5600     insert : function(index, key, o){
5601         if(arguments.length == 2){
5602             o = arguments[1];
5603             key = this.getKey(o);
5604         }
5605         if(this.containsKey(key)){
5606             this.suspendEvents();
5607             this.removeKey(key);
5608             this.resumeEvents();
5609         }
5610         if(index >= this.length){
5611             return this.add(key, o);
5612         }
5613         this.length++;
5614         this.items.splice(index, 0, o);
5615         if(typeof key != 'undefined' && key !== null){
5616             this.map[key] = o;
5617         }
5618         this.keys.splice(index, 0, key);
5619         this.fireEvent('add', index, o, key);
5620         return o;
5621     },
5622
5623     /**
5624      * Remove an item from the collection.
5625      * @param {Object} o The item to remove.
5626      * @return {Object} The item removed or false if no item was removed.
5627      */
5628     remove : function(o){
5629         return this.removeAt(this.indexOf(o));
5630     },
5631
5632     /**
5633      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
5634      * @param {Number} index The index within the collection of the item to remove.
5635      * @return {Object} The item removed or false if no item was removed.
5636      */
5637     removeAt : function(index){
5638         if(index < this.length && index >= 0){
5639             this.length--;
5640             var o = this.items[index];
5641             this.items.splice(index, 1);
5642             var key = this.keys[index];
5643             if(typeof key != 'undefined'){
5644                 delete this.map[key];
5645             }
5646             this.keys.splice(index, 1);
5647             this.fireEvent('remove', o, key);
5648             return o;
5649         }
5650         return false;
5651     },
5652
5653     /**
5654      * Removed an item associated with the passed key fom the collection.
5655      * @param {String} key The key of the item to remove.
5656      * @return {Object} The item removed or false if no item was removed.
5657      */
5658     removeKey : function(key){
5659         return this.removeAt(this.indexOfKey(key));
5660     },
5661
5662     /**
5663      * Returns the number of items in the collection.
5664      * @return {Number} the number of items in the collection.
5665      */
5666     getCount : function(){
5667         return this.length;
5668     },
5669
5670     /**
5671      * Returns index within the collection of the passed Object.
5672      * @param {Object} o The item to find the index of.
5673      * @return {Number} index of the item. Returns -1 if not found.
5674      */
5675     indexOf : function(o){
5676         return this.items.indexOf(o);
5677     },
5678
5679     /**
5680      * Returns index within the collection of the passed key.
5681      * @param {String} key The key to find the index of.
5682      * @return {Number} index of the key.
5683      */
5684     indexOfKey : function(key){
5685         return this.keys.indexOf(key);
5686     },
5687
5688     /**
5689      * Returns the item associated with the passed key OR index.
5690      * Key has priority over index.  This is the equivalent
5691      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
5692      * @param {String/Number} key The key or index of the item.
5693      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
5694      * If an item was found, but is a Class, returns <tt>null</tt>.
5695      */
5696     item : function(key){
5697         var mk = this.map[key],
5698             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
5699         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
5700     },
5701
5702     /**
5703      * Returns the item at the specified index.
5704      * @param {Number} index The index of the item.
5705      * @return {Object} The item at the specified index.
5706      */
5707     itemAt : function(index){
5708         return this.items[index];
5709     },
5710
5711     /**
5712      * Returns the item associated with the passed key.
5713      * @param {String/Number} key The key of the item.
5714      * @return {Object} The item associated with the passed key.
5715      */
5716     key : function(key){
5717         return this.map[key];
5718     },
5719
5720     /**
5721      * Returns true if the collection contains the passed Object as an item.
5722      * @param {Object} o  The Object to look for in the collection.
5723      * @return {Boolean} True if the collection contains the Object as an item.
5724      */
5725     contains : function(o){
5726         return this.indexOf(o) != -1;
5727     },
5728
5729     /**
5730      * Returns true if the collection contains the passed Object as a key.
5731      * @param {String} key The key to look for in the collection.
5732      * @return {Boolean} True if the collection contains the Object as a key.
5733      */
5734     containsKey : function(key){
5735         return typeof this.map[key] != 'undefined';
5736     },
5737
5738     /**
5739      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
5740      */
5741     clear : function(){
5742         this.length = 0;
5743         this.items = [];
5744         this.keys = [];
5745         this.map = {};
5746         this.fireEvent('clear');
5747     },
5748
5749     /**
5750      * Returns the first item in the collection.
5751      * @return {Object} the first item in the collection..
5752      */
5753     first : function(){
5754         return this.items[0];
5755     },
5756
5757     /**
5758      * Returns the last item in the collection.
5759      * @return {Object} the last item in the collection..
5760      */
5761     last : function(){
5762         return this.items[this.length-1];
5763     },
5764
5765     /**
5766      * @private
5767      * Performs the actual sorting based on a direction and a sorting function. Internally,
5768      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
5769      * the sorted array data back into this.items and this.keys
5770      * @param {String} property Property to sort by ('key', 'value', or 'index')
5771      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
5772      * @param {Function} fn (optional) Comparison function that defines the sort order.
5773      * Defaults to sorting by numeric value.
5774      */
5775     _sort : function(property, dir, fn){
5776         var i, len,
5777             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
5778
5779             //this is a temporary array used to apply the sorting function
5780             c     = [],
5781             keys  = this.keys,
5782             items = this.items;
5783
5784         //default to a simple sorter function if one is not provided
5785         fn = fn || function(a, b) {
5786             return a - b;
5787         };
5788
5789         //copy all the items into a temporary array, which we will sort
5790         for(i = 0, len = items.length; i < len; i++){
5791             c[c.length] = {
5792                 key  : keys[i],
5793                 value: items[i],
5794                 index: i
5795             };
5796         }
5797
5798         //sort the temporary array
5799         c.sort(function(a, b){
5800             var v = fn(a[property], b[property]) * dsc;
5801             if(v === 0){
5802                 v = (a.index < b.index ? -1 : 1);
5803             }
5804             return v;
5805         });
5806
5807         //copy the temporary array back into the main this.items and this.keys objects
5808         for(i = 0, len = c.length; i < len; i++){
5809             items[i] = c[i].value;
5810             keys[i]  = c[i].key;
5811         }
5812
5813         this.fireEvent('sort', this);
5814     },
5815
5816     /**
5817      * Sorts this collection by <b>item</b> value with the passed comparison function.
5818      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5819      * @param {Function} fn (optional) Comparison function that defines the sort order.
5820      * Defaults to sorting by numeric value.
5821      */
5822     sort : function(dir, fn){
5823         this._sort('value', dir, fn);
5824     },
5825
5826     /**
5827      * Reorders each of the items based on a mapping from old index to new index. Internally this
5828      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
5829      * @param {Object} mapping Mapping from old item index to new item index
5830      */
5831     reorder: function(mapping) {
5832         this.suspendEvents();
5833
5834         var items = this.items,
5835             index = 0,
5836             length = items.length,
5837             order = [],
5838             remaining = [],
5839             oldIndex;
5840
5841         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
5842         for (oldIndex in mapping) {
5843             order[mapping[oldIndex]] = items[oldIndex];
5844         }
5845
5846         for (index = 0; index < length; index++) {
5847             if (mapping[index] == undefined) {
5848                 remaining.push(items[index]);
5849             }
5850         }
5851
5852         for (index = 0; index < length; index++) {
5853             if (order[index] == undefined) {
5854                 order[index] = remaining.shift();
5855             }
5856         }
5857
5858         this.clear();
5859         this.addAll(order);
5860
5861         this.resumeEvents();
5862         this.fireEvent('sort', this);
5863     },
5864
5865     /**
5866      * Sorts this collection by <b>key</b>s.
5867      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5868      * @param {Function} fn (optional) Comparison function that defines the sort order.
5869      * Defaults to sorting by case insensitive string.
5870      */
5871     keySort : function(dir, fn){
5872         this._sort('key', dir, fn || function(a, b){
5873             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
5874             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5875         });
5876     },
5877
5878     /**
5879      * Returns a range of items in this collection
5880      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
5881      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
5882      * @return {Array} An array of items
5883      */
5884     getRange : function(start, end){
5885         var items = this.items;
5886         if(items.length < 1){
5887             return [];
5888         }
5889         start = start || 0;
5890         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
5891         var i, r = [];
5892         if(start <= end){
5893             for(i = start; i <= end; i++) {
5894                 r[r.length] = items[i];
5895             }
5896         }else{
5897             for(i = start; i >= end; i--) {
5898                 r[r.length] = items[i];
5899             }
5900         }
5901         return r;
5902     },
5903
5904     /**
5905      * Filter the <i>objects</i> in this collection by a specific property.
5906      * Returns a new collection that has been filtered.
5907      * @param {String} property A property on your objects
5908      * @param {String/RegExp} value Either string that the property values
5909      * should start with or a RegExp to test against the property
5910      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
5911      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
5912      * @return {MixedCollection} The new filtered collection
5913      */
5914     filter : function(property, value, anyMatch, caseSensitive){
5915         if(Ext.isEmpty(value, false)){
5916             return this.clone();
5917         }
5918         value = this.createValueMatcher(value, anyMatch, caseSensitive);
5919         return this.filterBy(function(o){
5920             return o && value.test(o[property]);
5921         });
5922     },
5923
5924     /**
5925      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
5926      * The passed function will be called with each object in the collection.
5927      * If the function returns true, the value is included otherwise it is filtered.
5928      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
5929      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5930      * @return {MixedCollection} The new filtered collection
5931      */
5932     filterBy : function(fn, scope){
5933         var r = new Ext.util.MixedCollection();
5934         r.getKey = this.getKey;
5935         var k = this.keys, it = this.items;
5936         for(var i = 0, len = it.length; i < len; i++){
5937             if(fn.call(scope||this, it[i], k[i])){
5938                 r.add(k[i], it[i]);
5939             }
5940         }
5941         return r;
5942     },
5943
5944     /**
5945      * Finds the index of the first matching object in this collection by a specific property/value.
5946      * @param {String} property The name of a property on your objects.
5947      * @param {String/RegExp} value A string that the property values
5948      * should start with or a RegExp to test against the property.
5949      * @param {Number} start (optional) The index to start searching at (defaults to 0).
5950      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
5951      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
5952      * @return {Number} The matched index or -1
5953      */
5954     findIndex : function(property, value, start, anyMatch, caseSensitive){
5955         if(Ext.isEmpty(value, false)){
5956             return -1;
5957         }
5958         value = this.createValueMatcher(value, anyMatch, caseSensitive);
5959         return this.findIndexBy(function(o){
5960             return o && value.test(o[property]);
5961         }, null, start);
5962     },
5963
5964     /**
5965      * Find the index of the first matching object in this collection by a function.
5966      * If the function returns <i>true</i> it is considered a match.
5967      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
5968      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5969      * @param {Number} start (optional) The index to start searching at (defaults to 0).
5970      * @return {Number} The matched index or -1
5971      */
5972     findIndexBy : function(fn, scope, start){
5973         var k = this.keys, it = this.items;
5974         for(var i = (start||0), len = it.length; i < len; i++){
5975             if(fn.call(scope||this, it[i], k[i])){
5976                 return i;
5977             }
5978         }
5979         return -1;
5980     },
5981
5982     /**
5983      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
5984      * and by Ext.data.Store#filter
5985      * @private
5986      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
5987      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
5988      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
5989      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
5990      */
5991     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
5992         if (!value.exec) { // not a regex
5993             var er = Ext.escapeRe;
5994             value = String(value);
5995
5996             if (anyMatch === true) {
5997                 value = er(value);
5998             } else {
5999                 value = '^' + er(value);
6000                 if (exactMatch === true) {
6001                     value += '$';
6002                 }
6003             }
6004             value = new RegExp(value, caseSensitive ? '' : 'i');
6005          }
6006          return value;
6007     },
6008
6009     /**
6010      * Creates a shallow copy of this collection
6011      * @return {MixedCollection}
6012      */
6013     clone : function(){
6014         var r = new Ext.util.MixedCollection();
6015         var k = this.keys, it = this.items;
6016         for(var i = 0, len = it.length; i < len; i++){
6017             r.add(k[i], it[i]);
6018         }
6019         r.getKey = this.getKey;
6020         return r;
6021     }
6022 });
6023 /**
6024  * This method calls {@link #item item()}.
6025  * Returns the item associated with the passed key OR index. Key has priority
6026  * over index.  This is the equivalent of calling {@link #key} first, then if
6027  * nothing matched calling {@link #itemAt}.
6028  * @param {String/Number} key The key or index of the item.
6029  * @return {Object} If the item is found, returns the item.  If the item was
6030  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
6031  * returns <tt>null</tt>.
6032  */
6033 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
6034 /**
6035  * @class Ext.AbstractManager
6036  * @extends Object
6037  * Base Manager class - extended by ComponentMgr and PluginMgr
6038  */
6039 Ext.AbstractManager = Ext.extend(Object, {
6040     typeName: 'type',
6041     
6042     constructor: function(config) {
6043         Ext.apply(this, config || {});
6044         
6045         /**
6046          * Contains all of the items currently managed
6047          * @property all
6048          * @type Ext.util.MixedCollection
6049          */
6050         this.all = new Ext.util.MixedCollection();
6051         
6052         this.types = {};
6053     },
6054     
6055     /**
6056      * Returns a component by {@link Ext.Component#id id}.
6057      * For additional details see {@link Ext.util.MixedCollection#get}.
6058      * @param {String} id The component {@link Ext.Component#id id}
6059      * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
6060      * Class was found.
6061      */
6062     get : function(id){
6063         return this.all.get(id);
6064     },
6065     
6066     /**
6067      * Registers an item to be managed
6068      * @param {Mixed} item The item to register
6069      */
6070     register: function(item) {
6071         this.all.add(item);
6072     },
6073     
6074     /**
6075      * Unregisters a component by removing it from this manager
6076      * @param {Mixed} item The item to unregister
6077      */
6078     unregister: function(item) {
6079         this.all.remove(item);        
6080     },
6081     
6082     /**
6083      * <p>Registers a new Component constructor, keyed by a new
6084      * {@link Ext.Component#xtype}.</p>
6085      * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
6086      * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
6087      * child Components.
6088      * see {@link Ext.Container#items}</p>
6089      * @param {String} xtype The mnemonic string by which the Component class may be looked up.
6090      * @param {Constructor} cls The new Component class.
6091      */
6092     registerType : function(type, cls){
6093         this.types[type] = cls;
6094         cls[this.typeName] = type;
6095     },
6096     
6097     /**
6098      * Checks if a Component type is registered.
6099      * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
6100      * @return {Boolean} Whether the type is registered.
6101      */
6102     isRegistered : function(type){
6103         return this.types[type] !== undefined;    
6104     },
6105     
6106     /**
6107      * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
6108      * @param {Object} config The config object
6109      * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
6110      * @return {Mixed} The instance of whatever this manager is managing
6111      */
6112     create: function(config, defaultType) {
6113         var type        = config[this.typeName] || config.type || defaultType,
6114             Constructor = this.types[type];
6115         
6116         if (Constructor == undefined) {
6117             throw new Error(String.format("The '{0}' type has not been registered with this manager", type));
6118         }
6119         
6120         return new Constructor(config);
6121     },
6122     
6123     /**
6124      * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation.
6125      * @param {String} id The component {@link Ext.Component#id id}
6126      * @param {Function} fn The callback function
6127      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
6128      */
6129     onAvailable : function(id, fn, scope){
6130         var all = this.all;
6131         
6132         all.on("add", function(index, o){
6133             if (o.id == id) {
6134                 fn.call(scope || o, o);
6135                 all.un("add", fn, scope);
6136             }
6137         });
6138     }
6139 });/**
6140  * @class Ext.util.Format
6141  * Reusable data formatting functions
6142  * @singleton
6143  */
6144 Ext.util.Format = function() {
6145     var trimRe         = /^\s+|\s+$/g,
6146         stripTagsRE    = /<\/?[^>]+>/gi,
6147         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
6148         nl2brRe        = /\r?\n/g;
6149
6150     return {
6151         /**
6152          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
6153          * @param {String} value The string to truncate
6154          * @param {Number} length The maximum length to allow before truncating
6155          * @param {Boolean} word True to try to find a common work break
6156          * @return {String} The converted text
6157          */
6158         ellipsis : function(value, len, word) {
6159             if (value && value.length > len) {
6160                 if (word) {
6161                     var vs    = value.substr(0, len - 2),
6162                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
6163                     if (index == -1 || index < (len - 15)) {
6164                         return value.substr(0, len - 3) + "...";
6165                     } else {
6166                         return vs.substr(0, index) + "...";
6167                     }
6168                 } else {
6169                     return value.substr(0, len - 3) + "...";
6170                 }
6171             }
6172             return value;
6173         },
6174
6175         /**
6176          * Checks a reference and converts it to empty string if it is undefined
6177          * @param {Mixed} value Reference to check
6178          * @return {Mixed} Empty string if converted, otherwise the original value
6179          */
6180         undef : function(value) {
6181             return value !== undefined ? value : "";
6182         },
6183
6184         /**
6185          * Checks a reference and converts it to the default value if it's empty
6186          * @param {Mixed} value Reference to check
6187          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
6188          * @return {String}
6189          */
6190         defaultValue : function(value, defaultValue) {
6191             return value !== undefined && value !== '' ? value : defaultValue;
6192         },
6193
6194         /**
6195          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
6196          * @param {String} value The string to encode
6197          * @return {String} The encoded text
6198          */
6199         htmlEncode : function(value) {
6200             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
6201         },
6202
6203         /**
6204          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
6205          * @param {String} value The string to decode
6206          * @return {String} The decoded text
6207          */
6208         htmlDecode : function(value) {
6209             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
6210         },
6211
6212         /**
6213          * Trims any whitespace from either side of a string
6214          * @param {String} value The text to trim
6215          * @return {String} The trimmed text
6216          */
6217         trim : function(value) {
6218             return String(value).replace(trimRe, "");
6219         },
6220
6221         /**
6222          * Returns a substring from within an original string
6223          * @param {String} value The original text
6224          * @param {Number} start The start index of the substring
6225          * @param {Number} length The length of the substring
6226          * @return {String} The substring
6227          */
6228         substr : function(value, start, length) {
6229             return String(value).substr(start, length);
6230         },
6231
6232         /**
6233          * Converts a string to all lower case letters
6234          * @param {String} value The text to convert
6235          * @return {String} The converted text
6236          */
6237         lowercase : function(value) {
6238             return String(value).toLowerCase();
6239         },
6240
6241         /**
6242          * Converts a string to all upper case letters
6243          * @param {String} value The text to convert
6244          * @return {String} The converted text
6245          */
6246         uppercase : function(value) {
6247             return String(value).toUpperCase();
6248         },
6249
6250         /**
6251          * Converts the first character only of a string to upper case
6252          * @param {String} value The text to convert
6253          * @return {String} The converted text
6254          */
6255         capitalize : function(value) {
6256             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
6257         },
6258
6259         // private
6260         call : function(value, fn) {
6261             if (arguments.length > 2) {
6262                 var args = Array.prototype.slice.call(arguments, 2);
6263                 args.unshift(value);
6264                 return eval(fn).apply(window, args);
6265             } else {
6266                 return eval(fn).call(window, value);
6267             }
6268         },
6269
6270         /**
6271          * Format a number as US currency
6272          * @param {Number/String} value The numeric value to format
6273          * @return {String} The formatted currency string
6274          */
6275         usMoney : function(v) {
6276             v = (Math.round((v-0)*100))/100;
6277             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
6278             v = String(v);
6279             var ps = v.split('.'),
6280                 whole = ps[0],
6281                 sub = ps[1] ? '.'+ ps[1] : '.00',
6282                 r = /(\d+)(\d{3})/;
6283             while (r.test(whole)) {
6284                 whole = whole.replace(r, '$1' + ',' + '$2');
6285             }
6286             v = whole + sub;
6287             if (v.charAt(0) == '-') {
6288                 return '-$' + v.substr(1);
6289             }
6290             return "$" +  v;
6291         },
6292
6293         /**
6294          * Parse a value into a formatted date using the specified format pattern.
6295          * @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)
6296          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
6297          * @return {String} The formatted date string
6298          */
6299         date : function(v, format) {
6300             if (!v) {
6301                 return "";
6302             }
6303             if (!Ext.isDate(v)) {
6304                 v = new Date(Date.parse(v));
6305             }
6306             return v.dateFormat(format || "m/d/Y");
6307         },
6308
6309         /**
6310          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
6311          * @param {String} format Any valid date format string
6312          * @return {Function} The date formatting function
6313          */
6314         dateRenderer : function(format) {
6315             return function(v) {
6316                 return Ext.util.Format.date(v, format);
6317             };
6318         },
6319
6320         /**
6321          * Strips all HTML tags
6322          * @param {Mixed} value The text from which to strip tags
6323          * @return {String} The stripped text
6324          */
6325         stripTags : function(v) {
6326             return !v ? v : String(v).replace(stripTagsRE, "");
6327         },
6328
6329         /**
6330          * Strips all script tags
6331          * @param {Mixed} value The text from which to strip script tags
6332          * @return {String} The stripped text
6333          */
6334         stripScripts : function(v) {
6335             return !v ? v : String(v).replace(stripScriptsRe, "");
6336         },
6337
6338         /**
6339          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
6340          * @param {Number/String} size The numeric value to format
6341          * @return {String} The formatted file size
6342          */
6343         fileSize : function(size) {
6344             if (size < 1024) {
6345                 return size + " bytes";
6346             } else if (size < 1048576) {
6347                 return (Math.round(((size*10) / 1024))/10) + " KB";
6348             } else {
6349                 return (Math.round(((size*10) / 1048576))/10) + " MB";
6350             }
6351         },
6352
6353         /**
6354          * It does simple math for use in a template, for example:<pre><code>
6355          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
6356          * </code></pre>
6357          * @return {Function} A function that operates on the passed value.
6358          */
6359         math : function(){
6360             var fns = {};
6361             
6362             return function(v, a){
6363                 if (!fns[a]) {
6364                     fns[a] = new Function('v', 'return v ' + a + ';');
6365                 }
6366                 return fns[a](v);
6367             };
6368         }(),
6369
6370         /**
6371          * Rounds the passed number to the required decimal precision.
6372          * @param {Number/String} value The numeric value to round.
6373          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
6374          * @return {Number} The rounded value.
6375          */
6376         round : function(value, precision) {
6377             var result = Number(value);
6378             if (typeof precision == 'number') {
6379                 precision = Math.pow(10, precision);
6380                 result = Math.round(value * precision) / precision;
6381             }
6382             return result;
6383         },
6384
6385         /**
6386          * Formats the number according to the format string.
6387          * <div style="margin-left:40px">examples (123456.789):
6388          * <div style="margin-left:10px">
6389          * 0 - (123456) show only digits, no precision<br>
6390          * 0.00 - (123456.78) show only digits, 2 precision<br>
6391          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
6392          * 0,000 - (123,456) show comma and digits, no precision<br>
6393          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
6394          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
6395          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
6396          * For example: 0.000,00/i
6397          * </div></div>
6398          * @param {Number} v The number to format.
6399          * @param {String} format The way you would like to format this text.
6400          * @return {String} The formatted number.
6401          */
6402         number: function(v, format) {
6403             if (!format) {
6404                 return v;
6405             }
6406             v = Ext.num(v, NaN);
6407             if (isNaN(v)) {
6408                 return '';
6409             }
6410             var comma = ',',
6411                 dec   = '.',
6412                 i18n  = false,
6413                 neg   = v < 0;
6414
6415             v = Math.abs(v);
6416             if (format.substr(format.length - 2) == '/i') {
6417                 format = format.substr(0, format.length - 2);
6418                 i18n   = true;
6419                 comma  = '.';
6420                 dec    = ',';
6421             }
6422
6423             var hasComma = format.indexOf(comma) != -1,
6424                 psplit   = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
6425
6426             if (1 < psplit.length) {
6427                 v = v.toFixed(psplit[1].length);
6428             } else if(2 < psplit.length) {
6429                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
6430             } else {
6431                 v = v.toFixed(0);
6432             }
6433
6434             var fnum = v.toString();
6435
6436             psplit = fnum.split('.');
6437
6438             if (hasComma) {
6439                 var cnum = psplit[0], 
6440                     parr = [], 
6441                     j    = cnum.length, 
6442                     m    = Math.floor(j / 3),
6443                     n    = cnum.length % 3 || 3,
6444                     i;
6445
6446                 for (i = 0; i < j; i += n) {
6447                     if (i != 0) {
6448                         n = 3;
6449                     }
6450                     
6451                     parr[parr.length] = cnum.substr(i, n);
6452                     m -= 1;
6453                 }
6454                 fnum = parr.join(comma);
6455                 if (psplit[1]) {
6456                     fnum += dec + psplit[1];
6457                 }
6458             } else {
6459                 if (psplit[1]) {
6460                     fnum = psplit[0] + dec + psplit[1];
6461                 }
6462             }
6463
6464             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
6465         },
6466
6467         /**
6468          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
6469          * @param {String} format Any valid number format string for {@link #number}
6470          * @return {Function} The number formatting function
6471          */
6472         numberRenderer : function(format) {
6473             return function(v) {
6474                 return Ext.util.Format.number(v, format);
6475             };
6476         },
6477
6478         /**
6479          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
6480          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
6481          * if the value is 0 or greater than 1.
6482          * @param {Number} value The value to compare against
6483          * @param {String} singular The singular form of the word
6484          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
6485          */
6486         plural : function(v, s, p) {
6487             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
6488         },
6489
6490         /**
6491          * Converts newline characters to the HTML tag &lt;br/>
6492          * @param {String} The string value to format.
6493          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
6494          */
6495         nl2br : function(v) {
6496             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
6497         }
6498     };
6499 }();
6500 /**
6501  * @class Ext.XTemplate
6502  * @extends Ext.Template
6503  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
6504  * <li>Autofilling arrays using templates and sub-templates</li>
6505  * <li>Conditional processing with basic comparison operators</li>
6506  * <li>Basic math function support</li>
6507  * <li>Execute arbitrary inline code with special built-in template variables</li>
6508  * <li>Custom member functions</li>
6509  * <li>Many special tags and built-in operators that aren't defined as part of
6510  * the API, but are supported in the templates that can be created</li>
6511  * </ul></div></p>
6512  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
6513  * <li>{@link Ext.DataView}</li>
6514  * <li>{@link Ext.ListView}</li>
6515  * <li>{@link Ext.form.ComboBox}</li>
6516  * <li>{@link Ext.grid.TemplateColumn}</li>
6517  * <li>{@link Ext.grid.GroupingView}</li>
6518  * <li>{@link Ext.menu.Item}</li>
6519  * <li>{@link Ext.layout.MenuLayout}</li>
6520  * <li>{@link Ext.ColorPalette}</li>
6521  * </ul></div></p>
6522  *
6523  * <p>For example usage {@link #XTemplate see the constructor}.</p>
6524  *
6525  * @constructor
6526  * The {@link Ext.Template#Template Ext.Template constructor} describes
6527  * the acceptable parameters to pass to the constructor. The following
6528  * examples demonstrate all of the supported features.</p>
6529  *
6530  * <div class="mdetail-params"><ul>
6531  *
6532  * <li><b><u>Sample Data</u></b>
6533  * <div class="sub-desc">
6534  * <p>This is the data object used for reference in each code example:</p>
6535  * <pre><code>
6536 var data = {
6537     name: 'Jack Slocum',
6538     title: 'Lead Developer',
6539     company: 'Ext JS, LLC',
6540     email: 'jack@extjs.com',
6541     address: '4 Red Bulls Drive',
6542     city: 'Cleveland',
6543     state: 'Ohio',
6544     zip: '44102',
6545     drinks: ['Red Bull', 'Coffee', 'Water'],
6546     kids: [{
6547         name: 'Sara Grace',
6548         age:3
6549     },{
6550         name: 'Zachary',
6551         age:2
6552     },{
6553         name: 'John James',
6554         age:0
6555     }]
6556 };
6557  * </code></pre>
6558  * </div>
6559  * </li>
6560  *
6561  *
6562  * <li><b><u>Auto filling of arrays</u></b>
6563  * <div class="sub-desc">
6564  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
6565  * to process the provided data object:
6566  * <ul>
6567  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
6568  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
6569  * array.</li>
6570  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
6571  * <li>While processing an array, the special variable <tt>{#}</tt>
6572  * will provide the current array index + 1 (starts at 1, not 0).</li>
6573  * </ul>
6574  * </p>
6575  * <pre><code>
6576 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
6577 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
6578 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
6579  * </code></pre>
6580  * Using the sample data above:
6581  * <pre><code>
6582 var tpl = new Ext.XTemplate(
6583     '&lt;p>Kids: ',
6584     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
6585         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
6586     '&lt;/tpl>&lt;/p>'
6587 );
6588 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
6589  * </code></pre>
6590  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
6591  * to access specified members of the provided data object to populate the template:</p>
6592  * <pre><code>
6593 var tpl = new Ext.XTemplate(
6594     '&lt;p>Name: {name}&lt;/p>',
6595     '&lt;p>Title: {title}&lt;/p>',
6596     '&lt;p>Company: {company}&lt;/p>',
6597     '&lt;p>Kids: ',
6598     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
6599         '&lt;p>{name}&lt;/p>',
6600     '&lt;/tpl>&lt;/p>'
6601 );
6602 tpl.overwrite(panel.body, data);  // pass the root node of the data object
6603  * </code></pre>
6604  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
6605  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
6606  * will represent the value of the array at the current index:</p>
6607  * <pre><code>
6608 var tpl = new Ext.XTemplate(
6609     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
6610     '&lt;tpl for="drinks">',
6611        '&lt;div> - {.}&lt;/div>',
6612     '&lt;/tpl>'
6613 );
6614 tpl.overwrite(panel.body, data);
6615  * </code></pre>
6616  * <p>When processing a sub-template, for example while looping through a child array,
6617  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
6618  * <pre><code>
6619 var tpl = new Ext.XTemplate(
6620     '&lt;p>Name: {name}&lt;/p>',
6621     '&lt;p>Kids: ',
6622     '&lt;tpl for="kids">',
6623         '&lt;tpl if="age > 1">',
6624             '&lt;p>{name}&lt;/p>',
6625             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
6626         '&lt;/tpl>',
6627     '&lt;/tpl>&lt;/p>'
6628 );
6629 tpl.overwrite(panel.body, data);
6630  * </code></pre>
6631  * </div>
6632  * </li>
6633  *
6634  *
6635  * <li><b><u>Conditional processing with basic comparison operators</u></b>
6636  * <div class="sub-desc">
6637  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
6638  * to provide conditional checks for deciding whether or not to render specific
6639  * parts of the template. Notes:<div class="sub-desc"><ul>
6640  * <li>Double quotes must be encoded if used within the conditional</li>
6641  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
6642  * <tt>if</tt> statements should be used.</li>
6643  * </ul></div>
6644  * <pre><code>
6645 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
6646 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
6647 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
6648 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
6649 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
6650 // no good:
6651 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
6652 // encode &#34; if it is part of the condition, e.g.
6653 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
6654  * </code></pre>
6655  * Using the sample data above:
6656  * <pre><code>
6657 var tpl = new Ext.XTemplate(
6658     '&lt;p>Name: {name}&lt;/p>',
6659     '&lt;p>Kids: ',
6660     '&lt;tpl for="kids">',
6661         '&lt;tpl if="age > 1">',
6662             '&lt;p>{name}&lt;/p>',
6663         '&lt;/tpl>',
6664     '&lt;/tpl>&lt;/p>'
6665 );
6666 tpl.overwrite(panel.body, data);
6667  * </code></pre>
6668  * </div>
6669  * </li>
6670  *
6671  *
6672  * <li><b><u>Basic math support</u></b>
6673  * <div class="sub-desc">
6674  * <p>The following basic math operators may be applied directly on numeric
6675  * data values:</p><pre>
6676  * + - * /
6677  * </pre>
6678  * For example:
6679  * <pre><code>
6680 var tpl = new Ext.XTemplate(
6681     '&lt;p>Name: {name}&lt;/p>',
6682     '&lt;p>Kids: ',
6683     '&lt;tpl for="kids">',
6684         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
6685             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
6686             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
6687             '&lt;p>Dad: {parent.name}&lt;/p>',
6688         '&lt;/tpl>',
6689     '&lt;/tpl>&lt;/p>'
6690 );
6691 tpl.overwrite(panel.body, data);
6692 </code></pre>
6693  * </div>
6694  * </li>
6695  *
6696  *
6697  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
6698  * <div class="sub-desc">
6699  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
6700  * in the scope of the template. There are some special variables available in that code:
6701  * <ul>
6702  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
6703  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
6704  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
6705  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
6706  * loop you are in (1-based).</li>
6707  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
6708  * of the array you are looping.</li>
6709  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
6710  * </ul>
6711  * This example demonstrates basic row striping using an inline code block and the
6712  * <tt>xindex</tt> variable:</p>
6713  * <pre><code>
6714 var tpl = new Ext.XTemplate(
6715     '&lt;p>Name: {name}&lt;/p>',
6716     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
6717     '&lt;p>Kids: ',
6718     '&lt;tpl for="kids">',
6719        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
6720         '{name}',
6721         '&lt;/div>',
6722     '&lt;/tpl>&lt;/p>'
6723 );
6724 tpl.overwrite(panel.body, data);
6725  * </code></pre>
6726  * </div>
6727  * </li>
6728  *
6729  * <li><b><u>Template member functions</u></b>
6730  * <div class="sub-desc">
6731  * <p>One or more member functions can be specified in a configuration
6732  * object passed into the XTemplate constructor for more complex processing:</p>
6733  * <pre><code>
6734 var tpl = new Ext.XTemplate(
6735     '&lt;p>Name: {name}&lt;/p>',
6736     '&lt;p>Kids: ',
6737     '&lt;tpl for="kids">',
6738         '&lt;tpl if="this.isGirl(name)">',
6739             '&lt;p>Girl: {name} - {age}&lt;/p>',
6740         '&lt;/tpl>',
6741         // use opposite if statement to simulate 'else' processing:
6742         '&lt;tpl if="this.isGirl(name) == false">',
6743             '&lt;p>Boy: {name} - {age}&lt;/p>',
6744         '&lt;/tpl>',
6745         '&lt;tpl if="this.isBaby(age)">',
6746             '&lt;p>{name} is a baby!&lt;/p>',
6747         '&lt;/tpl>',
6748     '&lt;/tpl>&lt;/p>',
6749     {
6750         // XTemplate configuration:
6751         compiled: true,
6752         disableFormats: true,
6753         // member functions:
6754         isGirl: function(name){
6755             return name == 'Sara Grace';
6756         },
6757         isBaby: function(age){
6758             return age < 1;
6759         }
6760     }
6761 );
6762 tpl.overwrite(panel.body, data);
6763  * </code></pre>
6764  * </div>
6765  * </li>
6766  *
6767  * </ul></div>
6768  *
6769  * @param {Mixed} config
6770  */
6771 Ext.XTemplate = function(){
6772     Ext.XTemplate.superclass.constructor.apply(this, arguments);
6773
6774     var me = this,
6775         s = me.html,
6776         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
6777         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
6778         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
6779         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
6780         m,
6781         id = 0,
6782         tpls = [],
6783         VALUES = 'values',
6784         PARENT = 'parent',
6785         XINDEX = 'xindex',
6786         XCOUNT = 'xcount',
6787         RETURN = 'return ',
6788         WITHVALUES = 'with(values){ ';
6789
6790     s = ['<tpl>', s, '</tpl>'].join('');
6791
6792     while((m = s.match(re))){
6793         var m2 = m[0].match(nameRe),
6794             m3 = m[0].match(ifRe),
6795             m4 = m[0].match(execRe),
6796             exp = null,
6797             fn = null,
6798             exec = null,
6799             name = m2 && m2[1] ? m2[1] : '';
6800
6801        if (m3) {
6802            exp = m3 && m3[1] ? m3[1] : null;
6803            if(exp){
6804                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
6805            }
6806        }
6807        if (m4) {
6808            exp = m4 && m4[1] ? m4[1] : null;
6809            if(exp){
6810                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
6811            }
6812        }
6813        if(name){
6814            switch(name){
6815                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
6816                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
6817                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
6818            }
6819        }
6820        tpls.push({
6821             id: id,
6822             target: name,
6823             exec: exec,
6824             test: fn,
6825             body: m[1]||''
6826         });
6827        s = s.replace(m[0], '{xtpl'+ id + '}');
6828        ++id;
6829     }
6830     for(var i = tpls.length-1; i >= 0; --i){
6831         me.compileTpl(tpls[i]);
6832     }
6833     me.master = tpls[tpls.length-1];
6834     me.tpls = tpls;
6835 };
6836 Ext.extend(Ext.XTemplate, Ext.Template, {
6837     // private
6838     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
6839     // private
6840     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
6841
6842     // private
6843     applySubTemplate : function(id, values, parent, xindex, xcount){
6844         var me = this,
6845             len,
6846             t = me.tpls[id],
6847             vs,
6848             buf = [];
6849         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
6850             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
6851             return '';
6852         }
6853         vs = t.target ? t.target.call(me, values, parent) : values;
6854         len = vs.length;
6855         parent = t.target ? values : parent;
6856         if(t.target && Ext.isArray(vs)){
6857             for(var i = 0, len = vs.length; i < len; i++){
6858                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
6859             }
6860             return buf.join('');
6861         }
6862         return t.compiled.call(me, vs, parent, xindex, xcount);
6863     },
6864
6865     // private
6866     compileTpl : function(tpl){
6867         var fm = Ext.util.Format,
6868             useF = this.disableFormats !== true,
6869             sep = Ext.isGecko ? "+" : ",",
6870             body;
6871
6872         function fn(m, name, format, args, math){
6873             if(name.substr(0, 4) == 'xtpl'){
6874                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
6875             }
6876             var v;
6877             if(name === '.'){
6878                 v = 'values';
6879             }else if(name === '#'){
6880                 v = 'xindex';
6881             }else if(name.indexOf('.') != -1){
6882                 v = name;
6883             }else{
6884                 v = "values['" + name + "']";
6885             }
6886             if(math){
6887                 v = '(' + v + math + ')';
6888             }
6889             if (format && useF) {
6890                 args = args ? ',' + args : "";
6891                 if(format.substr(0, 5) != "this."){
6892                     format = "fm." + format + '(';
6893                 }else{
6894                     format = 'this.call("'+ format.substr(5) + '", ';
6895                     args = ", values";
6896                 }
6897             } else {
6898                 args= ''; format = "("+v+" === undefined ? '' : ";
6899             }
6900             return "'"+ sep + format + v + args + ")"+sep+"'";
6901         }
6902
6903         function codeFn(m, code){
6904             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
6905             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
6906         }
6907
6908         // branched to use + in gecko and [].join() in others
6909         if(Ext.isGecko){
6910             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
6911                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
6912                     "';};";
6913         }else{
6914             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
6915             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
6916             body.push("'].join('');};");
6917             body = body.join('');
6918         }
6919         eval(body);
6920         return this;
6921     },
6922
6923     /**
6924      * Returns an HTML fragment of this template with the specified values applied.
6925      * @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'})
6926      * @return {String} The HTML fragment
6927      */
6928     applyTemplate : function(values){
6929         return this.master.compiled.call(this, values, {}, 1, 1);
6930     },
6931
6932     /**
6933      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
6934      * @return {Function} The compiled function
6935      */
6936     compile : function(){return this;}
6937
6938     /**
6939      * @property re
6940      * @hide
6941      */
6942     /**
6943      * @property disableFormats
6944      * @hide
6945      */
6946     /**
6947      * @method set
6948      * @hide
6949      */
6950
6951 });
6952 /**
6953  * Alias for {@link #applyTemplate}
6954  * Returns an HTML fragment of this template with the specified values applied.
6955  * @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'})
6956  * @return {String} The HTML fragment
6957  * @member Ext.XTemplate
6958  * @method apply
6959  */
6960 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
6961
6962 /**
6963  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6964  * @param {String/HTMLElement} el A DOM element or its id
6965  * @return {Ext.Template} The created template
6966  * @static
6967  */
6968 Ext.XTemplate.from = function(el){
6969     el = Ext.getDom(el);
6970     return new Ext.XTemplate(el.value || el.innerHTML);
6971 };
6972 /**
6973  * @class Ext.util.CSS
6974  * Utility class for manipulating CSS rules
6975  * @singleton
6976  */
6977 Ext.util.CSS = function(){
6978         var rules = null;
6979         var doc = document;
6980
6981     var camelRe = /(-[a-z])/gi;
6982     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6983
6984    return {
6985    /**
6986     * Creates a stylesheet from a text blob of rules.
6987     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
6988     * @param {String} cssText The text containing the css rules
6989     * @param {String} id An id to add to the stylesheet for later removal
6990     * @return {StyleSheet}
6991     */
6992    createStyleSheet : function(cssText, id){
6993        var ss;
6994        var head = doc.getElementsByTagName("head")[0];
6995        var rules = doc.createElement("style");
6996        rules.setAttribute("type", "text/css");
6997        if(id){
6998            rules.setAttribute("id", id);
6999        }
7000        if(Ext.isIE){
7001            head.appendChild(rules);
7002            ss = rules.styleSheet;
7003            ss.cssText = cssText;
7004        }else{
7005            try{
7006                 rules.appendChild(doc.createTextNode(cssText));
7007            }catch(e){
7008                rules.cssText = cssText;
7009            }
7010            head.appendChild(rules);
7011            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
7012        }
7013        this.cacheStyleSheet(ss);
7014        return ss;
7015    },
7016
7017    /**
7018     * Removes a style or link tag by id
7019     * @param {String} id The id of the tag
7020     */
7021    removeStyleSheet : function(id){
7022        var existing = doc.getElementById(id);
7023        if(existing){
7024            existing.parentNode.removeChild(existing);
7025        }
7026    },
7027
7028    /**
7029     * Dynamically swaps an existing stylesheet reference for a new one
7030     * @param {String} id The id of an existing link tag to remove
7031     * @param {String} url The href of the new stylesheet to include
7032     */
7033    swapStyleSheet : function(id, url){
7034        this.removeStyleSheet(id);
7035        var ss = doc.createElement("link");
7036        ss.setAttribute("rel", "stylesheet");
7037        ss.setAttribute("type", "text/css");
7038        ss.setAttribute("id", id);
7039        ss.setAttribute("href", url);
7040        doc.getElementsByTagName("head")[0].appendChild(ss);
7041    },
7042    
7043    /**
7044     * Refresh the rule cache if you have dynamically added stylesheets
7045     * @return {Object} An object (hash) of rules indexed by selector
7046     */
7047    refreshCache : function(){
7048        return this.getRules(true);
7049    },
7050
7051    // private
7052    cacheStyleSheet : function(ss){
7053        if(!rules){
7054            rules = {};
7055        }
7056        try{// try catch for cross domain access issue
7057            var ssRules = ss.cssRules || ss.rules;
7058            for(var j = ssRules.length-1; j >= 0; --j){
7059                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
7060            }
7061        }catch(e){}
7062    },
7063    
7064    /**
7065     * Gets all css rules for the document
7066     * @param {Boolean} refreshCache true to refresh the internal cache
7067     * @return {Object} An object (hash) of rules indexed by selector
7068     */
7069    getRules : function(refreshCache){
7070                 if(rules === null || refreshCache){
7071                         rules = {};
7072                         var ds = doc.styleSheets;
7073                         for(var i =0, len = ds.length; i < len; i++){
7074                             try{
7075                         this.cacheStyleSheet(ds[i]);
7076                     }catch(e){} 
7077                 }
7078                 }
7079                 return rules;
7080         },
7081         
7082         /**
7083     * Gets an an individual CSS rule by selector(s)
7084     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
7085     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
7086     * @return {CSSRule} The CSS rule or null if one is not found
7087     */
7088    getRule : function(selector, refreshCache){
7089                 var rs = this.getRules(refreshCache);
7090                 if(!Ext.isArray(selector)){
7091                     return rs[selector.toLowerCase()];
7092                 }
7093                 for(var i = 0; i < selector.length; i++){
7094                         if(rs[selector[i]]){
7095                                 return rs[selector[i].toLowerCase()];
7096                         }
7097                 }
7098                 return null;
7099         },
7100         
7101         
7102         /**
7103     * Updates a rule property
7104     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
7105     * @param {String} property The css property
7106     * @param {String} value The new value for the property
7107     * @return {Boolean} true If a rule was found and updated
7108     */
7109    updateRule : function(selector, property, value){
7110                 if(!Ext.isArray(selector)){
7111                         var rule = this.getRule(selector);
7112                         if(rule){
7113                                 rule.style[property.replace(camelRe, camelFn)] = value;
7114                                 return true;
7115                         }
7116                 }else{
7117                         for(var i = 0; i < selector.length; i++){
7118                                 if(this.updateRule(selector[i], property, value)){
7119                                         return true;
7120                                 }
7121                         }
7122                 }
7123                 return false;
7124         }
7125    };   
7126 }();/**
7127  @class Ext.util.ClickRepeater
7128  @extends Ext.util.Observable
7129
7130  A wrapper class which can be applied to any element. Fires a "click" event while the
7131  mouse is pressed. The interval between firings may be specified in the config but
7132  defaults to 20 milliseconds.
7133
7134  Optionally, a CSS class may be applied to the element during the time it is pressed.
7135
7136  @cfg {Mixed} el The element to act as a button.
7137  @cfg {Number} delay The initial delay before the repeating event begins firing.
7138  Similar to an autorepeat key delay.
7139  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
7140  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
7141  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
7142            "interval" and "delay" are ignored.
7143  @cfg {Boolean} preventDefault True to prevent the default click event
7144  @cfg {Boolean} stopDefault True to stop the default click event
7145
7146  @history
7147     2007-02-02 jvs Original code contributed by Nige "Animal" White
7148     2007-02-02 jvs Renamed to ClickRepeater
7149     2007-02-03 jvs Modifications for FF Mac and Safari
7150
7151  @constructor
7152  @param {Mixed} el The element to listen on
7153  @param {Object} config
7154  */
7155 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
7156     
7157     constructor : function(el, config){
7158         this.el = Ext.get(el);
7159         this.el.unselectable();
7160
7161         Ext.apply(this, config);
7162
7163         this.addEvents(
7164         /**
7165          * @event mousedown
7166          * Fires when the mouse button is depressed.
7167          * @param {Ext.util.ClickRepeater} this
7168          * @param {Ext.EventObject} e
7169          */
7170         "mousedown",
7171         /**
7172          * @event click
7173          * Fires on a specified interval during the time the element is pressed.
7174          * @param {Ext.util.ClickRepeater} this
7175          * @param {Ext.EventObject} e
7176          */
7177         "click",
7178         /**
7179          * @event mouseup
7180          * Fires when the mouse key is released.
7181          * @param {Ext.util.ClickRepeater} this
7182          * @param {Ext.EventObject} e
7183          */
7184         "mouseup"
7185         );
7186
7187         if(!this.disabled){
7188             this.disabled = true;
7189             this.enable();
7190         }
7191
7192         // allow inline handler
7193         if(this.handler){
7194             this.on("click", this.handler,  this.scope || this);
7195         }
7196
7197         Ext.util.ClickRepeater.superclass.constructor.call(this);        
7198     },
7199     
7200     interval : 20,
7201     delay: 250,
7202     preventDefault : true,
7203     stopDefault : false,
7204     timer : 0,
7205
7206     /**
7207      * Enables the repeater and allows events to fire.
7208      */
7209     enable: function(){
7210         if(this.disabled){
7211             this.el.on('mousedown', this.handleMouseDown, this);
7212             if (Ext.isIE){
7213                 this.el.on('dblclick', this.handleDblClick, this);
7214             }
7215             if(this.preventDefault || this.stopDefault){
7216                 this.el.on('click', this.eventOptions, this);
7217             }
7218         }
7219         this.disabled = false;
7220     },
7221
7222     /**
7223      * Disables the repeater and stops events from firing.
7224      */
7225     disable: function(/* private */ force){
7226         if(force || !this.disabled){
7227             clearTimeout(this.timer);
7228             if(this.pressClass){
7229                 this.el.removeClass(this.pressClass);
7230             }
7231             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
7232             this.el.removeAllListeners();
7233         }
7234         this.disabled = true;
7235     },
7236
7237     /**
7238      * Convenience function for setting disabled/enabled by boolean.
7239      * @param {Boolean} disabled
7240      */
7241     setDisabled: function(disabled){
7242         this[disabled ? 'disable' : 'enable']();
7243     },
7244
7245     eventOptions: function(e){
7246         if(this.preventDefault){
7247             e.preventDefault();
7248         }
7249         if(this.stopDefault){
7250             e.stopEvent();
7251         }
7252     },
7253
7254     // private
7255     destroy : function() {
7256         this.disable(true);
7257         Ext.destroy(this.el);
7258         this.purgeListeners();
7259     },
7260
7261     handleDblClick : function(e){
7262         clearTimeout(this.timer);
7263         this.el.blur();
7264
7265         this.fireEvent("mousedown", this, e);
7266         this.fireEvent("click", this, e);
7267     },
7268
7269     // private
7270     handleMouseDown : function(e){
7271         clearTimeout(this.timer);
7272         this.el.blur();
7273         if(this.pressClass){
7274             this.el.addClass(this.pressClass);
7275         }
7276         this.mousedownTime = new Date();
7277
7278         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
7279         this.el.on("mouseout", this.handleMouseOut, this);
7280
7281         this.fireEvent("mousedown", this, e);
7282         this.fireEvent("click", this, e);
7283
7284         // Do not honor delay or interval if acceleration wanted.
7285         if (this.accelerate) {
7286             this.delay = 400;
7287         }
7288         this.timer = this.click.defer(this.delay || this.interval, this, [e]);
7289     },
7290
7291     // private
7292     click : function(e){
7293         this.fireEvent("click", this, e);
7294         this.timer = this.click.defer(this.accelerate ?
7295             this.easeOutExpo(this.mousedownTime.getElapsed(),
7296                 400,
7297                 -390,
7298                 12000) :
7299             this.interval, this, [e]);
7300     },
7301
7302     easeOutExpo : function (t, b, c, d) {
7303         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
7304     },
7305
7306     // private
7307     handleMouseOut : function(){
7308         clearTimeout(this.timer);
7309         if(this.pressClass){
7310             this.el.removeClass(this.pressClass);
7311         }
7312         this.el.on("mouseover", this.handleMouseReturn, this);
7313     },
7314
7315     // private
7316     handleMouseReturn : function(){
7317         this.el.un("mouseover", this.handleMouseReturn, this);
7318         if(this.pressClass){
7319             this.el.addClass(this.pressClass);
7320         }
7321         this.click();
7322     },
7323
7324     // private
7325     handleMouseUp : function(e){
7326         clearTimeout(this.timer);
7327         this.el.un("mouseover", this.handleMouseReturn, this);
7328         this.el.un("mouseout", this.handleMouseOut, this);
7329         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
7330         this.el.removeClass(this.pressClass);
7331         this.fireEvent("mouseup", this, e);
7332     }
7333 });/**
7334  * @class Ext.KeyNav
7335  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
7336  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
7337  * way to implement custom navigation schemes for any UI component.</p>
7338  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
7339  * pageUp, pageDown, del, home, end.  Usage:</p>
7340  <pre><code>
7341 var nav = new Ext.KeyNav("my-element", {
7342     "left" : function(e){
7343         this.moveLeft(e.ctrlKey);
7344     },
7345     "right" : function(e){
7346         this.moveRight(e.ctrlKey);
7347     },
7348     "enter" : function(e){
7349         this.save();
7350     },
7351     scope : this
7352 });
7353 </code></pre>
7354  * @constructor
7355  * @param {Mixed} el The element to bind to
7356  * @param {Object} config The config
7357  */
7358 Ext.KeyNav = function(el, config){
7359     this.el = Ext.get(el);
7360     Ext.apply(this, config);
7361     if(!this.disabled){
7362         this.disabled = true;
7363         this.enable();
7364     }
7365 };
7366
7367 Ext.KeyNav.prototype = {
7368     /**
7369      * @cfg {Boolean} disabled
7370      * True to disable this KeyNav instance (defaults to false)
7371      */
7372     disabled : false,
7373     /**
7374      * @cfg {String} defaultEventAction
7375      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
7376      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
7377      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
7378      */
7379     defaultEventAction: "stopEvent",
7380     /**
7381      * @cfg {Boolean} forceKeyDown
7382      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
7383      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
7384      * handle keydown instead of keypress.
7385      */
7386     forceKeyDown : false,
7387
7388     // private
7389     relay : function(e){
7390         var k = e.getKey(),
7391             h = this.keyToHandler[k];
7392         if(h && this[h]){
7393             if(this.doRelay(e, this[h], h) !== true){
7394                 e[this.defaultEventAction]();
7395             }
7396         }
7397     },
7398
7399     // private
7400     doRelay : function(e, h, hname){
7401         return h.call(this.scope || this, e, hname);
7402     },
7403
7404     // possible handlers
7405     enter : false,
7406     left : false,
7407     right : false,
7408     up : false,
7409     down : false,
7410     tab : false,
7411     esc : false,
7412     pageUp : false,
7413     pageDown : false,
7414     del : false,
7415     home : false,
7416     end : false,
7417
7418     // quick lookup hash
7419     keyToHandler : {
7420         37 : "left",
7421         39 : "right",
7422         38 : "up",
7423         40 : "down",
7424         33 : "pageUp",
7425         34 : "pageDown",
7426         46 : "del",
7427         36 : "home",
7428         35 : "end",
7429         13 : "enter",
7430         27 : "esc",
7431         9  : "tab"
7432     },
7433     
7434     stopKeyUp: function(e) {
7435         var k = e.getKey();
7436
7437         if (k >= 37 && k <= 40) {
7438             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
7439             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
7440             e.stopEvent();
7441         }
7442     },
7443     
7444     /**
7445      * Destroy this KeyNav (this is the same as calling disable).
7446      */
7447     destroy: function(){
7448         this.disable();    
7449     },
7450
7451         /**
7452          * Enable this KeyNav
7453          */
7454         enable: function() {
7455         if (this.disabled) {
7456             if (Ext.isSafari2) {
7457                 // call stopKeyUp() on "keyup" event
7458                 this.el.on('keyup', this.stopKeyUp, this);
7459             }
7460
7461             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7462             this.disabled = false;
7463         }
7464     },
7465
7466         /**
7467          * Disable this KeyNav
7468          */
7469         disable: function() {
7470         if (!this.disabled) {
7471             if (Ext.isSafari2) {
7472                 // remove "keyup" event handler
7473                 this.el.un('keyup', this.stopKeyUp, this);
7474             }
7475
7476             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7477             this.disabled = true;
7478         }
7479     },
7480     
7481     /**
7482      * Convenience function for setting disabled/enabled by boolean.
7483      * @param {Boolean} disabled
7484      */
7485     setDisabled : function(disabled){
7486         this[disabled ? "disable" : "enable"]();
7487     },
7488     
7489     // private
7490     isKeydown: function(){
7491         return this.forceKeyDown || Ext.EventManager.useKeydown;
7492     }
7493 };
7494 /**
7495  * @class Ext.KeyMap
7496  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
7497  * The constructor accepts the same config object as defined by {@link #addBinding}.
7498  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
7499  * combination it will call the function with this signature (if the match is a multi-key
7500  * combination the callback will still be called only once): (String key, Ext.EventObject e)
7501  * A KeyMap can also handle a string representation of keys.<br />
7502  * Usage:
7503  <pre><code>
7504 // map one key by key code
7505 var map = new Ext.KeyMap("my-element", {
7506     key: 13, // or Ext.EventObject.ENTER
7507     fn: myHandler,
7508     scope: myObject
7509 });
7510
7511 // map multiple keys to one action by string
7512 var map = new Ext.KeyMap("my-element", {
7513     key: "a\r\n\t",
7514     fn: myHandler,
7515     scope: myObject
7516 });
7517
7518 // map multiple keys to multiple actions by strings and array of codes
7519 var map = new Ext.KeyMap("my-element", [
7520     {
7521         key: [10,13],
7522         fn: function(){ alert("Return was pressed"); }
7523     }, {
7524         key: "abc",
7525         fn: function(){ alert('a, b or c was pressed'); }
7526     }, {
7527         key: "\t",
7528         ctrl:true,
7529         shift:true,
7530         fn: function(){ alert('Control + shift + tab was pressed.'); }
7531     }
7532 ]);
7533 </code></pre>
7534  * <b>Note: A KeyMap starts enabled</b>
7535  * @constructor
7536  * @param {Mixed} el The element to bind to
7537  * @param {Object} config The config (see {@link #addBinding})
7538  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
7539  */
7540 Ext.KeyMap = function(el, config, eventName){
7541     this.el  = Ext.get(el);
7542     this.eventName = eventName || "keydown";
7543     this.bindings = [];
7544     if(config){
7545         this.addBinding(config);
7546     }
7547     this.enable();
7548 };
7549
7550 Ext.KeyMap.prototype = {
7551     /**
7552      * True to stop the event from bubbling and prevent the default browser action if the
7553      * key was handled by the KeyMap (defaults to false)
7554      * @type Boolean
7555      */
7556     stopEvent : false,
7557
7558     /**
7559      * Add a new binding to this KeyMap. The following config object properties are supported:
7560      * <pre>
7561 Property    Type             Description
7562 ----------  ---------------  ----------------------------------------------------------------------
7563 key         String/Array     A single keycode or an array of keycodes to handle
7564 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)
7565 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)
7566 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)
7567 handler     Function         The function to call when KeyMap finds the expected key combination
7568 fn          Function         Alias of handler (for backwards-compatibility)
7569 scope       Object           The scope of the callback function
7570 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)
7571 </pre>
7572      *
7573      * Usage:
7574      * <pre><code>
7575 // Create a KeyMap
7576 var map = new Ext.KeyMap(document, {
7577     key: Ext.EventObject.ENTER,
7578     fn: handleKey,
7579     scope: this
7580 });
7581
7582 //Add a new binding to the existing KeyMap later
7583 map.addBinding({
7584     key: 'abc',
7585     shift: true,
7586     fn: handleKey,
7587     scope: this
7588 });
7589 </code></pre>
7590      * @param {Object/Array} config A single KeyMap config or an array of configs
7591      */
7592         addBinding : function(config){
7593         if(Ext.isArray(config)){
7594             Ext.each(config, function(c){
7595                 this.addBinding(c);
7596             }, this);
7597             return;
7598         }
7599         var keyCode = config.key,
7600             fn = config.fn || config.handler,
7601             scope = config.scope;
7602
7603         if (config.stopEvent) {
7604             this.stopEvent = config.stopEvent;    
7605         }       
7606
7607         if(typeof keyCode == "string"){
7608             var ks = [];
7609             var keyString = keyCode.toUpperCase();
7610             for(var j = 0, len = keyString.length; j < len; j++){
7611                 ks.push(keyString.charCodeAt(j));
7612             }
7613             keyCode = ks;
7614         }
7615         var keyArray = Ext.isArray(keyCode);
7616         
7617         var handler = function(e){
7618             if(this.checkModifiers(config, e)){
7619                 var k = e.getKey();
7620                 if(keyArray){
7621                     for(var i = 0, len = keyCode.length; i < len; i++){
7622                         if(keyCode[i] == k){
7623                           if(this.stopEvent){
7624                               e.stopEvent();
7625                           }
7626                           fn.call(scope || window, k, e);
7627                           return;
7628                         }
7629                     }
7630                 }else{
7631                     if(k == keyCode){
7632                         if(this.stopEvent){
7633                            e.stopEvent();
7634                         }
7635                         fn.call(scope || window, k, e);
7636                     }
7637                 }
7638             }
7639         };
7640         this.bindings.push(handler);
7641         },
7642     
7643     // private
7644     checkModifiers: function(config, e){
7645         var val, key, keys = ['shift', 'ctrl', 'alt'];
7646         for (var i = 0, len = keys.length; i < len; ++i){
7647             key = keys[i];
7648             val = config[key];
7649             if(!(val === undefined || (val === e[key + 'Key']))){
7650                 return false;
7651             }
7652         }
7653         return true;
7654     },
7655
7656     /**
7657      * Shorthand for adding a single key listener
7658      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
7659      * following options:
7660      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
7661      * @param {Function} fn The function to call
7662      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
7663      */
7664     on : function(key, fn, scope){
7665         var keyCode, shift, ctrl, alt;
7666         if(typeof key == "object" && !Ext.isArray(key)){
7667             keyCode = key.key;
7668             shift = key.shift;
7669             ctrl = key.ctrl;
7670             alt = key.alt;
7671         }else{
7672             keyCode = key;
7673         }
7674         this.addBinding({
7675             key: keyCode,
7676             shift: shift,
7677             ctrl: ctrl,
7678             alt: alt,
7679             fn: fn,
7680             scope: scope
7681         });
7682     },
7683
7684     // private
7685     handleKeyDown : function(e){
7686             if(this.enabled){ //just in case
7687             var b = this.bindings;
7688             for(var i = 0, len = b.length; i < len; i++){
7689                 b[i].call(this, e);
7690             }
7691             }
7692         },
7693
7694         /**
7695          * Returns true if this KeyMap is enabled
7696          * @return {Boolean}
7697          */
7698         isEnabled : function(){
7699             return this.enabled;
7700         },
7701
7702         /**
7703          * Enables this KeyMap
7704          */
7705         enable: function(){
7706                 if(!this.enabled){
7707                     this.el.on(this.eventName, this.handleKeyDown, this);
7708                     this.enabled = true;
7709                 }
7710         },
7711
7712         /**
7713          * Disable this KeyMap
7714          */
7715         disable: function(){
7716                 if(this.enabled){
7717                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
7718                     this.enabled = false;
7719                 }
7720         },
7721     
7722     /**
7723      * Convenience function for setting disabled/enabled by boolean.
7724      * @param {Boolean} disabled
7725      */
7726     setDisabled : function(disabled){
7727         this[disabled ? "disable" : "enable"]();
7728     }
7729 };/**
7730  * @class Ext.util.TextMetrics
7731  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
7732  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
7733  * should not contain any HTML, otherwise it may not be measured correctly.
7734  * @singleton
7735  */
7736 Ext.util.TextMetrics = function(){
7737     var shared;
7738     return {
7739         /**
7740          * Measures the size of the specified text
7741          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
7742          * that can affect the size of the rendered text
7743          * @param {String} text The text to measure
7744          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7745          * in order to accurately measure the text height
7746          * @return {Object} An object containing the text's size {width: (width), height: (height)}
7747          */
7748         measure : function(el, text, fixedWidth){
7749             if(!shared){
7750                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
7751             }
7752             shared.bind(el);
7753             shared.setFixedWidth(fixedWidth || 'auto');
7754             return shared.getSize(text);
7755         },
7756
7757         /**
7758          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
7759          * the overhead of multiple calls to initialize the style properties on each measurement.
7760          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
7761          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7762          * in order to accurately measure the text height
7763          * @return {Ext.util.TextMetrics.Instance} instance The new instance
7764          */
7765         createInstance : function(el, fixedWidth){
7766             return Ext.util.TextMetrics.Instance(el, fixedWidth);
7767         }
7768     };
7769 }();
7770
7771 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
7772     var ml = new Ext.Element(document.createElement('div'));
7773     document.body.appendChild(ml.dom);
7774     ml.position('absolute');
7775     ml.setLeftTop(-1000, -1000);
7776     ml.hide();
7777
7778     if(fixedWidth){
7779         ml.setWidth(fixedWidth);
7780     }
7781
7782     var instance = {
7783         /**
7784          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7785          * Returns the size of the specified text based on the internal element's style and width properties
7786          * @param {String} text The text to measure
7787          * @return {Object} An object containing the text's size {width: (width), height: (height)}
7788          */
7789         getSize : function(text){
7790             ml.update(text);
7791             var s = ml.getSize();
7792             ml.update('');
7793             return s;
7794         },
7795
7796         /**
7797          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7798          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
7799          * that can affect the size of the rendered text
7800          * @param {String/HTMLElement} el The element, dom node or id
7801          */
7802         bind : function(el){
7803             ml.setStyle(
7804                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
7805             );
7806         },
7807
7808         /**
7809          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7810          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
7811          * to set a fixed width in order to accurately measure the text height.
7812          * @param {Number} width The width to set on the element
7813          */
7814         setFixedWidth : function(width){
7815             ml.setWidth(width);
7816         },
7817
7818         /**
7819          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7820          * Returns the measured width of the specified text
7821          * @param {String} text The text to measure
7822          * @return {Number} width The width in pixels
7823          */
7824         getWidth : function(text){
7825             ml.dom.style.width = 'auto';
7826             return this.getSize(text).width;
7827         },
7828
7829         /**
7830          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7831          * Returns the measured height of the specified text.  For multiline text, be sure to call
7832          * {@link #setFixedWidth} if necessary.
7833          * @param {String} text The text to measure
7834          * @return {Number} height The height in pixels
7835          */
7836         getHeight : function(text){
7837             return this.getSize(text).height;
7838         }
7839     };
7840
7841     instance.bind(bindTo);
7842
7843     return instance;
7844 };
7845
7846 Ext.Element.addMethods({
7847     /**
7848      * Returns the width in pixels of the passed text, or the width of the text in this Element.
7849      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
7850      * @param {Number} min (Optional) The minumum value to return.
7851      * @param {Number} max (Optional) The maximum value to return.
7852      * @return {Number} The text width in pixels.
7853      * @member Ext.Element getTextWidth
7854      */
7855     getTextWidth : function(text, min, max){
7856         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
7857     }
7858 });
7859 /**
7860  * @class Ext.util.Cookies
7861  * Utility class for managing and interacting with cookies.
7862  * @singleton
7863  */
7864 Ext.util.Cookies = {
7865     /**
7866      * Create a cookie with the specified name and value. Additional settings
7867      * for the cookie may be optionally specified (for example: expiration,
7868      * access restriction, SSL).
7869      * @param {String} name The name of the cookie to set. 
7870      * @param {Mixed} value The value to set for the cookie.
7871      * @param {Object} expires (Optional) Specify an expiration date the
7872      * cookie is to persist until.  Note that the specified Date object will
7873      * be converted to Greenwich Mean Time (GMT). 
7874      * @param {String} path (Optional) Setting a path on the cookie restricts
7875      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
7876      * @param {String} domain (Optional) Setting a domain restricts access to
7877      * pages on a given domain (typically used to allow cookie access across
7878      * subdomains). For example, "extjs.com" will create a cookie that can be
7879      * accessed from any subdomain of extjs.com, including www.extjs.com,
7880      * support.extjs.com, etc.
7881      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
7882      * should only be accessible via SSL on a page using the HTTPS protocol.
7883      * Defaults to <tt>false</tt>. Note that this will only work if the page
7884      * calling this code uses the HTTPS protocol, otherwise the cookie will be
7885      * created with default options.
7886      */
7887     set : function(name, value){
7888         var argv = arguments;
7889         var argc = arguments.length;
7890         var expires = (argc > 2) ? argv[2] : null;
7891         var path = (argc > 3) ? argv[3] : '/';
7892         var domain = (argc > 4) ? argv[4] : null;
7893         var secure = (argc > 5) ? argv[5] : false;
7894         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
7895     },
7896
7897     /**
7898      * Retrieves cookies that are accessible by the current page. If a cookie
7899      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
7900      * example retrieves the cookie called "valid" and stores the String value
7901      * in the variable <tt>validStatus</tt>.
7902      * <pre><code>
7903      * var validStatus = Ext.util.Cookies.get("valid");
7904      * </code></pre>
7905      * @param {String} name The name of the cookie to get
7906      * @return {Mixed} Returns the cookie value for the specified name;
7907      * null if the cookie name does not exist.
7908      */
7909     get : function(name){
7910         var arg = name + "=";
7911         var alen = arg.length;
7912         var clen = document.cookie.length;
7913         var i = 0;
7914         var j = 0;
7915         while(i < clen){
7916             j = i + alen;
7917             if(document.cookie.substring(i, j) == arg){
7918                 return Ext.util.Cookies.getCookieVal(j);
7919             }
7920             i = document.cookie.indexOf(" ", i) + 1;
7921             if(i === 0){
7922                 break;
7923             }
7924         }
7925         return null;
7926     },
7927
7928     /**
7929      * Removes a cookie with the provided name from the browser
7930      * if found by setting its expiration date to sometime in the past. 
7931      * @param {String} name The name of the cookie to remove
7932      */
7933     clear : function(name){
7934         if(Ext.util.Cookies.get(name)){
7935             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
7936         }
7937     },
7938     /**
7939      * @private
7940      */
7941     getCookieVal : function(offset){
7942         var endstr = document.cookie.indexOf(";", offset);
7943         if(endstr == -1){
7944             endstr = document.cookie.length;
7945         }
7946         return unescape(document.cookie.substring(offset, endstr));
7947     }
7948 };/**
7949  * Framework-wide error-handler.  Developers can override this method to provide
7950  * custom exception-handling.  Framework errors will often extend from the base
7951  * Ext.Error class.
7952  * @param {Object/Error} e The thrown exception object.
7953  */
7954 Ext.handleError = function(e) {
7955     throw e;
7956 };
7957
7958 /**
7959  * @class Ext.Error
7960  * @extends Error
7961  * <p>A base error class. Future implementations are intended to provide more
7962  * robust error handling throughout the framework (<b>in the debug build only</b>)
7963  * to check for common errors and problems. The messages issued by this class
7964  * will aid error checking. Error checks will be automatically removed in the
7965  * production build so that performance is not negatively impacted.</p>
7966  * <p>Some sample messages currently implemented:</p><pre>
7967 "DataProxy attempted to execute an API-action but found an undefined
7968 url / function. Please review your Proxy url/api-configuration."
7969  * </pre><pre>
7970 "Could not locate your "root" property in your server response.
7971 Please review your JsonReader config to ensure the config-property
7972 "root" matches the property your server-response.  See the JsonReader
7973 docs for additional assistance."
7974  * </pre>
7975  * <p>An example of the code used for generating error messages:</p><pre><code>
7976 try {
7977     generateError({
7978         foo: 'bar'
7979     });
7980 }
7981 catch (e) {
7982     console.error(e);
7983 }
7984 function generateError(data) {
7985     throw new Ext.Error('foo-error', data);
7986 }
7987  * </code></pre>
7988  * @param {String} message
7989  */
7990 Ext.Error = function(message) {
7991     // Try to read the message from Ext.Error.lang
7992     this.message = (this.lang[message]) ? this.lang[message] : message;
7993 };
7994
7995 Ext.Error.prototype = new Error();
7996 Ext.apply(Ext.Error.prototype, {
7997     // protected.  Extensions place their error-strings here.
7998     lang: {},
7999
8000     name: 'Ext.Error',
8001     /**
8002      * getName
8003      * @return {String}
8004      */
8005     getName : function() {
8006         return this.name;
8007     },
8008     /**
8009      * getMessage
8010      * @return {String}
8011      */
8012     getMessage : function() {
8013         return this.message;
8014     },
8015     /**
8016      * toJson
8017      * @return {String}
8018      */
8019     toJson : function() {
8020         return Ext.encode(this);
8021     }
8022 });