]> git.proxmox.com Git - extjs.git/blob - extjs/packages/core/src/Ext.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / Ext.js
1 // @tag core
2 /**
3 * @class Ext
4 *
5 * The Ext namespace (global object) encapsulates all classes, singletons, and
6 * utility methods provided by Sencha's libraries.
7 *
8 * Most user interface Components are at a lower level of nesting in the namespace,
9 * but many common utility functions are provided as direct properties of the Ext namespace.
10 *
11 * Also many frequently used methods from other classes are provided as shortcuts
12 * within the Ext namespace. For example {@link Ext#getCmp Ext.getCmp} aliases
13 * {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
14 *
15 * Many applications are initiated with {@link Ext#application Ext.application} which is
16 * called once the DOM is ready. This ensures all scripts have been loaded, preventing
17 * dependency issues. For example:
18 *
19 * Ext.application({
20 * name: 'MyApp',
21 *
22 * launch: function () {
23 * Ext.Msg.alert(this.name, 'Ready to go!');
24 * }
25 * });
26 *
27 * <b><a href="http://www.sencha.com/products/sencha-cmd/">Sencha Cmd</a></b> is a free tool
28 * for helping you generate and build Ext JS (and Sencha Touch) applications. See
29 * `{@link Ext.app.Application Application}` for more information about creating an app.
30 *
31 * A lower-level technique that does not use the `Ext.app.Application` architecture is
32 * {@link Ext#onReady Ext.onReady}.
33 *
34 * For more information about how to use the Ext classes, see:
35 *
36 * - <a href="http://www.sencha.com/learn/">The Learning Center</a>
37 * - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
38 * - <a href="http://www.sencha.com/forum/">The forums</a>
39 *
40 * @singleton
41 */
42 var Ext = Ext || {}; // jshint ignore:line
43 // @define Ext
44
45 (function() {
46 var global = this,
47 objectPrototype = Object.prototype,
48 toString = objectPrototype.toString,
49 enumerables = [//'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
50 'valueOf', 'toLocaleString', 'toString', 'constructor'],
51 emptyFn = function () {},
52 privateFn = function () {},
53 identityFn = function(o) { return o; },
54 // This is the "$previous" method of a hook function on an instance. When called, it
55 // calls through the class prototype by the name of the called method.
56 callOverrideParent = function () {
57 var method = callOverrideParent.caller.caller; // skip callParent (our caller)
58 return method.$owner.prototype[method.$name].apply(this, arguments);
59 },
60 manifest = Ext.manifest || {},
61 i,
62 iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,
63 MSDateRe = /^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;
64
65 Ext.global = global;
66
67 /**
68 * Returns the current timestamp.
69 * @return {Number} Milliseconds since UNIX epoch.
70 * @method now
71 * @member Ext
72 */
73 Ext.now = Date.now || (Date.now = function() {
74 return +new Date();
75 });
76
77 /**
78 * Returns the current high-resolution timestamp.
79 * @return {Number} Milliseconds ellapsed since arbitrary epoch.
80 * @method ticks
81 * @member Ext
82 * @since 6.0.1
83 */
84 Ext.ticks = (global.performance && global.performance.now) ? function() {
85 return performance.now(); // jshint ignore:line
86 } : Ext.now;
87
88 Ext._startTime = Ext.ticks();
89
90 // Mark these special fn's for easy identification:
91 emptyFn.$nullFn = identityFn.$nullFn = emptyFn.$emptyFn = identityFn.$identityFn =
92 privateFn.$nullFn = true;
93 privateFn.$privacy = 'framework';
94
95 // These are emptyFn's in core and are redefined only in Ext JS (we use this syntax
96 // so Cmd does not detect them):
97 Ext['suspendLayouts'] = Ext['resumeLayouts'] = emptyFn; // jshint ignore:line
98
99 for (i in { toString: 1 }) {
100 enumerables = null;
101 }
102
103 /**
104 * An array containing extra enumerables for old browsers
105 * @property {String[]}
106 */
107 Ext.enumerables = enumerables;
108
109 /**
110 * Copies all the properties of `config` to the specified `object`. There are two levels
111 * of defaulting supported:
112 *
113 * Ext.apply(obj, { a: 1 }, { a: 2 });
114 * //obj.a === 1
115 *
116 * Ext.apply(obj, { }, { a: 2 });
117 * //obj.a === 2
118 *
119 * Note that if recursive merging and cloning without referencing the original objects
120 * or arrays is needed, use {@link Ext.Object#merge} instead.
121 *
122 * @param {Object} object The receiver of the properties.
123 * @param {Object} config The primary source of the properties.
124 * @param {Object} [defaults] An object that will also be applied for default values.
125 * @return {Object} returns `object`.
126 */
127 Ext.apply = function(object, config, defaults) {
128 if (defaults) {
129 Ext.apply(object, defaults);
130 }
131
132 if (object && config && typeof config === 'object') {
133 var i, j, k;
134
135 for (i in config) {
136 object[i] = config[i];
137 }
138
139 if (enumerables) {
140 for (j = enumerables.length; j--;) {
141 k = enumerables[j];
142 if (config.hasOwnProperty(k)) {
143 object[k] = config[k];
144 }
145 }
146 }
147 }
148
149 return object;
150 };
151
152 // Used by Ext.override
153 function addInstanceOverrides(target, owner, overrides) {
154 var name, value;
155
156 for (name in overrides) {
157 if (overrides.hasOwnProperty(name)) {
158 value = overrides[name];
159
160 if (typeof value === 'function') {
161 //<debug>
162 if (owner.$className) {
163 value.name = owner.$className + '#' + name;
164 }
165 //</debug>
166
167 value.$name = name;
168 value.$owner = owner;
169
170 value.$previous = target.hasOwnProperty(name) ?
171 target[name] // already hooked, so call previous hook
172 : callOverrideParent; // calls by name on prototype
173 }
174
175 target[name] = value;
176 }
177 }
178 }
179
180 Ext.buildSettings = Ext.apply({
181 baseCSSPrefix: 'x-'
182 }, Ext.buildSettings || {});
183
184 Ext.apply(Ext, {
185 /**
186 * @private
187 */
188 idSeed: 0,
189
190 /**
191 * @private
192 */
193 idPrefix: 'ext-',
194
195 /**
196 * @property {Boolean} isSecure
197 * True if the page is running over SSL
198 * @readonly
199 */
200 isSecure: /^https/i.test(window.location.protocol),
201
202 /**
203 * `true` to automatically uncache orphaned Ext.Elements periodically. If set to
204 * `false`, the application will be required to clean up orphaned Ext.Elements and
205 * it's listeners as to not cause memory leakage.
206 */
207 enableGarbageCollector: false,
208
209 /**
210 * True to automatically purge event listeners during garbageCollection.
211 */
212 enableListenerCollection: true,
213
214 /**
215 * @property {String} [name='Ext']
216 * <p>The name of the property in the global namespace (The <code>window</code> in browser environments) which refers to the current instance of Ext.</p>
217 * <p>This is usually <code>"Ext"</code>, but if a sandboxed build of ExtJS is being used, this will be an alternative name.</p>
218 * <p>If code is being generated for use by <code>eval</code> or to create a <code>new Function</code>, and the global instance
219 * of Ext must be referenced, this is the name that should be built into the code.</p>
220 */
221 name: Ext.sandboxName || 'Ext',
222
223 /**
224 * @property {Function}
225 * A reusable empty function for use as `privates` members.
226 *
227 * Ext.define('MyClass', {
228 * nothing: Ext.emptyFn,
229 *
230 * privates: {
231 * privateNothing: Ext.privateFn
232 * }
233 * });
234 *
235 */
236 privateFn: privateFn,
237
238 /**
239 * @property {Function}
240 * A reusable empty function.
241 */
242 emptyFn: emptyFn,
243
244 /**
245 * @property {Function}
246 * A reusable identity function that simply returns its first argument.
247 */
248 identityFn: identityFn,
249
250 /**
251 * This indicate the start timestamp of current cycle.
252 * It is only reliable during dom-event-initiated cycles and
253 * {@link Ext.draw.Animator} initiated cycles.
254 */
255 frameStartTime: Ext.now(),
256
257 /**
258 * This object is initialized prior to loading the framework (Ext JS or Sencha
259 * Touch) and contains settings and other information describing the application.
260 *
261 * For applications built using Sencha Cmd, this is produced from the `"app.json"`
262 * file with information extracted from all of the required packages' `"package.json"`
263 * files. This can be set to a string when your application is using the
264 * (microloader)[#/guide/microloader]. In this case, the string of "foo" will be
265 * requested as `"foo.json"` and the object in that JSON file will parsed and set
266 * as this object.
267 *
268 * @cfg {String/Object} manifest
269 *
270 * @cfg {String/Object} manifest.compatibility An object keyed by package name with
271 * the value being to desired compatibility level as a version number. If this is
272 * just a string, this version is assumed to apply to the framework ('ext' or
273 * 'touch'). Setting this value to less than 5 for 'ext' will enable the compatibility
274 * layer to assist in the application upgrade process. For details on the upgrade
275 * process, see the (Upgrade Guide)[#/guides/upgrade_50].
276 *
277 * @cfg {Object} manifest.debug An object configuring the debugging characteristics
278 * of the framework. See `Ext.debugConfig` which is set to this value.
279 *
280 * @cfg {Object} manifest.packages An object keyed by package name with the value
281 * being a subset of the package's `"package.json"` descriptor.
282 * @since 5.0.0
283 */
284 manifest: manifest,
285
286 //<debug>
287 /**
288 * @cfg {Object} [debugConfig]
289 * This object is used to enable or disable debugging for classes or namespaces. The
290 * default instance looks like this:
291 *
292 * Ext.debugConfig = {
293 * hooks: {
294 * '*': true
295 * }
296 * };
297 *
298 * Typically applications will set this in their `"app.json"` like so:
299 *
300 * {
301 * "debug": {
302 * "hooks": {
303 * // Default for all namespaces:
304 * '*': true,
305 *
306 * // Except for Ext namespace which is disabled
307 * 'Ext': false,
308 *
309 * // Except for Ext.layout namespace which is enabled
310 * 'Ext.layout': true
311 * }
312 * }
313 * }
314 *
315 * Alternatively, because this property is consumed very early in the load process of
316 * the framework, this can be set in a `script` tag that is defined prior to loading
317 * the framework itself.
318 *
319 * For example, to enable debugging for the `Ext.layout` namespace only:
320 *
321 * var Ext = Ext || {};
322 * Ext.debugConfig = {
323 * hooks: {
324 * //...
325 * }
326 * };
327 *
328 * For any class declared, the longest matching namespace specified determines if its
329 * `debugHooks` will be enabled. The default setting is specified by the '*' property.
330 *
331 * **NOTE:** This option only applies to debug builds. All debugging is disabled in
332 * production builds.
333 */
334 debugConfig: Ext.debugConfig || manifest.debug || {
335 hooks: {
336 '*': true
337 }
338 },
339 //</debug>
340
341 /**
342 * @property {Boolean} [enableAria=true] This property is provided for backward
343 * compatibility with previous versions of Ext JS. Accessibility is always enabled
344 * in Ext JS 6.0+
345 * @since 6.0.0
346 */
347 enableAria: true,
348
349 /**
350 * @property {Boolean} [enableAriaButtons=true] Set to `false` to disable WAI-ARIA
351 * compatibility checks for buttons.
352 * @since 6.0.0
353 */
354 enableAriaButtons: true,
355
356 /**
357 * @property {Boolean} [enableAriaPanels=true] Set to `false` to disable WAI-ARIA
358 * compatibility checks for panels.
359 * @since 6.0.0
360 */
361 enableAriaPanels: true,
362
363 startsWithHashRe: /^#/,
364
365 /**
366 * @property {RegExp}
367 * @private
368 * Regular expression used for validating identifiers.
369 */
370 validIdRe: /^[a-z_][a-z0-9\-_]*$/i,
371
372 /**
373 * @property {String} BLANK_IMAGE_URL
374 * URL to a 1x1 transparent gif image used by Ext to create inline icons with
375 * CSS background images.
376 */
377 BLANK_IMAGE_URL: 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
378
379 /**
380 * Converts an id (`'foo'`) into an id selector (`'#foo'`). This method is used
381 * internally by the framework whenever an id needs to be converted into a selector
382 * and is provided as a hook for those that need to escape IDs selectors since,
383 * as of Ext 5.0, the framework no longer escapes IDs by default.
384 * @private
385 * @param {String} id
386 * @return {String}
387 */
388 makeIdSelector: function(id) {
389 //<debug>
390 if (!Ext.validIdRe.test(id)) {
391 Ext.raise('Invalid id selector: "' + id + '"');
392 }
393 //</debug>
394 return '#' + id;
395 },
396
397 /**
398 * Generates unique ids. If the object/element is passes and it already has an `id`, it is unchanged.
399 * @param {Object} [o] The object to generate an id for.
400 * @param {String} [prefix=ext-gen] (optional) The `id` prefix.
401 * @return {String} The generated `id`.
402 */
403 id: function(o, prefix) {
404 if (o && o.id) {
405 return o.id;
406 }
407
408 var id = (prefix || Ext.idPrefix) + (++Ext.idSeed);
409
410 if (o) {
411 o.id = id;
412 }
413
414 return id;
415 },
416
417 /**
418 * A reusable function which returns the value of `getId()` called upon a single passed parameter.
419 * Useful when creating a {@link Ext.util.MixedCollection} of objects keyed by an identifier returned from a `getId` method.
420 */
421 returnId: function(o) {
422 return o.getId();
423 },
424
425 /**
426 * A reusable function which returns `true`.
427 */
428 returnTrue: function() {
429 return true;
430 },
431
432 /**
433 * A zero length string which will pass a truth test. Useful for passing to methods
434 * which use a truth test to reject <i>falsy</i> values where a string value must be cleared.
435 */
436 emptyString: new String(), // jshint ignore:line
437
438 /**
439 * @property {String} [baseCSSPrefix='x-']
440 * The base prefix to use for all `Ext` components. To configure this property, you should use the
441 * Ext.buildSettings object before the framework is loaded:
442 *
443 * Ext.buildSettings = {
444 * baseCSSPrefix : 'abc-'
445 * };
446 *
447 * or you can change it before any components are rendered:
448 *
449 * Ext.baseCSSPrefix = Ext.buildSettings.baseCSSPrefix = 'abc-';
450 *
451 * This will change what CSS classes components will use and you should
452 * then recompile the SASS changing the `$prefix` SASS variable to match.
453 */
454 baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
455
456 /**
457 * @property {Object} $eventNameMap
458 * A map of event names which contained the lower-cased versions of any mixed
459 * case event names.
460 * @private
461 */
462 $eventNameMap: {},
463
464 // Vendor-specific events do not work if lower-cased. This regex specifies event
465 // prefixes for names that should NOT be lower-cased by Ext.canonicalEventName()
466 $vendorEventRe: /^(Moz.+|MS.+|webkit.+)/,
467
468 // TODO: inlinable function - SDKTOOLS-686
469 /**
470 * @private
471 * @inline
472 */
473 canonicalEventName: function(name) {
474 return Ext.$eventNameMap[name] || (Ext.$eventNameMap[name] =
475 (Ext.$vendorEventRe.test(name) ? name : name.toLowerCase()));
476 },
477
478 /**
479 * Copies all the properties of config to object if they don't already exist.
480 * @param {Object} object The receiver of the properties
481 * @param {Object} config The source of the properties
482 * @return {Object} returns obj
483 */
484 applyIf: function(object, config) {
485 var property;
486
487 if (object) {
488 for (property in config) {
489 if (object[property] === undefined) {
490 object[property] = config[property];
491 }
492 }
493 }
494
495 return object;
496 },
497
498 /**
499 * Destroys all of the given objects. If arrays are passed, the elements of these
500 * are destroyed recursively.
501 *
502 * What it means to "destroy" an object depends on the type of object.
503 *
504 * * `Array`: Each element of the array is destroyed recursively.
505 * * `Object`: Any object with a `destroy` method will have that method called.
506 *
507 * @param {Mixed...} args Any number of objects or arrays.
508 */
509 destroy: function() {
510 var ln = arguments.length,
511 i, arg;
512
513 for (i = 0; i < ln; i++) {
514 arg = arguments[i];
515 if (arg) {
516 if (Ext.isArray(arg)) {
517 this.destroy.apply(this, arg);
518 } else if (Ext.isFunction(arg.destroy)) {
519 arg.destroy();
520 }
521 }
522 }
523 return null;
524 },
525
526 /**
527 * Destroys the specified named members of the given object using `Ext.destroy`. These
528 * properties will be set to `null`.
529 * @param {Object} object The object who's properties you wish to destroy.
530 * @param {String...} args One or more names of the properties to destroy and remove from the object.
531 */
532 destroyMembers: function (object) {
533 for (var ref, name, i = 1, a = arguments, len = a.length; i < len; i++) {
534 ref = object[name = a[i]];
535
536 // Avoid adding the property if it does not already exist
537 if (ref != null) {
538 object[name] = Ext.destroy(ref);
539 }
540 }
541 },
542
543 /**
544 * Overrides members of the specified `target` with the given values.
545 *
546 * If the `target` is a class declared using {@link Ext#define Ext.define}, the
547 * `override` method of that class is called (see {@link Ext.Base#override}) given
548 * the `overrides`.
549 *
550 * If the `target` is a function, it is assumed to be a constructor and the contents
551 * of `overrides` are applied to its `prototype` using {@link Ext#apply Ext.apply}.
552 *
553 * If the `target` is an instance of a class declared using {@link Ext#define Ext.define},
554 * the `overrides` are applied to only that instance. In this case, methods are
555 * specially processed to allow them to use {@link Ext.Base#callParent}.
556 *
557 * var panel = new Ext.Panel({ ... });
558 *
559 * Ext.override(panel, {
560 * initComponent: function () {
561 * // extra processing...
562 *
563 * this.callParent();
564 * }
565 * });
566 *
567 * If the `target` is none of these, the `overrides` are applied to the `target`
568 * using {@link Ext#apply Ext.apply}.
569 *
570 * Please refer to {@link Ext#define Ext.define} and {@link Ext.Base#override} for
571 * further details.
572 *
573 * @param {Object} target The target to override.
574 * @param {Object} overrides The properties to add or replace on `target`.
575 * @method override
576 */
577 override: function (target, overrides) {
578 if (target.$isClass) {
579 target.override(overrides);
580 } else if (typeof target === 'function') {
581 Ext.apply(target.prototype, overrides);
582 } else {
583 var owner = target.self,
584 privates;
585
586 if (owner && owner.$isClass) { // if (instance of Ext.define'd class)
587 privates = overrides.privates;
588 if (privates) {
589 overrides = Ext.apply({}, overrides);
590 delete overrides.privates;
591 addInstanceOverrides(target, owner, privates);
592 }
593
594 addInstanceOverrides(target, owner, overrides);
595 } else {
596 Ext.apply(target, overrides);
597 }
598 }
599
600 return target;
601 },
602
603 /**
604 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
605 * value (second argument) otherwise.
606 *
607 * @param {Object} value The value to test.
608 * @param {Object} defaultValue The value to return if the original value is empty.
609 * @param {Boolean} [allowBlank=false] `true` to allow zero length strings to qualify as non-empty.
610 * @return {Object} value, if non-empty, else defaultValue.
611 */
612 valueFrom: function(value, defaultValue, allowBlank){
613 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
614 },
615
616 /**
617 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
618 *
619 * - `null`
620 * - `undefined`
621 * - a zero-length array
622 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
623 *
624 * @param {Object} value The value to test.
625 * @param {Boolean} [allowEmptyString=false] `true` to allow empty strings.
626 * @return {Boolean}
627 */
628 isEmpty: function(value, allowEmptyString) {
629 return (value == null) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
630 },
631
632 /**
633 * Returns `true` if the passed value is a JavaScript Array, `false` otherwise.
634 *
635 * @param {Object} target The target to test.
636 * @return {Boolean}
637 * @method
638 */
639 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
640 return toString.call(value) === '[object Array]';
641 },
642
643 /**
644 * Returns `true` if the passed value is a JavaScript Date object, `false` otherwise.
645 * @param {Object} object The object to test.
646 * @return {Boolean}
647 */
648 isDate: function(value) {
649 return toString.call(value) === '[object Date]';
650 },
651
652 /**
653 * Returns 'true' if the passed value is a String that matches the MS Date JSON
654 * encoding format.
655 * @param {String} value The string to test.
656 * @return {Boolean}
657 */
658 isMSDate: function(value) {
659 if (!Ext.isString(value)) {
660 return false;
661 }
662 return MSDateRe.test(value);
663 },
664
665 /**
666 * Returns `true` if the passed value is a JavaScript Object, `false` otherwise.
667 * @param {Object} value The value to test.
668 * @return {Boolean}
669 * @method
670 */
671 isObject: (toString.call(null) === '[object Object]') ?
672 function(value) {
673 // check ownerDocument here as well to exclude DOM nodes
674 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
675 } :
676 function(value) {
677 return toString.call(value) === '[object Object]';
678 },
679
680 /**
681 * @private
682 */
683 isSimpleObject: function(value) {
684 return value instanceof Object && value.constructor === Object;
685 },
686
687 /**
688 * Returns `true` if the passed value is a JavaScript 'primitive', a string, number
689 * or boolean.
690 * @param {Object} value The value to test.
691 * @return {Boolean}
692 */
693 isPrimitive: function(value) {
694 var type = typeof value;
695
696 return type === 'string' || type === 'number' || type === 'boolean';
697 },
698
699 /**
700 * Returns `true` if the passed value is a JavaScript Function, `false` otherwise.
701 * @param {Object} value The value to test.
702 * @return {Boolean}
703 * @method
704 */
705 isFunction:
706 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
707 // Object.prototype.toString (slower)
708 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
709 return !!value && toString.call(value) === '[object Function]';
710 } : function(value) {
711 return !!value && typeof value === 'function';
712 },
713
714 /**
715 * Returns `true` if the passed value is a number. Returns `false` for non-finite numbers.
716 * @param {Object} value The value to test.
717 * @return {Boolean}
718 */
719 isNumber: function(value) {
720 return typeof value === 'number' && isFinite(value);
721 },
722
723 /**
724 * Validates that a value is numeric.
725 * @param {Object} value Examples: 1, '1', '2.34'
726 * @return {Boolean} True if numeric, false otherwise
727 */
728 isNumeric: function(value) {
729 return !isNaN(parseFloat(value)) && isFinite(value);
730 },
731
732 /**
733 * Returns `true `if the passed value is a string.
734 * @param {Object} value The value to test.
735 * @return {Boolean}
736 */
737 isString: function(value) {
738 return typeof value === 'string';
739 },
740
741 /**
742 * Returns `true` if the passed value is a boolean.
743 *
744 * @param {Object} value The value to test.
745 * @return {Boolean}
746 */
747 isBoolean: function(value) {
748 return typeof value === 'boolean';
749 },
750
751 /**
752 * Returns `true` if the passed value is an HTMLElement
753 * @param {Object} value The value to test.
754 * @return {Boolean}
755 */
756 isElement: function(value) {
757 return value ? value.nodeType === 1 : false;
758 },
759
760 /**
761 * Returns `true` if the passed value is a TextNode
762 * @param {Object} value The value to test.
763 * @return {Boolean}
764 */
765 isTextNode: function(value) {
766 return value ? value.nodeName === "#text" : false;
767 },
768
769 /**
770 * Returns `true` if the passed value is defined.
771 * @param {Object} value The value to test.
772 * @return {Boolean}
773 */
774 isDefined: function(value) {
775 return typeof value !== 'undefined';
776 },
777
778 /**
779 * Returns `true` if the passed value is iterable, that is, if elements of it are addressable using array
780 * notation with numeric indices, `false` otherwise.
781 *
782 * Arrays and function `arguments` objects are iterable. Also HTML collections such as `NodeList` and `HTMLCollection'
783 * are iterable.
784 *
785 * @param {Object} value The value to test
786 * @return {Boolean}
787 */
788 isIterable: function(value) {
789 // To be iterable, the object must have a numeric length property and must not be a string or function.
790 if (!value || typeof value.length !== 'number' || typeof value === 'string' || Ext.isFunction(value)) {
791 return false;
792 }
793
794 // Certain "standard" collections in IE (such as document.images) do not offer the correct
795 // Javascript Object interface; specifically, they lack the propertyIsEnumerable method.
796 // And the item property while it does exist is not typeof "function"
797 if (!value.propertyIsEnumerable) {
798 return !!value.item;
799 }
800
801 // If it is a regular, interrogatable JS object (not an IE ActiveX object), then...
802 // If it has its own property called "length", but not enumerable, it's iterable
803 if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) {
804 return true;
805 }
806
807 // Test against whitelist which includes known iterable collection types
808 return iterableRe.test(toString.call(value));
809 },
810
811 /**
812 * This method returns `true` if debug is enabled for the specified class. This is
813 * done by checking the `Ext.debugConfig.hooks` config for the closest match to the
814 * given `className`.
815 * @param {String} className The name of the class.
816 * @return {Boolean} `true` if debug is enabled for the specified class.
817 */
818 isDebugEnabled:
819 //<debug>
820 function (className, defaultEnabled) {
821 var debugConfig = Ext.debugConfig.hooks;
822
823 if (debugConfig.hasOwnProperty(className)) {
824 return debugConfig[className];
825 }
826
827 var enabled = debugConfig['*'],
828 prefixLength = 0;
829
830 if (defaultEnabled !== undefined) {
831 enabled = defaultEnabled;
832 }
833 if (!className) {
834 return enabled;
835 }
836
837 for (var prefix in debugConfig) {
838 var value = debugConfig[prefix];
839
840 // if prefix=='Ext' match 'Ext.foo.Bar' but not 'Ext4.foo.Bar'
841 if (className.charAt(prefix.length) === '.') {
842 if (className.substring(0, prefix.length) === prefix) {
843 if (prefixLength < prefix.length) {
844 prefixLength = prefix.length;
845 enabled = value;
846 }
847 }
848 }
849 }
850
851 return enabled;
852 } ||
853 //</debug>
854 emptyFn,
855
856 /**
857 * Clone simple variables including array, {}-like objects, DOM nodes and Date without keeping the old reference.
858 * A reference for the object itself is returned if it's not a direct descendant of Object. For model cloning,
859 * see {@link Ext.data.Model#copy Model.copy}.
860 *
861 * @param {Object} item The variable to clone
862 * @return {Object} clone
863 */
864 clone: function(item) {
865 if (item === null || item === undefined) {
866 return item;
867 }
868
869 // DOM nodes
870 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
871 // recursively
872 if (item.nodeType && item.cloneNode) {
873 return item.cloneNode(true);
874 }
875
876 var type = toString.call(item),
877 i, j, k, clone, key;
878
879 // Date
880 if (type === '[object Date]') {
881 return new Date(item.getTime());
882 }
883
884 // Array
885 if (type === '[object Array]') {
886 i = item.length;
887
888 clone = [];
889
890 while (i--) {
891 clone[i] = Ext.clone(item[i]);
892 }
893 }
894 // Object
895 else if (type === '[object Object]' && item.constructor === Object) {
896 clone = {};
897
898 for (key in item) {
899 clone[key] = Ext.clone(item[key]);
900 }
901
902 if (enumerables) {
903 for (j = enumerables.length; j--;) {
904 k = enumerables[j];
905 if (item.hasOwnProperty(k)) {
906 clone[k] = item[k];
907 }
908 }
909 }
910 }
911
912 return clone || item;
913 },
914
915 /**
916 * @private
917 * Generate a unique reference of Ext in the global scope, useful for sandboxing
918 */
919 getUniqueGlobalNamespace: function() {
920 var uniqueGlobalNamespace = this.uniqueGlobalNamespace,
921 i;
922
923 if (uniqueGlobalNamespace === undefined) {
924 i = 0;
925
926 do {
927 uniqueGlobalNamespace = 'ExtBox' + (++i);
928 } while (global[uniqueGlobalNamespace] !== undefined);
929
930 global[uniqueGlobalNamespace] = Ext;
931 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
932 }
933
934 return uniqueGlobalNamespace;
935 },
936
937 /**
938 * @private
939 */
940 functionFactoryCache: {},
941
942 cacheableFunctionFactory: function() {
943 var me = this,
944 args = Array.prototype.slice.call(arguments),
945 cache = me.functionFactoryCache,
946 idx, fn, ln;
947
948 if (Ext.isSandboxed) {
949 ln = args.length;
950 if (ln > 0) {
951 ln--;
952 args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
953 }
954 }
955 idx = args.join('');
956 fn = cache[idx];
957 if (!fn) {
958 fn = Function.prototype.constructor.apply(Function.prototype, args);
959
960 cache[idx] = fn;
961 }
962 return fn;
963 },
964
965 functionFactory: function() {
966 var args = Array.prototype.slice.call(arguments),
967 ln;
968
969 if (Ext.isSandboxed) {
970 ln = args.length;
971 if (ln > 0) {
972 ln--;
973 args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
974 }
975 }
976
977 return Function.prototype.constructor.apply(Function.prototype, args);
978 },
979
980 /**
981 * @private
982 */
983 Logger: {
984 //<feature logger>
985 log: function(message, priority) {
986 if (message && global.console) {
987 if (!priority || !(priority in global.console)) {
988 priority = 'log';
989 }
990 message = '[' + priority.toUpperCase() + '] ' + message;
991 global.console[priority](message);
992 }
993 },
994 verbose: function(message) {
995 this.log(message, 'verbose');
996 },
997 info: function(message) {
998 this.log(message, 'info');
999 },
1000 warn: function(message) {
1001 this.log(message, 'warn');
1002 },
1003 error: function(message) {
1004 throw new Error(message);
1005 },
1006 deprecate: function(message) {
1007 this.log(message, 'warn');
1008 }
1009 } || {
1010 //</feature>
1011 verbose: emptyFn,
1012 log: emptyFn,
1013 info: emptyFn,
1014 warn: emptyFn,
1015 error: function(message) {
1016 throw new Error(message);
1017 },
1018 deprecate: emptyFn
1019 },
1020
1021 /**
1022 * @private
1023 */
1024 getElementById: function(id) {
1025 return document.getElementById(id);
1026 },
1027
1028 /**
1029 * @member Ext
1030 * @private
1031 */
1032 splitAndUnescape: (function() {
1033 var cache = {};
1034
1035 return function(origin, delimiter) {
1036 if (!origin) {
1037 return [];
1038 }
1039 else if (!delimiter) {
1040 return [origin];
1041 }
1042
1043 var replaceRe = cache[delimiter] || (cache[delimiter] = new RegExp('\\\\' + delimiter, 'g')),
1044 result = [],
1045 parts, part;
1046
1047 parts = origin.split(delimiter);
1048
1049 while ((part = parts.shift()) !== undefined) {
1050 // If any of the parts ends with the delimiter that means
1051 // the delimiter was escaped and the split was invalid. Roll back.
1052 while (part.charAt(part.length - 1) === '\\' && parts.length > 0) {
1053 part = part + delimiter + parts.shift();
1054 }
1055
1056 // Now that we have split the parts, unescape the delimiter char
1057 part = part.replace(replaceRe, delimiter);
1058
1059 result.push(part);
1060 }
1061
1062 return result;
1063 };
1064 })()
1065 }); // Ext.apply(Ext
1066
1067 Ext.returnTrue.$nullFn = Ext.returnId.$nullFn = true;
1068 }());