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