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