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