Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / core / src / Ext-more.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext
17
18  The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
19  Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
20  as direct properties of the Ext namespace.
21
22  Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
23  For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
24
25  Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
26  This ensures all scripts have been loaded, preventing dependency issues. For example
27
28      Ext.onReady(function(){
29          new Ext.Component({
30              renderTo: document.body,
31              html: 'DOM ready!'
32          });
33      });
34
35 For more information about how to use the Ext classes, see
36
37 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
38 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
39 - <a href="http://www.sencha.com/forum/">The forums</a>
40
41  * @singleton
42  * @markdown
43  */
44 Ext.apply(Ext, {
45     userAgent: navigator.userAgent.toLowerCase(),
46     cache: {},
47     idSeed: 1000,
48     windowId: 'ext-window',
49     documentId: 'ext-document',
50
51     /**
52      * True when the document is fully initialized and ready for action
53      * @type Boolean
54      */
55     isReady: false,
56
57     /**
58      * True to automatically uncache orphaned Ext.Elements periodically
59      * @type Boolean
60      */
61     enableGarbageCollector: true,
62
63     /**
64      * True to automatically purge event listeners during garbageCollection.
65      * @type Boolean
66      */
67     enableListenerCollection: true,
68
69     /**
70      * Generates unique ids. If the element already has an id, it is unchanged
71      * @param {HTMLElement/Ext.Element} el (optional) The element to generate an id for
72      * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
73      * @return {String} The generated Id.
74      */
75     id: function(el, prefix) {
76         var me = this,
77             sandboxPrefix = '';
78         el = Ext.getDom(el, true) || {};
79         if (el === document) {
80             el.id = me.documentId;
81         }
82         else if (el === window) {
83             el.id = me.windowId;
84         }
85         if (!el.id) {
86             if (me.isSandboxed) {
87                 if (!me.uniqueGlobalNamespace) {
88                     me.getUniqueGlobalNamespace();
89                 }
90                 sandboxPrefix = me.uniqueGlobalNamespace + '-';
91             }
92             el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
93         }
94         return el.id;
95     },
96
97     /**
98      * Returns the current document body as an {@link Ext.Element}.
99      * @return Ext.Element The document body
100      */
101     getBody: function() {
102         return Ext.get(document.body || false);
103     },
104
105     /**
106      * Returns the current document head as an {@link Ext.Element}.
107      * @return Ext.Element The document head
108      * @method
109      */
110     getHead: function() {
111         var head;
112
113         return function() {
114             if (head == undefined) {
115                 head = Ext.get(document.getElementsByTagName("head")[0]);
116             }
117
118             return head;
119         };
120     }(),
121
122     /**
123      * Returns the current HTML document object as an {@link Ext.Element}.
124      * @return Ext.Element The document
125      */
126     getDoc: function() {
127         return Ext.get(document);
128     },
129
130     /**
131      * This is shorthand reference to {@link Ext.ComponentManager#get}.
132      * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
133      * @param {String} id The component {@link Ext.Component#id id}
134      * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
135      * Class was found.
136     */
137     getCmp: function(id) {
138         return Ext.ComponentManager.get(id);
139     },
140
141     /**
142      * Returns the current orientation of the mobile device
143      * @return {String} Either 'portrait' or 'landscape'
144      */
145     getOrientation: function() {
146         return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
147     },
148
149     /**
150      * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
151      * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
152      * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
153      * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
154      * passed into this function in a single call as separate arguments.
155      * @param {Ext.Element/Ext.Component/Ext.Element[]/Ext.Component[]...} arg1
156      * An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
157      */
158     destroy: function() {
159         var ln = arguments.length,
160         i, arg;
161
162         for (i = 0; i < ln; i++) {
163             arg = arguments[i];
164             if (arg) {
165                 if (Ext.isArray(arg)) {
166                     this.destroy.apply(this, arg);
167                 }
168                 else if (Ext.isFunction(arg.destroy)) {
169                     arg.destroy();
170                 }
171                 else if (arg.dom) {
172                     arg.remove();
173                 }
174             }
175         }
176     },
177
178     /**
179      * Execute a callback function in a particular scope. If no function is passed the call is ignored.
180      *
181      * For example, these lines are equivalent:
182      *
183      *     Ext.callback(myFunc, this, [arg1, arg2]);
184      *     Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
185      *
186      * @param {Function} callback The callback to execute
187      * @param {Object} scope (optional) The scope to execute in
188      * @param {Array} args (optional) The arguments to pass to the function
189      * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
190      */
191     callback: function(callback, scope, args, delay){
192         if(Ext.isFunction(callback)){
193             args = args || [];
194             scope = scope || window;
195             if (delay) {
196                 Ext.defer(callback, delay, scope, args);
197             } else {
198                 callback.apply(scope, args);
199             }
200         }
201     },
202
203     /**
204      * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
205      * @param {String} value The string to encode
206      * @return {String} The encoded text
207      */
208     htmlEncode : function(value) {
209         return Ext.String.htmlEncode(value);
210     },
211
212     /**
213      * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
214      * @param {String} value The string to decode
215      * @return {String} The decoded text
216      */
217     htmlDecode : function(value) {
218          return Ext.String.htmlDecode(value);
219     },
220
221     /**
222      * Appends content to the query string of a URL, handling logic for whether to place
223      * a question mark or ampersand.
224      * @param {String} url The URL to append to.
225      * @param {String} s The content to append to the URL.
226      * @return (String) The resulting URL
227      */
228     urlAppend : function(url, s) {
229         if (!Ext.isEmpty(s)) {
230             return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
231         }
232         return url;
233     }
234 });
235
236
237 Ext.ns = Ext.namespace;
238
239 // for old browsers
240 window.undefined = window.undefined;
241
242 /**
243  * @class Ext
244  * Ext core utilities and functions.
245  * @singleton
246  */
247 (function(){
248 /*
249 FF 3.6      - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17
250 FF 4.0.1    - Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
251 FF 5.0      - Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0
252
253 IE6         - Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)
254 IE7         - Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;)
255 IE8         - Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)
256 IE9         - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
257
258 Chrome 11   - Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.60 Safari/534.24
259
260 Safari 5    - Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1
261
262 Opera 11.11 - Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
263 */
264     var check = function(regex){
265             return regex.test(Ext.userAgent);
266         },
267         isStrict = document.compatMode == "CSS1Compat",
268         version = function (is, regex) {
269             var m;
270             return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0;
271         },
272         docMode = document.documentMode,
273         isOpera = check(/opera/),
274         isOpera10_5 = isOpera && check(/version\/10\.5/),
275         isChrome = check(/\bchrome\b/),
276         isWebKit = check(/webkit/),
277         isSafari = !isChrome && check(/safari/),
278         isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
279         isSafari3 = isSafari && check(/version\/3/),
280         isSafari4 = isSafari && check(/version\/4/),
281         isSafari5 = isSafari && check(/version\/5/),
282         isIE = !isOpera && check(/msie/),
283         isIE7 = isIE && (check(/msie 7/) || docMode == 7),
284         isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
285         isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
286         isIE6 = isIE && check(/msie 6/),
287         isGecko = !isWebKit && check(/gecko/),
288         isGecko3 = isGecko && check(/rv:1\.9/),
289         isGecko4 = isGecko && check(/rv:2\.0/),
290         isGecko5 = isGecko && check(/rv:5\./),
291         isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
292         isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
293         isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
294         isWindows = check(/windows|win32/),
295         isMac = check(/macintosh|mac os x/),
296         isLinux = check(/linux/),
297         scrollbarSize = null,
298         chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/),
299         firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/),
300         ieVersion = version(isIE, /msie (\d+\.\d+)/),
301         operaVersion = version(isOpera, /version\/(\d+\.\d+)/),
302         safariVersion = version(isSafari, /version\/(\d+\.\d+)/),
303         webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/),
304         isSecure = /^https/i.test(window.location.protocol);
305
306     // remove css image flicker
307     try {
308         document.execCommand("BackgroundImageCache", false, true);
309     } catch(e) {}
310
311     //<debug>
312     function dumpObject (object) {
313         var member, members = [];
314
315         // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
316         // ...and the data could be prettier!
317         Ext.Object.each(object, function (name, value) {
318             if (typeof(value) === "function") {
319                 return;
320             }
321
322             if (!Ext.isDefined(value) || value === null ||
323                     Ext.isDate(value) ||
324                     Ext.isString(value) || (typeof(value) == "number") ||
325                     Ext.isBoolean(value)) {
326                 member = Ext.encode(value);
327             } else if (Ext.isArray(value)) {
328                 member = '[ ]';
329             } else if (Ext.isObject(value)) {
330                 member = '{ }';
331             } else {
332                 member = 'undefined';
333             }
334             members.push(Ext.encode(name) + ': ' + member);
335         });
336
337         if (members.length) {
338             return ' \nData: {\n  ' + members.join(',\n  ') + '\n}';
339         }
340         return '';
341     }
342
343     function log (message) {
344         var options, dump,
345             con = Ext.global.console,
346             level = 'log',
347             indent = log.indent || 0,
348             stack;
349
350         log.indent = indent;
351
352         if (!Ext.isString(message)) {
353             options = message;
354             message = options.msg || '';
355             level = options.level || level;
356             dump = options.dump;
357             stack = options.stack;
358
359             if (options.indent) {
360                 ++log.indent;
361             } else if (options.outdent) {
362                 log.indent = indent = Math.max(indent - 1, 0);
363             }
364
365             if (dump && !(con && con.dir)) {
366                 message += dumpObject(dump);
367                 dump = null;
368             }
369         }
370
371         if (arguments.length > 1) {
372             message += Array.prototype.slice.call(arguments, 1).join('');
373         }
374
375         message = indent ? Ext.String.repeat('   ', indent) + message : message;
376         // w/o console, all messages are equal, so munge the level into the message:
377         if (level != 'log') {
378             message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
379         }
380
381         // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
382         // an early test may fail either direction if Firebug is toggled.
383         //
384         if (con) { // if (Firebug-like console)
385             if (con[level]) {
386                 con[level](message);
387             } else {
388                 con.log(message);
389             }
390
391             if (dump) {
392                 con.dir(dump);
393             }
394
395             if (stack && con.trace) {
396                 // Firebug's console.error() includes a trace already...
397                 if (!con.firebug || level != 'error') {
398                     con.trace();
399                 }
400             }
401         } else {
402             if (Ext.isOpera) {
403                 opera.postError(message);
404             } else {
405                 var out = log.out,
406                     max = log.max;
407
408                 if (out.length >= max) {
409                     // this formula allows out.max to change (via debugger), where the
410                     // more obvious "max/4" would not quite be the same
411                     Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
412                 }
413
414                 out.push(message);
415             }
416         }
417
418         // Mostly informational, but the Ext.Error notifier uses them:
419         ++log.count;
420         ++log.counters[level];
421     }
422
423     log.count = 0;
424     log.counters = { error: 0, warn: 0, info: 0, log: 0 };
425     log.out = [];
426     log.max = 250;
427     log.show = function () {
428         window.open('','extlog').document.write([
429             '<html><head><script type="text/javascript">',
430                 'var lastCount = 0;',
431                 'function update () {',
432                     'var ext = window.opener.Ext,',
433                         'extlog = ext && ext.log;',
434                     'if (extlog && extlog.out && lastCount != extlog.count) {',
435                         'lastCount = extlog.count;',
436                         'var s = "<tt>" + extlog.out.join("<br>").replace(/[ ]/g, "&nbsp;") + "</tt>";',
437                         'document.body.innerHTML = s;',
438                     '}',
439                     'setTimeout(update, 1000);',
440                 '}',
441                 'setTimeout(update, 1000);',
442             '</script></head><body></body></html>'].join(''));
443     };
444     //</debug>
445
446     Ext.setVersion('extjs', '4.0.7');
447     Ext.apply(Ext, {
448         /**
449          * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
450          * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
451          * @type String
452          */
453         SSL_SECURE_URL : isSecure && isIE ? 'javascript:""' : 'about:blank',
454
455         /**
456          * True if the {@link Ext.fx.Anim} Class is available
457          * @type Boolean
458          * @property enableFx
459          */
460
461         /**
462          * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
463          * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
464          * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
465          * @type Boolean
466          */
467         scopeResetCSS : Ext.buildSettings.scopeResetCSS,
468
469         /**
470          * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
471          * Currently not optimized for performance.
472          * @type Boolean
473          */
474         enableNestedListenerRemoval : false,
475
476         /**
477          * Indicates whether to use native browser parsing for JSON methods.
478          * This option is ignored if the browser does not support native JSON methods.
479          * <b>Note: Native JSON methods will not work with objects that have functions.
480          * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
481          * @type Boolean
482          */
483         USE_NATIVE_JSON : false,
484
485         /**
486          * Return the dom node for the passed String (id), dom node, or Ext.Element.
487          * Optional 'strict' flag is needed for IE since it can return 'name' and
488          * 'id' elements by using getElementById.
489          * Here are some examples:
490          * <pre><code>
491 // gets dom node based on id
492 var elDom = Ext.getDom('elId');
493 // gets dom node based on the dom node
494 var elDom1 = Ext.getDom(elDom);
495
496 // If we don&#39;t know if we are working with an
497 // Ext.Element or a dom node use Ext.getDom
498 function(el){
499     var dom = Ext.getDom(el);
500     // do something with the dom node
501 }
502          * </code></pre>
503          * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
504          * when this method is called to be successful.
505          * @param {String/HTMLElement/Ext.Element} el
506          * @return HTMLElement
507          */
508         getDom : function(el, strict) {
509             if (!el || !document) {
510                 return null;
511             }
512             if (el.dom) {
513                 return el.dom;
514             } else {
515                 if (typeof el == 'string') {
516                     var e = document.getElementById(el);
517                     // IE returns elements with the 'name' and 'id' attribute.
518                     // we do a strict check to return the element with only the id attribute
519                     if (e && isIE && strict) {
520                         if (el == e.getAttribute('id')) {
521                             return e;
522                         } else {
523                             return null;
524                         }
525                     }
526                     return e;
527                 } else {
528                     return el;
529                 }
530             }
531         },
532
533         /**
534          * Removes a DOM node from the document.
535          * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
536          * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
537          * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
538          * will be ignored if passed in.</p>
539          * @param {HTMLElement} node The node to remove
540          * @method
541          */
542         removeNode : isIE6 || isIE7 ? function() {
543             var d;
544             return function(n){
545                 if(n && n.tagName != 'BODY'){
546                     (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
547                     d = d || document.createElement('div');
548                     d.appendChild(n);
549                     d.innerHTML = '';
550                     delete Ext.cache[n.id];
551                 }
552             };
553         }() : function(n) {
554             if (n && n.parentNode && n.tagName != 'BODY') {
555                 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
556                 n.parentNode.removeChild(n);
557                 delete Ext.cache[n.id];
558             }
559         },
560
561         isStrict: isStrict,
562
563         isIEQuirks: isIE && !isStrict,
564
565         /**
566          * True if the detected browser is Opera.
567          * @type Boolean
568          */
569         isOpera : isOpera,
570
571         /**
572          * True if the detected browser is Opera 10.5x.
573          * @type Boolean
574          */
575         isOpera10_5 : isOpera10_5,
576
577         /**
578          * True if the detected browser uses WebKit.
579          * @type Boolean
580          */
581         isWebKit : isWebKit,
582
583         /**
584          * True if the detected browser is Chrome.
585          * @type Boolean
586          */
587         isChrome : isChrome,
588
589         /**
590          * True if the detected browser is Safari.
591          * @type Boolean
592          */
593         isSafari : isSafari,
594
595         /**
596          * True if the detected browser is Safari 3.x.
597          * @type Boolean
598          */
599         isSafari3 : isSafari3,
600
601         /**
602          * True if the detected browser is Safari 4.x.
603          * @type Boolean
604          */
605         isSafari4 : isSafari4,
606
607         /**
608          * True if the detected browser is Safari 5.x.
609          * @type Boolean
610          */
611         isSafari5 : isSafari5,
612
613         /**
614          * True if the detected browser is Safari 2.x.
615          * @type Boolean
616          */
617         isSafari2 : isSafari2,
618
619         /**
620          * True if the detected browser is Internet Explorer.
621          * @type Boolean
622          */
623         isIE : isIE,
624
625         /**
626          * True if the detected browser is Internet Explorer 6.x.
627          * @type Boolean
628          */
629         isIE6 : isIE6,
630
631         /**
632          * True if the detected browser is Internet Explorer 7.x.
633          * @type Boolean
634          */
635         isIE7 : isIE7,
636
637         /**
638          * True if the detected browser is Internet Explorer 8.x.
639          * @type Boolean
640          */
641         isIE8 : isIE8,
642
643         /**
644          * True if the detected browser is Internet Explorer 9.x.
645          * @type Boolean
646          */
647         isIE9 : isIE9,
648
649         /**
650          * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
651          * @type Boolean
652          */
653         isGecko : isGecko,
654
655         /**
656          * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
657          * @type Boolean
658          */
659         isGecko3 : isGecko3,
660
661         /**
662          * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
663          * @type Boolean
664          */
665         isGecko4 : isGecko4,
666
667         /**
668          * True if the detected browser uses a Gecko 5.0+ layout engine (e.g. Firefox 5.x).
669          * @type Boolean
670          */
671         isGecko5 : isGecko5,
672
673         /**
674          * True if the detected browser uses FireFox 3.0
675          * @type Boolean
676          */
677         isFF3_0 : isFF3_0,
678
679         /**
680          * True if the detected browser uses FireFox 3.5
681          * @type Boolean
682          */
683         isFF3_5 : isFF3_5,
684
685         /**
686          * True if the detected browser uses FireFox 3.6
687          * @type Boolean
688          */
689         isFF3_6 : isFF3_6,
690
691         /**
692          * True if the detected browser uses FireFox 4
693          * @type Boolean
694          */
695         isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,
696
697         /**
698          * True if the detected browser uses FireFox 5
699          * @type Boolean
700          */
701         isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,
702
703         /**
704          * True if the detected platform is Linux.
705          * @type Boolean
706          */
707         isLinux : isLinux,
708
709         /**
710          * True if the detected platform is Windows.
711          * @type Boolean
712          */
713         isWindows : isWindows,
714
715         /**
716          * True if the detected platform is Mac OS.
717          * @type Boolean
718          */
719         isMac : isMac,
720
721         /**
722          * The current version of Chrome (0 if the browser is not Chrome).
723          * @type Number
724          */
725         chromeVersion: chromeVersion,
726
727         /**
728          * The current version of Firefox (0 if the browser is not Firefox).
729          * @type Number
730          */
731         firefoxVersion: firefoxVersion,
732
733         /**
734          * The current version of IE (0 if the browser is not IE). This does not account
735          * for the documentMode of the current page, which is factored into {@link #isIE7},
736          * {@link #isIE8} and {@link #isIE9}. Thus this is not always true:
737          *
738          *      Ext.isIE8 == (Ext.ieVersion == 8)
739          *
740          * @type Number
741          * @markdown
742          */
743         ieVersion: ieVersion,
744
745         /**
746          * The current version of Opera (0 if the browser is not Opera).
747          * @type Number
748          */
749         operaVersion: operaVersion,
750
751         /**
752          * The current version of Safari (0 if the browser is not Safari).
753          * @type Number
754          */
755         safariVersion: safariVersion,
756
757         /**
758          * The current version of WebKit (0 if the browser does not use WebKit).
759          * @type Number
760          */
761         webKitVersion: webKitVersion,
762
763         /**
764          * True if the page is running over SSL
765          * @type Boolean
766          */
767         isSecure: isSecure,
768
769         /**
770          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
771          * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
772          * For other browsers it uses an inline data URL.
773          * @type String
774          */
775         BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
776
777         /**
778          * <p>Utility method for returning a default value if the passed value is empty.</p>
779          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
780          * <li>null</li>
781          * <li>undefined</li>
782          * <li>an empty array</li>
783          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
784          * </ul></div>
785          * @param {Object} value The value to test
786          * @param {Object} defaultValue The value to return if the original value is empty
787          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
788          * @return {Object} value, if non-empty, else defaultValue
789          * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
790          */
791         value : function(v, defaultValue, allowBlank){
792             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
793         },
794
795         /**
796          * Escapes the passed string for use in a regular expression
797          * @param {String} str
798          * @return {String}
799          * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
800          */
801         escapeRe : function(s) {
802             return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
803         },
804
805         /**
806          * Applies event listeners to elements by selectors when the document is ready.
807          * The event name is specified with an <tt>&#64;</tt> suffix.
808          * <pre><code>
809 Ext.addBehaviors({
810     // add a listener for click on all anchors in element with id foo
811     '#foo a&#64;click' : function(e, t){
812         // do something
813     },
814
815     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
816     '#foo a, #bar span.some-class&#64;mouseover' : function(){
817         // do something
818     }
819 });
820          * </code></pre>
821          * @param {Object} obj The list of behaviors to apply
822          */
823         addBehaviors : function(o){
824             if(!Ext.isReady){
825                 Ext.onReady(function(){
826                     Ext.addBehaviors(o);
827                 });
828             } else {
829                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
830                     parts,
831                     b,
832                     s;
833                 for (b in o) {
834                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
835                         s = parts[0];
836                         if(!cache[s]){
837                             cache[s] = Ext.select(s);
838                         }
839                         cache[s].on(parts[1], o[b]);
840                     }
841                 }
842                 cache = null;
843             }
844         },
845
846         /**
847          * Returns the size of the browser scrollbars. This can differ depending on
848          * operating system settings, such as the theme or font size.
849          * @param {Boolean} force (optional) true to force a recalculation of the value.
850          * @return {Object} An object containing the width of a vertical scrollbar and the
851          * height of a horizontal scrollbar.
852          */
853         getScrollbarSize: function (force) {
854             if(!Ext.isReady){
855                 return 0;
856             }
857
858             if(force === true || scrollbarSize === null){
859                 // BrowserBug: IE9
860                 // When IE9 positions an element offscreen via offsets, the offsetWidth is
861                 // inaccurately reported. For IE9 only, we render on screen before removing.
862                 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
863                     // Append our div, do our calculation and then remove it
864                     div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
865                     child = div.child('div', true),
866                     w1 = child.offsetWidth;
867
868                 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
869
870                 var w2 = child.offsetWidth, width = w1 - w2;
871                 div.remove();
872
873                 // We assume width == height for now. TODO: is this always true?
874                 scrollbarSize = { width: width, height: width };
875             }
876
877             return scrollbarSize;
878         },
879
880         /**
881          * Utility method for getting the width of the browser's vertical scrollbar. This
882          * can differ depending on operating system settings, such as the theme or font size.
883          *
884          * This method is deprected in favor of {@link #getScrollbarSize}.
885          *
886          * @param {Boolean} force (optional) true to force a recalculation of the value.
887          * @return {Number} The width of a vertical scrollbar.
888          * @deprecated
889          */
890         getScrollBarWidth: function(force){
891             var size = Ext.getScrollbarSize(force);
892             return size.width + 2; // legacy fudge factor
893         },
894
895         /**
896          * Copies a set of named properties fom the source object to the destination object.
897          *
898          * Example:
899          *
900          *     ImageComponent = Ext.extend(Ext.Component, {
901          *         initComponent: function() {
902          *             this.autoEl = { tag: 'img' };
903          *             MyComponent.superclass.initComponent.apply(this, arguments);
904          *             this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
905          *         }
906          *     });
907          *
908          * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
909          *
910          * @param {Object} dest The destination object.
911          * @param {Object} source The source object.
912          * @param {String/String[]} names Either an Array of property names, or a comma-delimited list
913          * of property names to copy.
914          * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
915          * @return {Object} The modified object.
916          */
917         copyTo : function(dest, source, names, usePrototypeKeys){
918             if(typeof names == 'string'){
919                 names = names.split(/[,;\s]/);
920             }
921             Ext.each(names, function(name){
922                 if(usePrototypeKeys || source.hasOwnProperty(name)){
923                     dest[name] = source[name];
924                 }
925             }, this);
926             return dest;
927         },
928
929         /**
930          * Attempts to destroy and then remove a set of named properties of the passed object.
931          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
932          * @param {String...} args One or more names of the properties to destroy and remove from the object.
933          */
934         destroyMembers : function(o){
935             for (var i = 1, a = arguments, len = a.length; i < len; i++) {
936                 Ext.destroy(o[a[i]]);
937                 delete o[a[i]];
938             }
939         },
940
941         /**
942          * Logs a message. If a console is present it will be used. On Opera, the method
943          * "opera.postError" is called. In other cases, the message is logged to an array
944          * "Ext.log.out". An attached debugger can watch this array and view the log. The
945          * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).
946          * The `Ext.log.out` array can also be written to a popup window by entering the
947          * following in the URL bar (a "bookmarklet"):
948          *
949          *    javascript:void(Ext.log.show());
950          *
951          * If additional parameters are passed, they are joined and appended to the message.
952          * A technique for tracing entry and exit of a function is this:
953          *
954          *      function foo () {
955          *          Ext.log({ indent: 1 }, '>> foo');
956          *
957          *          // log statements in here or methods called from here will be indented
958          *          // by one step
959          *
960          *          Ext.log({ outdent: 1 }, '<< foo');
961          *      }
962          *
963          * This method does nothing in a release build.
964          *
965          * @param {String/Object} message The message to log or an options object with any
966          * of the following properties:
967          *
968          *  - `msg`: The message to log (required).
969          *  - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
970          *  - `dump`: An object to dump to the log as part of the message.
971          *  - `stack`: True to include a stack trace in the log.
972          *  - `indent`: Cause subsequent log statements to be indented one step.
973          *  - `outdent`: Cause this and following statements to be one step less indented.
974          * @markdown
975          */
976         log :
977             //<debug>
978             log ||
979             //</debug>
980             Ext.emptyFn,
981
982         /**
983          * Partitions the set into two sets: a true set and a false set.
984          * Example:
985          * Example2:
986          * <pre><code>
987 // Example 1:
988 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
989
990 // Example 2:
991 Ext.partition(
992     Ext.query("p"),
993     function(val){
994         return val.className == "class1"
995     }
996 );
997 // true are those paragraph elements with a className of "class1",
998 // false set are those that do not have that className.
999          * </code></pre>
1000          * @param {Array/NodeList} arr The array to partition
1001          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
1002          * itself must be able to be evaluated for its truthfulness.
1003          * @return {Array} [array of truish values, array of falsy values]
1004          * @deprecated 4.0.0 Will be removed in the next major version
1005          */
1006         partition : function(arr, truth){
1007             var ret = [[],[]];
1008             Ext.each(arr, function(v, i, a) {
1009                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
1010             });
1011             return ret;
1012         },
1013
1014         /**
1015          * Invokes a method on each item in an Array.
1016          * <pre><code>
1017 // Example:
1018 Ext.invoke(Ext.query("p"), "getAttribute", "id");
1019 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
1020          * </code></pre>
1021          * @param {Array/NodeList} arr The Array of items to invoke the method on.
1022          * @param {String} methodName The method name to invoke.
1023          * @param {Object...} args Arguments to send into the method invocation.
1024          * @return {Array} The results of invoking the method on each item in the array.
1025          * @deprecated 4.0.0 Will be removed in the next major version
1026          */
1027         invoke : function(arr, methodName){
1028             var ret = [],
1029                 args = Array.prototype.slice.call(arguments, 2);
1030             Ext.each(arr, function(v,i) {
1031                 if (v && typeof v[methodName] == 'function') {
1032                     ret.push(v[methodName].apply(v, args));
1033                 } else {
1034                     ret.push(undefined);
1035                 }
1036             });
1037             return ret;
1038         },
1039
1040         /**
1041          * <p>Zips N sets together.</p>
1042          * <pre><code>
1043 // Example 1:
1044 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
1045 // Example 2:
1046 Ext.zip(
1047     [ "+", "-", "+"],
1048     [  12,  10,  22],
1049     [  43,  15,  96],
1050     function(a, b, c){
1051         return "$" + a + "" + b + "." + c
1052     }
1053 ); // ["$+12.43", "$-10.15", "$+22.96"]
1054          * </code></pre>
1055          * @param {Array/NodeList...} arr This argument may be repeated. Array(s) to contribute values.
1056          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
1057          * @return {Array} The zipped set.
1058          * @deprecated 4.0.0 Will be removed in the next major version
1059          */
1060         zip : function(){
1061             var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
1062                 arrs = parts[0],
1063                 fn = parts[1][0],
1064                 len = Ext.max(Ext.pluck(arrs, "length")),
1065                 ret = [];
1066
1067             for (var i = 0; i < len; i++) {
1068                 ret[i] = [];
1069                 if(fn){
1070                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
1071                 }else{
1072                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
1073                         ret[i].push( arrs[j][i] );
1074                     }
1075                 }
1076             }
1077             return ret;
1078         },
1079
1080         /**
1081          * Turns an array into a sentence, joined by a specified connector - e.g.:
1082          * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
1083          * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
1084          * @param {String[]} items The array to create a sentence from
1085          * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
1086          * @return {String} The sentence string
1087          * @deprecated 4.0.0 Will be removed in the next major version
1088          */
1089         toSentence: function(items, connector) {
1090             var length = items.length;
1091
1092             if (length <= 1) {
1093                 return items[0];
1094             } else {
1095                 var head = items.slice(0, length - 1),
1096                     tail = items[length - 1];
1097
1098                 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
1099             }
1100         },
1101
1102         /**
1103          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
1104          * you may want to set this to true.
1105          * @type Boolean
1106          */
1107         useShims: isIE6
1108     });
1109 })();
1110
1111 /**
1112  * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
1113  *
1114  * See Ext.app.Application for details.
1115  *
1116  * @param {Object} config
1117  */
1118 Ext.application = function(config) {
1119     Ext.require('Ext.app.Application');
1120
1121     Ext.onReady(function() {
1122         Ext.create('Ext.app.Application', config);
1123     });
1124 };
1125