Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / src / core / core / Ext.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7
8 // for old browsers
9 window.undefined = window.undefined;
10
11 /**
12  * @class Ext
13  * Ext core utilities and functions.
14  * @singleton
15  */
16
17 Ext = {
18     /**
19      * The version of the framework
20      * @type String
21      */
22     version : '3.1.0'
23 };
24
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Ext apply
32  */
33 Ext.apply = function(o, c, defaults){
34     // no "this" reference for friendly out of scope calls
35     if(defaults){
36         Ext.apply(o, defaults);
37     }
38     if(o && c && typeof c == 'object'){
39         for(var p in c){
40             o[p] = c[p];
41         }
42     }
43     return o;
44 };
45
46 (function(){
47     var idSeed = 0,
48         toString = Object.prototype.toString,
49         ua = navigator.userAgent.toLowerCase(),
50         check = function(r){
51             return r.test(ua);
52         },
53         DOC = document,
54         isStrict = DOC.compatMode == "CSS1Compat",
55         isOpera = check(/opera/),
56         isChrome = check(/chrome/),
57         isWebKit = check(/webkit/),
58         isSafari = !isChrome && check(/safari/),
59         isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
60         isSafari3 = isSafari && check(/version\/3/),
61         isSafari4 = isSafari && check(/version\/4/),
62         isIE = !isOpera && check(/msie/),
63         isIE7 = isIE && check(/msie 7/),
64         isIE8 = isIE && check(/msie 8/),
65         isIE6 = isIE && !isIE7 && !isIE8,
66         isGecko = !isWebKit && check(/gecko/),
67         isGecko2 = isGecko && check(/rv:1\.8/),
68         isGecko3 = isGecko && check(/rv:1\.9/),
69         isBorderBox = isIE && !isStrict,
70         isWindows = check(/windows|win32/),
71         isMac = check(/macintosh|mac os x/),
72         isAir = check(/adobeair/),
73         isLinux = check(/linux/),
74         isSecure = /^https/i.test(window.location.protocol);
75
76     // remove css image flicker
77     if(isIE6){
78         try{
79             DOC.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82
83     Ext.apply(Ext, {
84         /**
85          * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
86          * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
87          * @type String
88          */
89         SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank',
90         /**
91          * True if the browser is in strict (standards-compliant) mode, as opposed to quirks mode
92          * @type Boolean
93          */
94         isStrict : isStrict,
95         /**
96          * True if the page is running over SSL
97          * @type Boolean
98          */
99         isSecure : isSecure,
100         /**
101          * True when the document is fully initialized and ready for action
102          * @type Boolean
103          */
104         isReady : false,
105
106         /**
107          * True if the {@link Ext.Fx} Class is available
108          * @type Boolean
109          * @property enableFx
110          */
111
112         /**
113          * True to automatically uncache orphaned Ext.Elements periodically (defaults to true)
114          * @type Boolean
115          */
116         enableGarbageCollector : true,
117
118         /**
119          * True to automatically purge event listeners during garbageCollection (defaults to false).
120          * @type Boolean
121          */
122         enableListenerCollection : false,
123
124         /**
125          * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
126          * Currently not optimized for performance.
127          * @type Boolean
128          */
129         enableNestedListenerRemoval : false,
130
131         /**
132          * Indicates whether to use native browser parsing for JSON methods.
133          * This option is ignored if the browser does not support native JSON methods.
134          * <b>Note: Native JSON methods will not work with objects that have functions.
135          * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
136          * @type Boolean
137          */
138         USE_NATIVE_JSON : false,
139
140         /**
141          * Copies all the properties of config to obj if they don't already exist.
142          * @param {Object} obj The receiver of the properties
143          * @param {Object} config The source of the properties
144          * @return {Object} returns obj
145          */
146         applyIf : function(o, c){
147             if(o){
148                 for(var p in c){
149                     if(!Ext.isDefined(o[p])){
150                         o[p] = c[p];
151                     }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Generates unique ids. If the element already has an id, it is unchanged
159          * @param {Mixed} el (optional) The element to generate an id for
160          * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
161          * @return {String} The generated Id.
162          */
163         id : function(el, prefix){
164             return (el = Ext.getDom(el) || {}).id = el.id || (prefix || "ext-gen") + (++idSeed);
165         },
166
167         /**
168          * <p>Extends one class to create a subclass and optionally overrides members with the passed literal. This method
169          * also adds the function "override()" to the subclass that can be used to override members of the class.</p>
170          * For example, to create a subclass of Ext GridPanel:
171          * <pre><code>
172 MyGridPanel = Ext.extend(Ext.grid.GridPanel, {
173     constructor: function(config) {
174
175 //      Create configuration for this Grid.
176         var store = new Ext.data.Store({...});
177         var colModel = new Ext.grid.ColumnModel({...});
178
179 //      Create a new config object containing our computed properties
180 //      *plus* whatever was in the config parameter.
181         config = Ext.apply({
182             store: store,
183             colModel: colModel
184         }, config);
185
186         MyGridPanel.superclass.constructor.call(this, config);
187
188 //      Your postprocessing here
189     },
190
191     yourMethod: function() {
192         // etc.
193     }
194 });
195 </code></pre>
196          *
197          * <p>This function also supports a 3-argument call in which the subclass's constructor is
198          * passed as an argument. In this form, the parameters are as follows:</p>
199          * <div class="mdetail-params"><ul>
200          * <li><code>subclass</code> : Function <div class="sub-desc">The subclass constructor.</div></li>
201          * <li><code>superclass</code> : Function <div class="sub-desc">The constructor of class being extended</div></li>
202          * <li><code>overrides</code> : Object <div class="sub-desc">A literal with members which are copied into the subclass's
203          * prototype, and are therefore shared among all instances of the new class.</div></li>
204          * </ul></div>
205          *
206          * @param {Function} superclass The constructor of class being extended.
207          * @param {Object} overrides <p>A literal with members which are copied into the subclass's
208          * prototype, and are therefore shared between all instances of the new class.</p>
209          * <p>This may contain a special member named <tt><b>constructor</b></tt>. This is used
210          * to define the constructor of the new class, and is returned. If this property is
211          * <i>not</i> specified, a constructor is generated and returned which just calls the
212          * superclass's constructor passing on its parameters.</p>
213          * <p><b>It is essential that you call the superclass constructor in any provided constructor. See example code.</b></p>
214          * @return {Function} The subclass constructor from the <code>overrides</code> parameter, or a generated one if not provided.
215          */
216         extend : function(){
217             // inline overrides
218             var io = function(o){
219                 for(var m in o){
220                     this[m] = o[m];
221                 }
222             };
223             var oc = Object.prototype.constructor;
224
225             return function(sb, sp, overrides){
226                 if(Ext.isObject(sp)){
227                     overrides = sp;
228                     sp = sb;
229                     sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};
230                 }
231                 var F = function(){},
232                     sbp,
233                     spp = sp.prototype;
234
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 if(spp.constructor == oc){
240                     spp.constructor=sp;
241                 }
242                 sb.override = function(o){
243                     Ext.override(sb, o);
244                 };
245                 sbp.superclass = sbp.supr = (function(){
246                     return spp;
247                 });
248                 sbp.override = io;
249                 Ext.override(sb, overrides);
250                 sb.extend = function(o){return Ext.extend(sb, o);};
251                 return sb;
252             };
253         }(),
254
255         /**
256          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
257          * Usage:<pre><code>
258 Ext.override(MyClass, {
259     newMethod1: function(){
260         // etc.
261     },
262     newMethod2: function(foo){
263         // etc.
264     }
265 });
266 </code></pre>
267          * @param {Object} origclass The class to override
268          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
269          * containing one or more methods.
270          * @method override
271          */
272         override : function(origclass, overrides){
273             if(overrides){
274                 var p = origclass.prototype;
275                 Ext.apply(p, overrides);
276                 if(Ext.isIE && overrides.hasOwnProperty('toString')){
277                     p.toString = overrides.toString;
278                 }
279             }
280         },
281
282         /**
283          * Creates namespaces to be used for scoping variables and classes so that they are not global.
284          * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
285          * <pre><code>
286 Ext.namespace('Company', 'Company.data');
287 Ext.namespace('Company.data'); // equivalent and preferable to above syntax
288 Company.Widget = function() { ... }
289 Company.data.CustomStore = function(config) { ... }
290 </code></pre>
291          * @param {String} namespace1
292          * @param {String} namespace2
293          * @param {String} etc
294          * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
295          * @method namespace
296          */
297         namespace : function(){
298             var o, d;
299             Ext.each(arguments, function(v) {
300                 d = v.split(".");
301                 o = window[d[0]] = window[d[0]] || {};
302                 Ext.each(d.slice(1), function(v2){
303                     o = o[v2] = o[v2] || {};
304                 });
305             });
306             return o;
307         },
308
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Ext.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @param {String} pre (optional) A prefix to add to the url encoded string
313          * @return {String}
314          */
315         urlEncode : function(o, pre){
316             var empty,
317                 buf = [],
318                 e = encodeURIComponent;
319
320             Ext.iterate(o, function(key, item){
321                 empty = Ext.isEmpty(item);
322                 Ext.each(empty ? key : item, function(val){
323                     buf.push('&', e(key), '=', (!Ext.isEmpty(val) && (val != key || !empty)) ? (Ext.isDate(val) ? Ext.encode(val).replace(/"/g, '') : e(val)) : '');
324                 });
325             });
326             if(!pre){
327                 buf.shift();
328                 pre = '';
329             }
330             return pre + buf.join('');
331         },
332
333         /**
334          * Takes an encoded URL and and converts it to an object. Example: <pre><code>
335 Ext.urlDecode("foo=1&bar=2"); // returns {foo: "1", bar: "2"}
336 Ext.urlDecode("foo=1&bar=2&bar=3&bar=4", false); // returns {foo: "1", bar: ["2", "3", "4"]}
337 </code></pre>
338          * @param {String} string
339          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
340          * @return {Object} A literal with members
341          */
342         urlDecode : function(string, overwrite){
343             if(Ext.isEmpty(string)){
344                 return {};
345             }
346             var obj = {},
347                 pairs = string.split('&'),
348                 d = decodeURIComponent,
349                 name,
350                 value;
351             Ext.each(pairs, function(pair) {
352                 pair = pair.split('=');
353                 name = d(pair[0]);
354                 value = d(pair[1]);
355                 obj[name] = overwrite || !obj[name] ? value :
356                             [].concat(obj[name]).concat(value);
357             });
358             return obj;
359         },
360
361         /**
362          * Appends content to the query string of a URL, handling logic for whether to place
363          * a question mark or ampersand.
364          * @param {String} url The URL to append to.
365          * @param {String} s The content to append to the URL.
366          * @return (String) The resulting URL
367          */
368         urlAppend : function(url, s){
369             if(!Ext.isEmpty(s)){
370                 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
371             }
372             return url;
373         },
374
375         /**
376          * Converts any iterable (numeric indices and a length property) into a true array
377          * Don't use this on strings. IE doesn't support "abc"[0] which this implementation depends on.
378          * For strings, use this instead: "abc".match(/./g) => [a,b,c];
379          * @param {Iterable} the iterable object to be turned into a true Array.
380          * @return (Array) array
381          */
382          toArray : function(){
383              return isIE ?
384                  function(a, i, j, res){
385                      res = [];
386                      for(var x = 0, len = a.length; x < len; x++) {
387                          res.push(a[x]);
388                      }
389                      return res.slice(i || 0, j || res.length);
390                  } :
391                  function(a, i, j){
392                      return Array.prototype.slice.call(a, i || 0, j || a.length);
393                  }
394          }(),
395
396         isIterable : function(v){
397             //check for array or arguments
398             if(Ext.isArray(v) || v.callee){
399                 return true;
400             }
401             //check for node list type
402             if(/NodeList|HTMLCollection/.test(toString.call(v))){
403                 return true;
404             }
405             //NodeList has an item and length property
406             //IXMLDOMNodeList has nextNode method, needs to be checked first.
407             return ((v.nextNode || v.item) && Ext.isNumber(v.length));
408         },
409
410         /**
411          * Iterates an array calling the supplied function.
412          * @param {Array/NodeList/Mixed} array The array to be iterated. If this
413          * argument is not really an array, the supplied function is called once.
414          * @param {Function} fn The function to be called with each item. If the
415          * supplied function returns false, iteration stops and this method returns
416          * the current <code>index</code>. This function is called with
417          * the following arguments:
418          * <div class="mdetail-params"><ul>
419          * <li><code>item</code> : <i>Mixed</i>
420          * <div class="sub-desc">The item at the current <code>index</code>
421          * in the passed <code>array</code></div></li>
422          * <li><code>index</code> : <i>Number</i>
423          * <div class="sub-desc">The current index within the array</div></li>
424          * <li><code>allItems</code> : <i>Array</i>
425          * <div class="sub-desc">The <code>array</code> passed as the first
426          * argument to <code>Ext.each</code>.</div></li>
427          * </ul></div>
428          * @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed.
429          * Defaults to the <code>item</code> at the current <code>index</code>
430          * within the passed <code>array</code>.
431          * @return See description for the fn parameter.
432          */
433         each : function(array, fn, scope){
434             if(Ext.isEmpty(array, true)){
435                 return;
436             }
437             if(!Ext.isIterable(array) || Ext.isPrimitive(array)){
438                 array = [array];
439             }
440             for(var i = 0, len = array.length; i < len; i++){
441                 if(fn.call(scope || array[i], array[i], i, array) === false){
442                     return i;
443                 };
444             }
445         },
446
447         /**
448          * Iterates either the elements in an array, or each of the properties in an object.
449          * <b>Note</b>: If you are only iterating arrays, it is better to call {@link #each}.
450          * @param {Object/Array} object The object or array to be iterated
451          * @param {Function} fn The function to be called for each iteration.
452          * The iteration will stop if the supplied function returns false, or
453          * all array elements / object properties have been covered. The signature
454          * varies depending on the type of object being interated:
455          * <div class="mdetail-params"><ul>
456          * <li>Arrays : <tt>(Object item, Number index, Array allItems)</tt>
457          * <div class="sub-desc">
458          * When iterating an array, the supplied function is called with each item.</div></li>
459          * <li>Objects : <tt>(String key, Object value, Object)</tt>
460          * <div class="sub-desc">
461          * When iterating an object, the supplied function is called with each key-value pair in
462          * the object, and the iterated object</div></li>
463          * </ul></div>
464          * @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed. Defaults to
465          * the <code>object</code> being iterated.
466          */
467         iterate : function(obj, fn, scope){
468             if(Ext.isEmpty(obj)){
469                 return;
470             }
471             if(Ext.isIterable(obj)){
472                 Ext.each(obj, fn, scope);
473                 return;
474             }else if(Ext.isObject(obj)){
475                 for(var prop in obj){
476                     if(obj.hasOwnProperty(prop)){
477                         if(fn.call(scope || obj, prop, obj[prop], obj) === false){
478                             return;
479                         };
480                     }
481                 }
482             }
483         },
484
485         /**
486          * Return the dom node for the passed String (id), dom node, or Ext.Element.
487          * Here are some examples:
488          * <pre><code>
489 // gets dom node based on id
490 var elDom = Ext.getDom('elId');
491 // gets dom node based on the dom node
492 var elDom1 = Ext.getDom(elDom);
493
494 // If we don&#39;t know if we are working with an
495 // Ext.Element or a dom node use Ext.getDom
496 function(el){
497     var dom = Ext.getDom(el);
498     // do something with the dom node
499 }
500          * </code></pre>
501          * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
502          * when this method is called to be successful.
503          * @param {Mixed} el
504          * @return HTMLElement
505          */
506         getDom : function(el){
507             if(!el || !DOC){
508                 return null;
509             }
510             return el.dom ? el.dom : (Ext.isString(el) ? DOC.getElementById(el) : el);
511         },
512
513         /**
514          * Returns the current document body as an {@link Ext.Element}.
515          * @return Ext.Element The document body
516          */
517         getBody : function(){
518             return Ext.get(DOC.body || DOC.documentElement);
519         },
520
521         /**
522          * Removes a DOM node from the document.
523          */
524         /**
525          * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
526          * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval} is
527          * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
528          * will be ignored if passed in.</p>
529          * @param {HTMLElement} node The node to remove
530          */
531         removeNode : isIE && !isIE8 ? function(){
532             var d;
533             return function(n){
534                 if(n && n.tagName != 'BODY'){
535                     (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
536                     d = d || DOC.createElement('div');
537                     d.appendChild(n);
538                     d.innerHTML = '';
539                     delete Ext.elCache[n.id];
540                 }
541             }
542         }() : function(n){
543             if(n && n.parentNode && n.tagName != 'BODY'){
544                 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
545                 n.parentNode.removeChild(n);
546                 delete Ext.elCache[n.id];
547             }
548         },
549
550         /**
551          * <p>Returns true if the passed value is empty.</p>
552          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
553          * <li>null</li>
554          * <li>undefined</li>
555          * <li>an empty array</li>
556          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
557          * </ul></div>
558          * @param {Mixed} value The value to test
559          * @param {Boolean} allowBlank (optional) true to allow empty strings (defaults to false)
560          * @return {Boolean}
561          */
562         isEmpty : function(v, allowBlank){
563             return v === null || v === undefined || ((Ext.isArray(v) && !v.length)) || (!allowBlank ? v === '' : false);
564         },
565
566         /**
567          * Returns true if the passed value is a JavaScript array, otherwise false.
568          * @param {Mixed} value The value to test
569          * @return {Boolean}
570          */
571         isArray : function(v){
572             return toString.apply(v) === '[object Array]';
573         },
574
575         /**
576          * Returns true if the passed object is a JavaScript date object, otherwise false.
577          * @param {Object} object The object to test
578          * @return {Boolean}
579          */
580         isDate : function(v){
581             return toString.apply(v) === '[object Date]';
582         },
583
584         /**
585          * Returns true if the passed value is a JavaScript Object, otherwise false.
586          * @param {Mixed} value The value to test
587          * @return {Boolean}
588          */
589         isObject : function(v){
590             return !!v && Object.prototype.toString.call(v) === '[object Object]';
591         },
592
593         /**
594          * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
595          * @param {Mixed} value The value to test
596          * @return {Boolean}
597          */
598         isPrimitive : function(v){
599             return Ext.isString(v) || Ext.isNumber(v) || Ext.isBoolean(v);
600         },
601
602         /**
603          * Returns true if the passed value is a JavaScript Function, otherwise false.
604          * @param {Mixed} value The value to test
605          * @return {Boolean}
606          */
607         isFunction : function(v){
608             return toString.apply(v) === '[object Function]';
609         },
610
611         /**
612          * Returns true if the passed value is a number. Returns false for non-finite numbers.
613          * @param {Mixed} value The value to test
614          * @return {Boolean}
615          */
616         isNumber : function(v){
617             return typeof v === 'number' && isFinite(v);
618         },
619
620         /**
621          * Returns true if the passed value is a string.
622          * @param {Mixed} value The value to test
623          * @return {Boolean}
624          */
625         isString : function(v){
626             return typeof v === 'string';
627         },
628
629         /**
630          * Returns true if the passed value is a boolean.
631          * @param {Mixed} value The value to test
632          * @return {Boolean}
633          */
634         isBoolean : function(v){
635             return typeof v === 'boolean';
636         },
637
638         /**
639          * Returns true if the passed value is an HTMLElement
640          * @param {Mixed} value The value to test
641          * @return {Boolean}
642          */
643         isElement : function(v) {
644             return !!v && v.tagName;
645         },
646
647         /**
648          * Returns true if the passed value is not undefined.
649          * @param {Mixed} value The value to test
650          * @return {Boolean}
651          */
652         isDefined : function(v){
653             return typeof v !== 'undefined';
654         },
655
656         /**
657          * True if the detected browser is Opera.
658          * @type Boolean
659          */
660         isOpera : isOpera,
661         /**
662          * True if the detected browser uses WebKit.
663          * @type Boolean
664          */
665         isWebKit : isWebKit,
666         /**
667          * True if the detected browser is Chrome.
668          * @type Boolean
669          */
670         isChrome : isChrome,
671         /**
672          * True if the detected browser is Safari.
673          * @type Boolean
674          */
675         isSafari : isSafari,
676         /**
677          * True if the detected browser is Safari 3.x.
678          * @type Boolean
679          */
680         isSafari3 : isSafari3,
681         /**
682          * True if the detected browser is Safari 4.x.
683          * @type Boolean
684          */
685         isSafari4 : isSafari4,
686         /**
687          * True if the detected browser is Safari 2.x.
688          * @type Boolean
689          */
690         isSafari2 : isSafari2,
691         /**
692          * True if the detected browser is Internet Explorer.
693          * @type Boolean
694          */
695         isIE : isIE,
696         /**
697          * True if the detected browser is Internet Explorer 6.x.
698          * @type Boolean
699          */
700         isIE6 : isIE6,
701         /**
702          * True if the detected browser is Internet Explorer 7.x.
703          * @type Boolean
704          */
705         isIE7 : isIE7,
706         /**
707          * True if the detected browser is Internet Explorer 8.x.
708          * @type Boolean
709          */
710         isIE8 : isIE8,
711         /**
712          * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
713          * @type Boolean
714          */
715         isGecko : isGecko,
716         /**
717          * True if the detected browser uses a pre-Gecko 1.9 layout engine (e.g. Firefox 2.x).
718          * @type Boolean
719          */
720         isGecko2 : isGecko2,
721         /**
722          * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
723          * @type Boolean
724          */
725         isGecko3 : isGecko3,
726         /**
727          * True if the detected browser is Internet Explorer running in non-strict mode.
728          * @type Boolean
729          */
730         isBorderBox : isBorderBox,
731         /**
732          * True if the detected platform is Linux.
733          * @type Boolean
734          */
735         isLinux : isLinux,
736         /**
737          * True if the detected platform is Windows.
738          * @type Boolean
739          */
740         isWindows : isWindows,
741         /**
742          * True if the detected platform is Mac OS.
743          * @type Boolean
744          */
745         isMac : isMac,
746         /**
747          * True if the detected platform is Adobe Air.
748          * @type Boolean
749          */
750         isAir : isAir
751     });
752
753     /**
754      * Creates namespaces to be used for scoping variables and classes so that they are not global.
755      * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
756      * <pre><code>
757 Ext.namespace('Company', 'Company.data');
758 Ext.namespace('Company.data'); // equivalent and preferable to above syntax
759 Company.Widget = function() { ... }
760 Company.data.CustomStore = function(config) { ... }
761 </code></pre>
762      * @param {String} namespace1
763      * @param {String} namespace2
764      * @param {String} etc
765      * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
766      * @method ns
767      */
768     Ext.ns = Ext.namespace;
769 })();
770
771 Ext.ns("Ext.util", "Ext.lib", "Ext.data");
772
773 Ext.elCache = {};
774
775 /**
776  * @class Function
777  * These functions are available on every Function object (any JavaScript function).
778  */
779 Ext.apply(Function.prototype, {
780      /**
781      * Creates an interceptor function. The passed function is called before the original one. If it returns false,
782      * the original one is not called. The resulting function returns the results of the original function.
783      * The passed function is called with the parameters of the original function. Example usage:
784      * <pre><code>
785 var sayHi = function(name){
786     alert('Hi, ' + name);
787 }
788
789 sayHi('Fred'); // alerts "Hi, Fred"
790
791 // create a new function that validates input without
792 // directly modifying the original function:
793 var sayHiToFriend = sayHi.createInterceptor(function(name){
794     return name == 'Brian';
795 });
796
797 sayHiToFriend('Fred');  // no alert
798 sayHiToFriend('Brian'); // alerts "Hi, Brian"
799 </code></pre>
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
802      * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
803      * @return {Function} The new function
804      */
805     createInterceptor : function(fcn, scope){
806         var method = this;
807         return !Ext.isFunction(fcn) ?
808                 this :
809                 function() {
810                     var me = this,
811                         args = arguments;
812                     fcn.target = me;
813                     fcn.method = method;
814                     return (fcn.apply(scope || me || window, args) !== false) ?
815                             method.apply(me || window, args) :
816                             null;
817                 };
818     },
819
820      /**
821      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
822      * Call directly on any function. Example: <code>myFunction.createCallback(arg1, arg2)</code>
823      * Will create a function that is bound to those 2 args. <b>If a specific scope is required in the
824      * callback, use {@link #createDelegate} instead.</b> The function returned by createCallback always
825      * executes in the window scope.
826      * <p>This method is required when you want to pass arguments to a callback function.  If no arguments
827      * are needed, you can simply pass a reference to the function as a callback (e.g., callback: myFn).
828      * However, if you tried to pass a function with arguments (e.g., callback: myFn(arg1, arg2)) the function
829      * would simply execute immediately when the code is parsed. Example usage:
830      * <pre><code>
831 var sayHi = function(name){
832     alert('Hi, ' + name);
833 }
834
835 // clicking the button alerts "Hi, Fred"
836 new Ext.Button({
837     text: 'Say Hi',
838     renderTo: Ext.getBody(),
839     handler: sayHi.createCallback('Fred')
840 });
841 </code></pre>
842      * @return {Function} The new function
843     */
844     createCallback : function(/*args...*/){
845         // make args available, in function below
846         var args = arguments,
847             method = this;
848         return function() {
849             return method.apply(window, args);
850         };
851     },
852
853     /**
854      * Creates a delegate (callback) that sets the scope to obj.
855      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this, [arg1, arg2])</code>
856      * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
857      * callback points to obj. Example usage:
858      * <pre><code>
859 var sayHi = function(name){
860     // Note this use of "this.text" here.  This function expects to
861     // execute within a scope that contains a text property.  In this
862     // example, the "this" variable is pointing to the btn object that
863     // was passed in createDelegate below.
864     alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
865 }
866
867 var btn = new Ext.Button({
868     text: 'Say Hi',
869     renderTo: Ext.getBody()
870 });
871
872 // This callback will execute in the scope of the
873 // button instance. Clicking the button alerts
874 // "Hi, Fred. You clicked the "Say Hi" button."
875 btn.on('click', sayHi.createDelegate(btn, ['Fred']));
876 </code></pre>
877      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
878      * <b>If omitted, defaults to the browser window.</b>
879      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
880      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
881      * if a number the args are inserted at the specified position
882      * @return {Function} The new function
883      */
884     createDelegate : function(obj, args, appendArgs){
885         var method = this;
886         return function() {
887             var callArgs = args || arguments;
888             if (appendArgs === true){
889                 callArgs = Array.prototype.slice.call(arguments, 0);
890                 callArgs = callArgs.concat(args);
891             }else if (Ext.isNumber(appendArgs)){
892                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
893                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
894                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
895             }
896             return method.apply(obj || window, callArgs);
897         };
898     },
899
900     /**
901      * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
902      * <pre><code>
903 var sayHi = function(name){
904     alert('Hi, ' + name);
905 }
906
907 // executes immediately:
908 sayHi('Fred');
909
910 // executes after 2 seconds:
911 sayHi.defer(2000, this, ['Fred']);
912
913 // this syntax is sometimes useful for deferring
914 // execution of an anonymous function:
915 (function(){
916     alert('Anonymous');
917 }).defer(100);
918 </code></pre>
919      * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
920      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
921      * <b>If omitted, defaults to the browser window.</b>
922      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
923      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
924      * if a number the args are inserted at the specified position
925      * @return {Number} The timeout id that can be used with clearTimeout
926      */
927     defer : function(millis, obj, args, appendArgs){
928         var fn = this.createDelegate(obj, args, appendArgs);
929         if(millis > 0){
930             return setTimeout(fn, millis);
931         }
932         fn();
933         return 0;
934     }
935 });
936
937 /**
938  * @class String
939  * These functions are available on every String object.
940  */
941 Ext.applyIf(String, {
942     /**
943      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
944      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
945      * <pre><code>
946 var cls = 'my-class', text = 'Some text';
947 var s = String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
948 // s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
949      * </code></pre>
950      * @param {String} string The tokenized string to be formatted
951      * @param {String} value1 The value to replace token {0}
952      * @param {String} value2 Etc...
953      * @return {String} The formatted string
954      * @static
955      */
956     format : function(format){
957         var args = Ext.toArray(arguments, 1);
958         return format.replace(/\{(\d+)\}/g, function(m, i){
959             return args[i];
960         });
961     }
962 });
963
964 /**
965  * @class Array
966  */
967 Ext.applyIf(Array.prototype, {
968     /**
969      * Checks whether or not the specified object exists in the array.
970      * @param {Object} o The object to check for
971      * @param {Number} from (Optional) The index at which to begin the search
972      * @return {Number} The index of o in the array (or -1 if it is not found)
973      */
974     indexOf : function(o, from){
975         var len = this.length;
976         from = from || 0;
977         from += (from < 0) ? len : 0;
978         for (; from < len; ++from){
979             if(this[from] === o){
980                 return from;
981             }
982         }
983         return -1;
984     },
985
986     /**
987      * Removes the specified object from the array.  If the object is not found nothing happens.
988      * @param {Object} o The object to remove
989      * @return {Array} this array
990      */
991     remove : function(o){
992         var index = this.indexOf(o);
993         if(index != -1){
994             this.splice(index, 1);
995         }
996         return this;
997     }
998 });