]>
Commit | Line | Data |
---|---|---|
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 | |
6 | Ext.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 "<poolName@packageName>" 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 |