]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/Util.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / Util.js
CommitLineData
6527f429
DM
1/*\r
2 * This file contains miscellaneous utility methods that depends on various helper classes\r
3 * like `Ext.Array` and `Ext.Date`. Historically these methods were defined in Ext.js or\r
4 * Ext-more.js but that creates circular dependencies so they were consolidated here.\r
5 */\r
6Ext.apply(Ext, {\r
7// @define Ext.Util\r
8// @require Ext\r
9// @require Ext.lang.*\r
10\r
11 // shortcut for the special named scopes for listener scope resolution\r
12 _namedScopes: {\r
13 'this': { isThis: 1 },\r
14 controller: { isController: 1 },\r
15 // these two are private, used to indicate that listeners were declared on the\r
16 // class body with either an unspecified scope, or scope:'controller'\r
17 self: { isSelf: 1 },\r
18 'self.controller': { isSelf: 1, isController: 1 }\r
19 },\r
20\r
21 escapeId: (function(){\r
22 var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i,\r
23 escapeRx = /([\W]{1})/g,\r
24 leadingNumRx = /^(\d)/g,\r
25 escapeFn = function(match, capture){\r
26 return "\\" + capture;\r
27 },\r
28 numEscapeFn = function(match, capture){\r
29 return '\\00' + capture.charCodeAt(0).toString(16) + ' ';\r
30 };\r
31\r
32 return function(id) {\r
33 return validIdRe.test(id) ? id :\r
34 // replace the number portion last to keep the trailing ' '\r
35 // from being escaped\r
36 id.replace(escapeRx, escapeFn).replace(leadingNumRx, numEscapeFn);\r
37 };\r
38 }()),\r
39\r
40 /**\r
41 * @method callback\r
42 * @member Ext\r
43 * Execute a callback function in a particular scope. If `callback` argument is a\r
44 * function reference, that is called. If it is a string, the string is assumed to\r
45 * be the name of a method on the given `scope`. If no function is passed the call\r
46 * is ignored.\r
47 *\r
48 * For example, these calls are equivalent:\r
49 *\r
50 * var myFunc = this.myFunc;\r
51 *\r
52 * Ext.callback('myFunc', this, [arg1, arg2]);\r
53 * Ext.callback(myFunc, this, [arg1, arg2]);\r
54 *\r
55 * Ext.isFunction(myFunc) && this.myFunc(arg1, arg2);\r
56 *\r
57 * @param {Function/String} callback The callback function to execute or the name of\r
58 * the callback method on the provided `scope`.\r
59 * @param {Object} [scope] The scope in which `callback` should be invoked. If `callback`\r
60 * is a string this object provides the method by that name. If this is `null` then\r
61 * the `caller` is used to resolve the scope to a `ViewController` or the proper\r
62 * `defaultListenerScope`.\r
63 * @param {Array} [args] The arguments to pass to the function.\r
64 * @param {Number} [delay] Pass a number to delay the call by a number of milliseconds.\r
65 * @param {Object} [caller] The object calling the callback. This is used to resolve\r
66 * named methods when no explicit `scope` is provided.\r
67 * @param {Object} [defaultScope=caller] The default scope to return if none is found.\r
68 * @return The value returned by the callback or `undefined` (if there is a `delay`\r
69 * or if the `callback` is not a function).\r
70 */\r
71 callback: function (callback, scope, args, delay, caller, defaultScope) {\r
72 if (!callback) {\r
73 return;\r
74 }\r
75\r
76 var namedScope = (scope in Ext._namedScopes);\r
77 \r
78 if (callback.charAt) { // if (isString(fn))\r
79 if ((!scope || namedScope) && caller) {\r
80 scope = caller.resolveListenerScope(namedScope ? scope : defaultScope);\r
81 }\r
82 //<debug>\r
83 if (!scope || !Ext.isObject(scope)) {\r
84 Ext.raise('Named method "' + callback + '" requires a scope object');\r
85 }\r
86 if (!Ext.isFunction(scope[callback])) {\r
87 Ext.raise('No method named "' + callback + '" on ' +\r
88 (scope.$className || 'scope object'));\r
89 }\r
90 //</debug>\r
91\r
92 callback = scope[callback];\r
93 } else if (namedScope) {\r
94 scope = defaultScope || caller;\r
95 } else if (!scope) {\r
96 scope = caller;\r
97 }\r
98 \r
99 var ret;\r
100\r
101 if (callback && Ext.isFunction(callback)) {\r
102 scope = scope || Ext.global;\r
103 if (delay) {\r
104 Ext.defer(callback, delay, scope, args);\r
105 } else if (Ext.elevateFunction) {\r
106 ret = Ext.elevateFunction(callback, scope, args);\r
107 } else if (args) {\r
108 ret = callback.apply(scope, args);\r
109 } else {\r
110 ret = callback.call(scope);\r
111 }\r
112 }\r
113\r
114 return ret;\r
115 },\r
116\r
117 /**\r
118 * @method coerce\r
119 * @member Ext\r
120 * Coerces the first value if possible so that it is comparable to the second value.\r
121 *\r
122 * Coercion only works between the basic atomic data types String, Boolean, Number, Date, null and undefined.\r
123 *\r
124 * Numbers and numeric strings are coerced to Dates using the value as the millisecond era value.\r
125 *\r
126 * Strings are coerced to Dates by parsing using the {@link Ext.Date#defaultFormat defaultFormat}.\r
127 * \r
128 * For example\r
129 *\r
130 * Ext.coerce('false', true);\r
131 * \r
132 * returns the boolean value `false` because the second parameter is of type `Boolean`.\r
133 * \r
134 * @param {Mixed} from The value to coerce\r
135 * @param {Mixed} to The value it must be compared against\r
136 * @return The coerced value.\r
137 */\r
138 coerce: function(from, to) {\r
139 var fromType = Ext.typeOf(from),\r
140 toType = Ext.typeOf(to),\r
141 isString = typeof from === 'string';\r
142\r
143 if (fromType !== toType) {\r
144 switch (toType) {\r
145 case 'string':\r
146 return String(from);\r
147 case 'number':\r
148 return Number(from);\r
149 case 'boolean':\r
150 return isString && (!from || from === 'false') ? false : Boolean(from);\r
151 case 'null':\r
152 return isString && (!from || from === 'null') ? null : from;\r
153 case 'undefined':\r
154 return isString && (!from || from === 'undefined') ? undefined : from;\r
155 case 'date':\r
156 return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from));\r
157 }\r
158 }\r
159 return from;\r
160 },\r
161\r
162 /**\r
163 * @method copyTo\r
164 * @member Ext\r
165 * Copies a set of named properties fom the source object to the destination object.\r
166 *\r
167 * Example:\r
168 *\r
169 * var foo = { a: 1, b: 2, c: 3 };\r
170 *\r
171 * var bar = Ext.copyTo({}, foo, 'a,c');\r
172 * // bar = { a: 1, c: 3 };\r
173 *\r
174 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.\r
175 *\r
176 * @param {Object} dest The destination object.\r
177 * @param {Object} source The source object.\r
178 * @param {String/String[]} names Either an Array of property names, or a comma-delimited list\r
179 * of property names to copy.\r
180 * @param {Boolean} [usePrototypeKeys=false] Pass `true` to copy keys off of the\r
181 * prototype as well as the instance.\r
182 * @return {Object} The `dest` object.\r
183 * @deprecated 6.0.1 Use {@link Ext#copy Ext.copy} instead. This old method\r
184 * would copy the named preoperties even if they did not exist in the source which\r
185 * could produce `undefined` values in the destination.\r
186 */\r
187 copyTo: function (dest, source, names, usePrototypeKeys) {\r
188 if (typeof names === 'string') {\r
189 names = names.split(Ext.propertyNameSplitRe);\r
190 }\r
191\r
192 for (var name, i = 0, n = names ? names.length : 0; i < n; i++) {\r
193 name = names[i];\r
194\r
195 if (usePrototypeKeys || source.hasOwnProperty(name)) {\r
196 dest[name] = source[name];\r
197 }\r
198 }\r
199\r
200 return dest;\r
201 },\r
202 /**\r
203 * @method copy\r
204 * @member Ext\r
205 * Copies a set of named properties fom the source object to the destination object.\r
206 *\r
207 * Example:\r
208 *\r
209 * var foo = { a: 1, b: 2, c: 3 };\r
210 *\r
211 * var bar = Ext.copy({}, foo, 'a,c');\r
212 * // bar = { a: 1, c: 3 };\r
213 *\r
214 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.\r
215 *\r
216 * @param {Object} dest The destination object.\r
217 * @param {Object} source The source object.\r
218 * @param {String/String[]} names Either an Array of property names, or a comma-delimited list\r
219 * of property names to copy.\r
220 * @param {Boolean} [usePrototypeKeys=false] Pass `true` to copy keys off of the\r
221 * prototype as well as the instance.\r
222 * @return {Object} The `dest` object.\r
223 */\r
224 copy: function (dest, source, names, usePrototypeKeys) {\r
225 if (typeof names === 'string') {\r
226 names = names.split(Ext.propertyNameSplitRe);\r
227 }\r
228\r
229 for (var name, i = 0, n = names ? names.length : 0; i < n; i++) {\r
230 name = names[i];\r
231\r
232 // Only copy a property if the source actually *has* that property.\r
233 // If we are including prototype properties, then ensure that a property of\r
234 // that name can be found *somewhere* in the prototype chain (otherwise we'd be copying undefined in which may break things)\r
235 if (source.hasOwnProperty(name) || (usePrototypeKeys && name in source)) {\r
236 dest[name] = source[name];\r
237 }\r
238 }\r
239\r
240 return dest;\r
241 },\r
242\r
243 propertyNameSplitRe: /[,;\s]+/,\r
244\r
245 /**\r
246 * @method copyToIf\r
247 * @member Ext\r
248 * Copies a set of named properties fom the source object to the destination object\r
249 * if the destination object does not already have them.\r
250 *\r
251 * Example:\r
252 *\r
253 * var foo = { a: 1, b: 2, c: 3 };\r
254 *\r
255 * var bar = Ext.copyToIf({ a:42 }, foo, 'a,c');\r
256 * // bar = { a: 42, c: 3 };\r
257 *\r
258 * @param {Object} destination The destination object.\r
259 * @param {Object} source The source object.\r
260 * @param {String/String[]} names Either an Array of property names, or a single string\r
261 * with a list of property names separated by ",", ";" or spaces.\r
262 * @return {Object} The `dest` object.\r
263 * @deprecated 6.0.1 Use {@link Ext#copyIf Ext.copyIf} instead. This old method\r
264 * would copy the named preoperties even if they did not exist in the source which\r
265 * could produce `undefined` values in the destination.\r
266 */\r
267 copyToIf: function (destination, source, names) {\r
268 if (typeof names === 'string') {\r
269 names = names.split(Ext.propertyNameSplitRe);\r
270 }\r
271\r
272 for (var name, i = 0, n = names ? names.length : 0; i < n; i++) {\r
273 name = names[i];\r
274\r
275 if (destination[name] === undefined) {\r
276 destination[name] = source[name];\r
277 }\r
278 }\r
279\r
280 return destination;\r
281 },\r
282\r
283 /**\r
284 * @method copyIf\r
285 * @member Ext\r
286 * Copies a set of named properties fom the source object to the destination object\r
287 * if the destination object does not already have them.\r
288 *\r
289 * Example:\r
290 *\r
291 * var foo = { a: 1, b: 2, c: 3 };\r
292 *\r
293 * var bar = Ext.copyIf({ a:42 }, foo, 'a,c');\r
294 * // bar = { a: 42, c: 3 };\r
295 *\r
296 * @param {Object} destination The destination object.\r
297 * @param {Object} source The source object.\r
298 * @param {String/String[]} names Either an Array of property names, or a single string\r
299 * with a list of property names separated by ",", ";" or spaces.\r
300 * @return {Object} The `dest` object.\r
301 */\r
302 copyIf: function (destination, source, names) {\r
303 if (typeof names === 'string') {\r
304 names = names.split(Ext.propertyNameSplitRe);\r
305 }\r
306\r
307 for (var name, i = 0, n = names ? names.length : 0; i < n; i++) {\r
308 name = names[i];\r
309\r
310 // Only copy a property if the destination has no property by that name\r
311 if (!(name in destination) && (name in source)) {\r
312 destination[name] = source[name];\r
313 }\r
314 }\r
315\r
316 return destination;\r
317 },\r
318\r
319 /**\r
320 * @method extend\r
321 * @member Ext\r
322 * This method deprecated. Use {@link Ext#define Ext.define} instead.\r
323 * @param {Function} superclass\r
324 * @param {Object} overrides\r
325 * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.\r
326 * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead\r
327 */\r
328 extend: (function() {\r
329 // inline overrides\r
330 var objectConstructor = Object.prototype.constructor,\r
331 inlineOverrides = function(o) {\r
332 for (var m in o) {\r
333 if (!o.hasOwnProperty(m)) {\r
334 continue;\r
335 }\r
336 this[m] = o[m];\r
337 }\r
338 };\r
339\r
340 return function(subclass, superclass, overrides) {\r
341 // First we check if the user passed in just the superClass with overrides\r
342 if (Ext.isObject(superclass)) {\r
343 overrides = superclass;\r
344 superclass = subclass;\r
345 subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {\r
346 superclass.apply(this, arguments);\r
347 };\r
348 }\r
349\r
350 //<debug>\r
351 if (!superclass) {\r
352 Ext.raise({\r
353 sourceClass: 'Ext',\r
354 sourceMethod: 'extend',\r
355 msg: 'Attempting to extend from a class which has not been loaded on the page.'\r
356 });\r
357 }\r
358 //</debug>\r
359\r
360 // We create a new temporary class\r
361 var F = function() {},\r
362 subclassProto, superclassProto = superclass.prototype;\r
363\r
364 F.prototype = superclassProto;\r
365 subclassProto = subclass.prototype = new F();\r
366 subclassProto.constructor = subclass;\r
367 subclass.superclass = superclassProto;\r
368\r
369 if (superclassProto.constructor === objectConstructor) {\r
370 superclassProto.constructor = superclass;\r
371 }\r
372\r
373 subclass.override = function(overrides) {\r
374 Ext.override(subclass, overrides);\r
375 };\r
376\r
377 subclassProto.override = inlineOverrides;\r
378 subclassProto.proto = subclassProto;\r
379\r
380 subclass.override(overrides);\r
381 subclass.extend = function(o) {\r
382 return Ext.extend(subclass, o);\r
383 };\r
384\r
385 return subclass;\r
386 };\r
387 }()),\r
388\r
389 /**\r
390 * @method iterate\r
391 * @member Ext\r
392 * Iterates either an array or an object. This method delegates to\r
393 * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.\r
394 *\r
395 * @param {Object/Array} object The object or array to be iterated.\r
396 * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and\r
397 * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object\r
398 * type that is being iterated.\r
399 * @param {Object} [scope] The scope (`this` reference) in which the specified function is executed.\r
400 * Defaults to the object being iterated itself.\r
401 */\r
402 iterate: function(object, fn, scope) {\r
403 if (Ext.isEmpty(object)) {\r
404 return;\r
405 }\r
406\r
407 if (scope === undefined) {\r
408 scope = object;\r
409 }\r
410\r
411 if (Ext.isIterable(object)) {\r
412 Ext.Array.each.call(Ext.Array, object, fn, scope);\r
413 }\r
414 else {\r
415 Ext.Object.each.call(Ext.Object, object, fn, scope);\r
416 }\r
417 },\r
418\r
419 _resourcePoolRe: /^[<]([^<>@:]*)(?:[@]([^<>@:]+))?[>](.+)$/,\r
420\r
421 /**\r
422 * Resolves a resource URL that may contain a resource pool identifier token at the\r
423 * front. The tokens are formatted as HTML tags "&lt;poolName@packageName&gt;" followed\r
424 * by a normal relative path. This token is only processed if present at the first\r
425 * character of the given string.\r
426 *\r
427 * These tokens are parsed and the pieces are then passed to the\r
428 * {@link Ext#getResourcePath} method.\r
429 *\r
430 * For example:\r
431 *\r
432 * [{\r
433 * xtype: 'image',\r
434 * src: '<shared>images/foo.png'\r
435 * },{\r
436 * xtype: 'image',\r
437 * src: '<@package>images/foo.png'\r
438 * },{\r
439 * xtype: 'image',\r
440 * src: '<shared@package>images/foo.png'\r
441 * }]\r
442 *\r
443 * In the above example, "shared" is the name of a Sencha Cmd resource pool and\r
444 * "package" is the name of a Sencha Cmd package.\r
445 *\r
446 * @param {String} url The URL that may contain a resource pool token at the front.\r
447 * @return {String}\r
448 * @since 6.0.1\r
449 */\r
450 resolveResource: function (url) {\r
451 var ret = url,\r
452 m;\r
453\r
454 if (url && url.charAt(0) === '<') {\r
455 m = Ext._resourcePoolRe.exec(url);\r
456 if (m) {\r
457 ret = Ext.getResourcePath(m[3], m[1], m[2]);\r
458 }\r
459 }\r
460\r
461 return ret;\r
462 },\r
463\r
464 /**\r
465 *\r
466 * @member Ext\r
467 * @method urlEncode\r
468 * @inheritdoc Ext.Object#toQueryString\r
469 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead\r
470 */\r
471 urlEncode: function () {\r
472 var args = Ext.Array.from(arguments),\r
473 prefix = '';\r
474\r
475 // Support for the old `pre` argument\r
476 if (Ext.isString(args[1])) {\r
477 prefix = args[1] + '&';\r
478 args[1] = false;\r
479 }\r
480\r
481 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);\r
482 },\r
483\r
484 /**\r
485 * Alias for {@link Ext.Object#fromQueryString}.\r
486 *\r
487 * @member Ext\r
488 * @method urlDecode\r
489 * @inheritdoc Ext.Object#fromQueryString\r
490 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead\r
491 */\r
492 urlDecode: function() {\r
493 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);\r
494 },\r
495\r
496 /**\r
497 * @method getScrollbarSize\r
498 * @member Ext\r
499 * Returns the size of the browser scrollbars. This can differ depending on\r
500 * operating system settings, such as the theme or font size.\r
501 * @param {Boolean} [force] true to force a recalculation of the value.\r
502 * @return {Object} An object containing scrollbar sizes.\r
503 * @return {Number} return.width The width of the vertical scrollbar.\r
504 * @return {Number} return.height The height of the horizontal scrollbar.\r
505 */\r
506 getScrollbarSize: function (force) {\r
507 //<debug>\r
508 if (!Ext.isDomReady) {\r
509 Ext.raise("getScrollbarSize called before DomReady");\r
510 }\r
511 //</debug>\r
512\r
513 var scrollbarSize = Ext._scrollbarSize;\r
514\r
515 if (force || !scrollbarSize) {\r
516 var db = document.body,\r
517 div = document.createElement('div');\r
518\r
519 div.style.width = div.style.height = '100px';\r
520 div.style.overflow = 'scroll';\r
521 div.style.position = 'absolute';\r
522\r
523 db.appendChild(div); // now we can measure the div...\r
524\r
525 // at least in iE9 the div is not 100px - the scrollbar size is removed!\r
526 Ext._scrollbarSize = scrollbarSize = {\r
527 width: div.offsetWidth - div.clientWidth,\r
528 height: div.offsetHeight - div.clientHeight\r
529 };\r
530\r
531 db.removeChild(div);\r
532 }\r
533\r
534 return scrollbarSize;\r
535 },\r
536\r
537 /**\r
538 * @method typeOf\r
539 * @member Ext\r
540 * Returns the type of the given variable in string format. List of possible values are:\r
541 *\r
542 * - `undefined`: If the given value is `undefined`\r
543 * - `null`: If the given value is `null`\r
544 * - `string`: If the given value is a string\r
545 * - `number`: If the given value is a number\r
546 * - `boolean`: If the given value is a boolean value\r
547 * - `date`: If the given value is a `Date` object\r
548 * - `function`: If the given value is a function reference\r
549 * - `object`: If the given value is an object\r
550 * - `array`: If the given value is an array\r
551 * - `regexp`: If the given value is a regular expression\r
552 * - `element`: If the given value is a DOM Element\r
553 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace\r
554 * - `whitespace`: If the given value is a DOM text node and contains only whitespace\r
555 *\r
556 * @param {Object} value\r
557 * @return {String}\r
558 */\r
559 typeOf: (function () {\r
560 var nonWhitespaceRe = /\S/,\r
561 toString = Object.prototype.toString,\r
562 typeofTypes = {\r
563 number: 1,\r
564 string: 1,\r
565 'boolean': 1,\r
566 'undefined': 1\r
567 },\r
568 toStringTypes = {\r
569 '[object Array]' : 'array',\r
570 '[object Date]' : 'date',\r
571 '[object Boolean]': 'boolean',\r
572 '[object Number]' : 'number',\r
573 '[object RegExp]' : 'regexp'\r
574 };\r
575\r
576 return function(value) {\r
577 if (value === null) {\r
578 return 'null';\r
579 }\r
580\r
581 var type = typeof value,\r
582 ret, typeToString;\r
583\r
584 if (typeofTypes[type]) {\r
585 return type;\r
586 }\r
587\r
588 ret = toStringTypes[typeToString = toString.call(value)];\r
589 if (ret) {\r
590 return ret;\r
591 }\r
592\r
593 if (type === 'function') {\r
594 return 'function';\r
595 }\r
596\r
597 if (type === 'object') {\r
598 if (value.nodeType !== undefined) {\r
599 if (value.nodeType === 3) {\r
600 return nonWhitespaceRe.test(value.nodeValue) ? 'textnode' : 'whitespace';\r
601 }\r
602 else {\r
603 return 'element';\r
604 }\r
605 }\r
606\r
607 return 'object';\r
608 }\r
609\r
610 //<debug>\r
611 Ext.raise({\r
612 sourceClass: 'Ext',\r
613 sourceMethod: 'typeOf',\r
614 msg: 'Failed to determine the type of "' + value + '".'\r
615 });\r
616 //</debug>\r
617\r
618 return typeToString;\r
619 };\r
620 }()),\r
621\r
622 /**\r
623 * A global factory method to instantiate a class from a config object. For example,\r
624 * these two calls are equivalent:\r
625 *\r
626 * Ext.factory({ text: 'My Button' }, 'Ext.Button');\r
627 * Ext.create('Ext.Button', { text: 'My Button' });\r
628 *\r
629 * If an existing instance is also specified, it will be updated with the supplied config object. This is useful\r
630 * if you need to either create or update an object, depending on if an instance already exists. For example:\r
631 *\r
632 * var button;\r
633 * button = Ext.factory({ text: 'New Button' }, 'Ext.Button', button); // Button created\r
634 * button = Ext.factory({ text: 'Updated Button' }, 'Ext.Button', button); // Button updated\r
635 *\r
636 * @param {Object} config The config object to instantiate or update an instance with.\r
637 * @param {String} [classReference] The class to instantiate from (if there is a default).\r
638 * @param {Object} [instance] The instance to update.\r
639 * @param [aliasNamespace]\r
640 * @member Ext\r
641 */\r
642 factory: function(config, classReference, instance, aliasNamespace) {\r
643 var manager = Ext.ClassManager,\r
644 newInstance;\r
645\r
646 // If config is falsy or a valid instance, destroy the current instance\r
647 // (if it exists) and replace with the new one\r
648 if (!config || config.isInstance) {\r
649 if (instance && instance !== config) {\r
650 instance.destroy();\r
651 }\r
652\r
653 return config;\r
654 }\r
655\r
656 if (aliasNamespace) {\r
657 // If config is a string value, treat it as an alias\r
658 if (typeof config === 'string') {\r
659 return manager.instantiateByAlias(aliasNamespace + '.' + config);\r
660 }\r
661 // Same if 'type' is given in config\r
662 else if (Ext.isObject(config) && 'type' in config) {\r
663 return manager.instantiateByAlias(aliasNamespace + '.' + config.type, config);\r
664 }\r
665 }\r
666\r
667 if (config === true) {\r
668 //<debug>\r
669 if (!instance && !classReference) {\r
670 Ext.raise('[Ext.factory] Cannot determine type of class to create');\r
671 }\r
672 //</debug>\r
673 return instance || Ext.create(classReference);\r
674 }\r
675\r
676 //<debug>\r
677 if (!Ext.isObject(config)) {\r
678 Ext.raise("Invalid config, must be a valid config object");\r
679 }\r
680 //</debug>\r
681\r
682 if ('xtype' in config) {\r
683 newInstance = manager.instantiateByAlias('widget.' + config.xtype, config);\r
684 }\r
685 else if ('xclass' in config) {\r
686 newInstance = Ext.create(config.xclass, config);\r
687 }\r
688\r
689 if (newInstance) {\r
690 if (instance) {\r
691 instance.destroy();\r
692 }\r
693\r
694 return newInstance;\r
695 }\r
696\r
697 if (instance) {\r
698 return instance.setConfig(config);\r
699 }\r
700\r
701 return Ext.create(classReference, config);\r
702 },\r
703\r
704 /**\r
705 * @method log\r
706 * @member Ext\r
707 * Logs a message. If a console is present it will be used. On Opera, the method\r
708 * "opera.postError" is called. In other cases, the message is logged to an array\r
709 * "Ext.log.out". An attached debugger can watch this array and view the log. The\r
710 * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).\r
711 *\r
712 * If additional parameters are passed, they are joined and appended to the message.\r
713 * A technique for tracing entry and exit of a function is this:\r
714 *\r
715 * function foo () {\r
716 * Ext.log({ indent: 1 }, '>> foo');\r
717 *\r
718 * // log statements in here or methods called from here will be indented\r
719 * // by one step\r
720 *\r
721 * Ext.log({ outdent: 1 }, '<< foo');\r
722 * }\r
723 *\r
724 * This method does nothing in a release build.\r
725 *\r
726 * @param {String/Object} [options] The message to log or an options object with any\r
727 * of the following properties:\r
728 *\r
729 * - `msg`: The message to log (required).\r
730 * - `level`: One of: "error", "warn", "info" or "log" (the default is "log").\r
731 * - `dump`: An object to dump to the log as part of the message.\r
732 * - `stack`: True to include a stack trace in the log.\r
733 * - `indent`: Cause subsequent log statements to be indented one step.\r
734 * - `outdent`: Cause this and following statements to be one step less indented.\r
735 *\r
736 * @param {String...} [message] The message to log (required unless specified in\r
737 * options object).\r
738 */\r
739 log:\r
740 //<debug>\r
741 (function () {\r
742 /*\r
743 * Iterate through an object to dump its content into a string.\r
744 * For example:\r
745 * {\r
746 * style: {\r
747 * lineWidth: 1\r
748 * },\r
749 * label: {},\r
750 * marker: {\r
751 * strokeStyle: "#555",\r
752 * radius: 3,\r
753 * size: 3\r
754 * },\r
755 * subStyle: {\r
756 * fillStyle: [\r
757 * 0: "#133987",\r
758 * 1: "#1c55ca",\r
759 * 2: "#4d7fe6"\r
760 * ]\r
761 * },\r
762 * markerSubStyle: {}\r
763 * } \r
764 *\r
765 * @param {Object} object The object to iterate\r
766 * @param {Number} [level] Current level of identation (and recursion). Default is 0.\r
767 * @param {Number} [maxLevel] Maximum level of recursion. Default is 3.\r
768 * @param {Boolean} [withFunctions] Include functions in the output.\r
769 * @return {String} The string with the contents of the object\r
770 */\r
771 var primitiveRe = /string|number|boolean/;\r
772 function dumpObject (object, level, maxLevel, withFunctions) {\r
773 var member, type, value, name, prefix, suffix,\r
774 members = [];\r
775\r
776 if (Ext.isArray(object)) {\r
777 prefix = '[';\r
778 suffix = ']';\r
779 } else if (Ext.isObject(object)) {\r
780 prefix = '{';\r
781 suffix = '}';\r
782 }\r
783 if (!maxLevel) {\r
784 maxLevel = 3;\r
785 }\r
786 if (level > maxLevel) {\r
787 return prefix+'...'+suffix;\r
788 }\r
789\r
790 level = level || 1;\r
791 var spacer = (new Array(level)).join(' ');\r
792\r
793 // Cannot use Ext.encode since it can recurse endlessly\r
794 for (name in object) {\r
795 if (object.hasOwnProperty(name)) {\r
796 value = object[name];\r
797\r
798 type = typeof value;\r
799 if (type === 'function') {\r
800 if (!withFunctions) {\r
801 continue;\r
802 }\r
803 member = type;\r
804 } else if (type === 'undefined') {\r
805 member = type;\r
806 } else if (value === null || primitiveRe.test(type) || Ext.isDate(value)) {\r
807 member = Ext.encode(value);\r
808 } else if (Ext.isArray(value)) {\r
809 member = dumpObject(value, level+1, maxLevel, withFunctions);\r
810 } else if (Ext.isObject(value)) {\r
811 member = dumpObject(value, level+1, maxLevel, withFunctions);\r
812 } else {\r
813 member = type;\r
814 }\r
815 members.push(spacer + name + ': ' + member); // or Ext.encode(name)\r
816 }\r
817 }\r
818 if (members.length) {\r
819 return prefix + '\n '+ members.join(',\n ') + '\n'+spacer+suffix;\r
820 }\r
821 return prefix+suffix;\r
822 }\r
823\r
824 function log (message) {\r
825 var options, dump,\r
826 con = Ext.global.console,\r
827 level = 'log',\r
828 indent = log.indent || 0,\r
829 prefix, stack, fn, out, max;\r
830\r
831 log.indent = indent;\r
832\r
833 if (typeof message !== 'string') {\r
834 options = message;\r
835 message = options.msg || '';\r
836 level = options.level || level;\r
837 dump = options.dump;\r
838 stack = options.stack;\r
839 prefix = options.prefix;\r
840 fn = options.fn;\r
841\r
842 if (options.indent) {\r
843 ++log.indent;\r
844 } else if (options.outdent) {\r
845 log.indent = indent = Math.max(indent - 1, 0);\r
846 }\r
847\r
848 if (dump && !(con && con.dir)) {\r
849 message += dumpObject(dump);\r
850 dump = null;\r
851 }\r
852 }\r
853\r
854 if (arguments.length > 1) {\r
855 message += Array.prototype.slice.call(arguments, 1).join('');\r
856 }\r
857\r
858 if (prefix) {\r
859 message = prefix + ' - ' + message;\r
860 }\r
861\r
862 message = indent ? Ext.String.repeat(' ', log.indentSize * indent) + message : message;\r
863 // w/o console, all messages are equal, so munge the level into the message:\r
864 if (level !== 'log') {\r
865 message = '[' + level.charAt(0).toUpperCase() + '] ' + message;\r
866 }\r
867\r
868 if (fn) {\r
869 message += '\nCaller: ' + fn.toString();\r
870 }\r
871\r
872 // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so\r
873 // an early test may fail either direction if Firebug is toggled.\r
874 //\r
875 if (con) { // if (Firebug-like console)\r
876 if (con[level]) {\r
877 con[level](message);\r
878 } else {\r
879 con.log(message);\r
880 }\r
881\r
882 if (dump) {\r
883 con.dir(dump);\r
884 }\r
885\r
886 if (stack && con.trace) {\r
887 // Firebug's console.error() includes a trace already...\r
888 if (!con.firebug || level !== 'error') {\r
889 con.trace();\r
890 }\r
891 }\r
892 } else if (Ext.isOpera) {\r
893 opera.postError(message); // jshint ignore:line\r
894 } else {\r
895 out = log.out;\r
896 max = log.max;\r
897\r
898 if (out.length >= max) {\r
899 // this formula allows out.max to change (via debugger), where the\r
900 // more obvious "max/4" would not quite be the same\r
901 Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%\r
902 }\r
903\r
904 out.push(message);\r
905 }\r
906\r
907 // Mostly informational, but the Ext.Error notifier uses them:\r
908 ++log.count;\r
909 ++log.counters[level];\r
910 }\r
911\r
912 function logx (level, args) {\r
913 if (typeof args[0] === 'string') {\r
914 args.unshift({});\r
915 }\r
916 args[0].level = level;\r
917 log.apply(this, args);\r
918 }\r
919\r
920 log.error = function () {\r
921 logx('error', Array.prototype.slice.call(arguments));\r
922 };\r
923 log.info = function () {\r
924 logx('info', Array.prototype.slice.call(arguments));\r
925 };\r
926 log.warn = function () {\r
927 logx('warn', Array.prototype.slice.call(arguments));\r
928 };\r
929\r
930 log.count = 0;\r
931 log.counters = { error: 0, warn: 0, info: 0, log: 0 };\r
932 log.indentSize = 2;\r
933 log.out = [];\r
934 log.max = 750;\r
935\r
936 return log;\r
937 }()) ||\r
938 //</debug>\r
939 (function () {\r
940 var nullLog = function () {};\r
941 nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn;\r
942 return nullLog;\r
943 }())\r
944});\r