/ext-4.1.0_b3/src/core/src/Ext.js
JavaScript | 656 lines | 340 code | 87 blank | 229 comment | 79 complexity | c0bafa206e26aa97c2ea39efa3048a99 MD5 | raw file
1/**
2 * @class Ext
3 * @singleton
4 */
5var Ext = Ext || {};
6Ext._startTime = new Date().getTime();
7(function() {
8 var global = this,
9 objectPrototype = Object.prototype,
10 toString = objectPrototype.toString,
11 enumerables = true,
12 enumerablesTest = { toString: 1 },
13 emptyFn = function(){},
14 i;
15
16 Ext.global = global;
17
18 for (i in enumerablesTest) {
19 enumerables = null;
20 }
21
22 if (enumerables) {
23 enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
24 'toLocaleString', 'toString', 'constructor'];
25 }
26
27 /**
28 * An array containing extra enumerables for old browsers
29 * @property {String[]}
30 */
31 Ext.enumerables = enumerables;
32
33 /**
34 * Copies all the properties of config to the specified object.
35 * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
36 * {@link Ext.Object#merge} instead.
37 * @param {Object} object The receiver of the properties
38 * @param {Object} config The source of the properties
39 * @param {Object} defaults A different object that will also be applied for default values
40 * @return {Object} returns obj
41 */
42 Ext.apply = function(object, config, defaults) {
43 if (defaults) {
44 Ext.apply(object, defaults);
45 }
46
47 if (object && config && typeof config === 'object') {
48 var i, j, k;
49
50 for (i in config) {
51 object[i] = config[i];
52 }
53
54 if (enumerables) {
55 for (j = enumerables.length; j--;) {
56 k = enumerables[j];
57 if (config.hasOwnProperty(k)) {
58 object[k] = config[k];
59 }
60 }
61 }
62 }
63
64 return object;
65 };
66
67 Ext.buildSettings = Ext.apply({
68 baseCSSPrefix: 'x-',
69 scopeResetCSS: false
70 }, Ext.buildSettings || {});
71
72 Ext.apply(Ext, {
73
74 /**
75 * @property {String} [name='Ext']
76 * <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>
77 * <p>This is usually <code>"Ext"</code>, but if a sandboxed build of ExtJS is being used, this will be an alternative name.</p>
78 * <p>If code is being generated for use by <code>eval</code> or to create a <code>new Function</code>, and the global instance
79 * of Ext must be referenced, this is the name that should be built into the code.</p>
80 */
81 name: Ext.sandboxName || 'Ext',
82
83 /**
84 * A reusable empty function
85 */
86 emptyFn: emptyFn,
87
88 /**
89 * A zero length string which will pass a truth test. Useful for passing to methods
90 * which use a truth test to reject <i>falsy</i> values where a string value must be cleared.
91 */
92 emptyString: new String(),
93
94 baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
95
96 /**
97 * Copies all the properties of config to object if they don't already exist.
98 * @param {Object} object The receiver of the properties
99 * @param {Object} config The source of the properties
100 * @return {Object} returns obj
101 */
102 applyIf: function(object, config) {
103 var property;
104
105 if (object) {
106 for (property in config) {
107 if (object[property] === undefined) {
108 object[property] = config[property];
109 }
110 }
111 }
112
113 return object;
114 },
115
116 /**
117 * Iterates either an array or an object. This method delegates to
118 * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
119 *
120 * @param {Object/Array} object The object or array to be iterated.
121 * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
122 * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
123 * type that is being iterated.
124 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
125 * Defaults to the object being iterated itself.
126 * @markdown
127 */
128 iterate: function(object, fn, scope) {
129 if (Ext.isEmpty(object)) {
130 return;
131 }
132
133 if (scope === undefined) {
134 scope = object;
135 }
136
137 if (Ext.isIterable(object)) {
138 Ext.Array.each.call(Ext.Array, object, fn, scope);
139 }
140 else {
141 Ext.Object.each.call(Ext.Object, object, fn, scope);
142 }
143 }
144 });
145
146 Ext.apply(Ext, {
147
148 /**
149 * This method deprecated. Use {@link Ext#define Ext.define} instead.
150 * @method
151 * @param {Function} superclass
152 * @param {Object} overrides
153 * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
154 * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
155 */
156 extend: function() {
157 // inline overrides
158 var objectConstructor = objectPrototype.constructor,
159 inlineOverrides = function(o) {
160 for (var m in o) {
161 if (!o.hasOwnProperty(m)) {
162 continue;
163 }
164 this[m] = o[m];
165 }
166 };
167
168 return function(subclass, superclass, overrides) {
169 // First we check if the user passed in just the superClass with overrides
170 if (Ext.isObject(superclass)) {
171 overrides = superclass;
172 superclass = subclass;
173 subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
174 superclass.apply(this, arguments);
175 };
176 }
177
178 //<debug>
179 if (!superclass) {
180 Ext.Error.raise({
181 sourceClass: 'Ext',
182 sourceMethod: 'extend',
183 msg: 'Attempting to extend from a class which has not been loaded on the page.'
184 });
185 }
186 //</debug>
187
188 // We create a new temporary class
189 var F = function() {},
190 subclassProto, superclassProto = superclass.prototype;
191
192 F.prototype = superclassProto;
193 subclassProto = subclass.prototype = new F();
194 subclassProto.constructor = subclass;
195 subclass.superclass = superclassProto;
196
197 if (superclassProto.constructor === objectConstructor) {
198 superclassProto.constructor = superclass;
199 }
200
201 subclass.override = function(overrides) {
202 Ext.override(subclass, overrides);
203 };
204
205 subclassProto.override = inlineOverrides;
206 subclassProto.proto = subclassProto;
207
208 subclass.override(overrides);
209 subclass.extend = function(o) {
210 return Ext.extend(subclass, o);
211 };
212
213 return subclass;
214 };
215 }(),
216
217 /**
218 * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
219 *
220 * @param {Object} cls The class to override
221 * @param {Object} overrides The properties to add to origClass. This should be specified as an object literal
222 * containing one or more properties.
223 * @method override
224 * @markdown
225 * @deprecated 4.1.0 Use {@link Ext#define Ext.define} instead
226 */
227 override: function(cls, overrides) {
228 if (cls.$isClass) {
229 return cls.override(overrides);
230 }
231 else {
232 Ext.apply(cls.prototype, overrides);
233 }
234 }
235 });
236
237 // A full set of static methods to do type checking
238 Ext.apply(Ext, {
239
240 /**
241 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
242 * value (second argument) otherwise.
243 *
244 * @param {Object} value The value to test
245 * @param {Object} defaultValue The value to return if the original value is empty
246 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
247 * @return {Object} value, if non-empty, else defaultValue
248 */
249 valueFrom: function(value, defaultValue, allowBlank){
250 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
251 },
252
253 /**
254 * Returns the type of the given variable in string format. List of possible values are:
255 *
256 * - `undefined`: If the given value is `undefined`
257 * - `null`: If the given value is `null`
258 * - `string`: If the given value is a string
259 * - `number`: If the given value is a number
260 * - `boolean`: If the given value is a boolean value
261 * - `date`: If the given value is a `Date` object
262 * - `function`: If the given value is a function reference
263 * - `object`: If the given value is an object
264 * - `array`: If the given value is an array
265 * - `regexp`: If the given value is a regular expression
266 * - `element`: If the given value is a DOM Element
267 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
268 * - `whitespace`: If the given value is a DOM text node and contains only whitespace
269 *
270 * @param {Object} value
271 * @return {String}
272 * @markdown
273 */
274 typeOf: function(value) {
275 if (value === null) {
276 return 'null';
277 }
278
279 var type = typeof value;
280
281 if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
282 return type;
283 }
284
285 var typeToString = toString.call(value);
286
287 switch(typeToString) {
288 case '[object Array]':
289 return 'array';
290 case '[object Date]':
291 return 'date';
292 case '[object Boolean]':
293 return 'boolean';
294 case '[object Number]':
295 return 'number';
296 case '[object RegExp]':
297 return 'regexp';
298 }
299
300 if (type === 'function') {
301 return 'function';
302 }
303
304 if (type === 'object') {
305 if (value.nodeType !== undefined) {
306 if (value.nodeType === 3) {
307 return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
308 }
309 else {
310 return 'element';
311 }
312 }
313
314 return 'object';
315 }
316
317 //<debug error>
318 Ext.Error.raise({
319 sourceClass: 'Ext',
320 sourceMethod: 'typeOf',
321 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
322 });
323 //</debug>
324 },
325
326 /**
327 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
328 *
329 * - `null`
330 * - `undefined`
331 * - a zero-length array
332 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
333 *
334 * @param {Object} value The value to test
335 * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
336 * @return {Boolean}
337 * @markdown
338 */
339 isEmpty: function(value, allowEmptyString) {
340 return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
341 },
342
343 /**
344 * Returns true if the passed value is a JavaScript Array, false otherwise.
345 *
346 * @param {Object} target The target to test
347 * @return {Boolean}
348 * @method
349 */
350 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
351 return toString.call(value) === '[object Array]';
352 },
353
354 /**
355 * Returns true if the passed value is a JavaScript Date object, false otherwise.
356 * @param {Object} object The object to test
357 * @return {Boolean}
358 */
359 isDate: function(value) {
360 return toString.call(value) === '[object Date]';
361 },
362
363 /**
364 * Returns true if the passed value is a JavaScript Object, false otherwise.
365 * @param {Object} value The value to test
366 * @return {Boolean}
367 * @method
368 */
369 isObject: (toString.call(null) === '[object Object]') ?
370 function(value) {
371 // check ownerDocument here as well to exclude DOM nodes
372 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
373 } :
374 function(value) {
375 return toString.call(value) === '[object Object]';
376 },
377
378 /**
379 * @private
380 */
381 isSimpleObject: function(value) {
382 return value instanceof Object && value.constructor === Object;
383 },
384 /**
385 * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
386 * @param {Object} value The value to test
387 * @return {Boolean}
388 */
389 isPrimitive: function(value) {
390 var type = typeof value;
391
392 return type === 'string' || type === 'number' || type === 'boolean';
393 },
394
395 /**
396 * Returns true if the passed value is a JavaScript Function, false otherwise.
397 * @param {Object} value The value to test
398 * @return {Boolean}
399 * @method
400 */
401 isFunction:
402 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
403 // Object.prototype.toString (slower)
404 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
405 return toString.call(value) === '[object Function]';
406 } : function(value) {
407 return typeof value === 'function';
408 },
409
410 /**
411 * Returns true if the passed value is a number. Returns false for non-finite numbers.
412 * @param {Object} value The value to test
413 * @return {Boolean}
414 */
415 isNumber: function(value) {
416 return typeof value === 'number' && isFinite(value);
417 },
418
419 /**
420 * Validates that a value is numeric.
421 * @param {Object} value Examples: 1, '1', '2.34'
422 * @return {Boolean} True if numeric, false otherwise
423 */
424 isNumeric: function(value) {
425 return !isNaN(parseFloat(value)) && isFinite(value);
426 },
427
428 /**
429 * Returns true if the passed value is a string.
430 * @param {Object} value The value to test
431 * @return {Boolean}
432 */
433 isString: function(value) {
434 return typeof value === 'string';
435 },
436
437 /**
438 * Returns true if the passed value is a boolean.
439 *
440 * @param {Object} value The value to test
441 * @return {Boolean}
442 */
443 isBoolean: function(value) {
444 return typeof value === 'boolean';
445 },
446
447 /**
448 * Returns true if the passed value is an HTMLElement
449 * @param {Object} value The value to test
450 * @return {Boolean}
451 */
452 isElement: function(value) {
453 return value ? value.nodeType === 1 : false;
454 },
455
456 /**
457 * Returns true if the passed value is a TextNode
458 * @param {Object} value The value to test
459 * @return {Boolean}
460 */
461 isTextNode: function(value) {
462 return value ? value.nodeName === "#text" : false;
463 },
464
465 /**
466 * Returns true if the passed value is defined.
467 * @param {Object} value The value to test
468 * @return {Boolean}
469 */
470 isDefined: function(value) {
471 return typeof value !== 'undefined';
472 },
473
474 /**
475 * Returns true if the passed value is iterable, false otherwise
476 * @param {Object} value The value to test
477 * @return {Boolean}
478 */
479 isIterable: function(value) {
480 var type = typeof value,
481 checkLength = false;
482 if (value && type != 'string') {
483 // Functions have a length property, so we need to filter them out
484 if (type == 'function') {
485 // In Safari, NodeList/HTMLCollection both return "function" when using typeof, so we need
486 // to explicitly check them here.
487 if (Ext.isSafari) {
488 checkLength = value instanceof NodeList || value instanceof HTMLCollection;
489 }
490 } else {
491 checkLength = true;
492 }
493 }
494 return checkLength ? value.length !== undefined : false;
495 }
496 });
497
498 Ext.apply(Ext, {
499
500 /**
501 * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
502 * @param {Object} item The variable to clone
503 * @return {Object} clone
504 */
505 clone: function(item) {
506 if (item === null || item === undefined) {
507 return item;
508 }
509
510 // DOM nodes
511 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
512 // recursively
513 if (item.nodeType && item.cloneNode) {
514 return item.cloneNode(true);
515 }
516
517 var type = toString.call(item);
518
519 // Date
520 if (type === '[object Date]') {
521 return new Date(item.getTime());
522 }
523
524 var i, j, k, clone, key;
525
526 // Array
527 if (type === '[object Array]') {
528 i = item.length;
529
530 clone = [];
531
532 while (i--) {
533 clone[i] = Ext.clone(item[i]);
534 }
535 }
536 // Object
537 else if (type === '[object Object]' && item.constructor === Object) {
538 clone = {};
539
540 for (key in item) {
541 clone[key] = Ext.clone(item[key]);
542 }
543
544 if (enumerables) {
545 for (j = enumerables.length; j--;) {
546 k = enumerables[j];
547 clone[k] = item[k];
548 }
549 }
550 }
551
552 return clone || item;
553 },
554
555 /**
556 * @private
557 * Generate a unique reference of Ext in the global scope, useful for sandboxing
558 */
559 getUniqueGlobalNamespace: function() {
560 var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
561
562 if (uniqueGlobalNamespace === undefined) {
563 var i = 0;
564
565 do {
566 uniqueGlobalNamespace = 'ExtBox' + (++i);
567 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
568
569 Ext.global[uniqueGlobalNamespace] = Ext;
570 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
571 }
572
573 return uniqueGlobalNamespace;
574 },
575
576 /**
577 * @private
578 */
579 functionFactoryCache: {},
580
581 cacheableFunctionFactory: function() {
582 var me = this,
583 args = Array.prototype.slice.call(arguments),
584 cache = me.functionFactoryCache,
585 idx, fn, ln;
586
587 if (Ext.isSandboxed) {
588 ln = args.length;
589 if (ln > 0) {
590 ln--;
591 args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
592 }
593 }
594 idx = args.join('');
595 fn = cache[idx];
596 if (!fn) {
597 fn = Function.prototype.constructor.apply(Function.prototype, args);
598
599 cache[idx] = fn;
600 }
601 return fn;
602 },
603
604 functionFactory: function() {
605 var me = this,
606 args = Array.prototype.slice.call(arguments),
607 ln;
608
609 if (Ext.isSandboxed) {
610 ln = args.length;
611 if (ln > 0) {
612 ln--;
613 args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
614 }
615 }
616
617 return Function.prototype.constructor.apply(Function.prototype, args);
618 },
619
620 /**
621 * @property
622 * @private
623 */
624 globalEval: ('execScript' in global) ? function(code) {
625 global.execScript(code)
626 } : function(code) {
627 (function(){
628 eval(code);
629 })();
630 },
631
632 /**
633 * @private
634 * @property
635 */
636 Logger: {
637 verbose: emptyFn,
638 log: emptyFn,
639 info: emptyFn,
640 warn: emptyFn,
641 error: function(message) {
642 throw new Error(message);
643 },
644 deprecate: emptyFn
645 }
646 });
647
648 /**
649 * Old alias to {@link Ext#typeOf}
650 * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
651 * @method
652 * @inheritdoc Ext#typeOf
653 */
654 Ext.type = Ext.typeOf;
655
656})();