3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
21 objectPrototype = Object.prototype,
22 toString = objectPrototype.toString,
24 enumerablesTest = { toString: 1 },
27 if (typeof Ext === 'undefined') {
33 for (i in enumerablesTest) {
38 enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
39 'toLocaleString', 'toString', 'constructor'];
43 * An array containing extra enumerables for old browsers
44 * @property {String[]}
46 Ext.enumerables = enumerables;
49 * Copies all the properties of config to the specified object.
50 * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
51 * {@link Ext.Object#merge} instead.
52 * @param {Object} object The receiver of the properties
53 * @param {Object} config The source of the properties
54 * @param {Object} defaults A different object that will also be applied for default values
55 * @return {Object} returns obj
57 Ext.apply = function(object, config, defaults) {
59 Ext.apply(object, defaults);
62 if (object && config && typeof config === 'object') {
66 object[i] = config[i];
70 for (j = enumerables.length; j--;) {
72 if (config.hasOwnProperty(k)) {
73 object[k] = config[k];
82 Ext.buildSettings = Ext.apply({
85 }, Ext.buildSettings || {});
89 * A reusable empty function
91 emptyFn: function() {},
93 baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
96 * Copies all the properties of config to object if they don't already exist.
97 * @param {Object} object The receiver of the properties
98 * @param {Object} config The source of the properties
99 * @return {Object} returns obj
101 applyIf: function(object, config) {
105 for (property in config) {
106 if (object[property] === undefined) {
107 object[property] = config[property];
116 * Iterates either an array or an object. This method delegates to
117 * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
119 * @param {Object/Array} object The object or array to be iterated.
120 * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
121 * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
122 * type that is being iterated.
123 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
124 * Defaults to the object being iterated itself.
127 iterate: function(object, fn, scope) {
128 if (Ext.isEmpty(object)) {
132 if (scope === undefined) {
136 if (Ext.isIterable(object)) {
137 Ext.Array.each.call(Ext.Array, object, fn, scope);
140 Ext.Object.each.call(Ext.Object, object, fn, scope);
148 * This method deprecated. Use {@link Ext#define Ext.define} instead.
150 * @param {Function} superclass
151 * @param {Object} overrides
152 * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
153 * @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
157 var objectConstructor = objectPrototype.constructor,
158 inlineOverrides = function(o) {
160 if (!o.hasOwnProperty(m)) {
167 return function(subclass, superclass, overrides) {
168 // First we check if the user passed in just the superClass with overrides
169 if (Ext.isObject(superclass)) {
170 overrides = superclass;
171 superclass = subclass;
172 subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
173 superclass.apply(this, arguments);
181 sourceMethod: 'extend',
182 msg: 'Attempting to extend from a class which has not been loaded on the page.'
187 // We create a new temporary class
188 var F = function() {},
189 subclassProto, superclassProto = superclass.prototype;
191 F.prototype = superclassProto;
192 subclassProto = subclass.prototype = new F();
193 subclassProto.constructor = subclass;
194 subclass.superclass = superclassProto;
196 if (superclassProto.constructor === objectConstructor) {
197 superclassProto.constructor = superclass;
200 subclass.override = function(overrides) {
201 Ext.override(subclass, overrides);
204 subclassProto.override = inlineOverrides;
205 subclassProto.proto = subclassProto;
207 subclass.override(overrides);
208 subclass.extend = function(o) {
209 return Ext.extend(subclass, o);
217 * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
219 Ext.define('My.cool.Class', {
225 Ext.override(My.cool.Class, {
227 alert('About to say...');
229 this.callOverridden();
233 var cool = new My.cool.Class();
234 cool.sayHi(); // alerts 'About to say...'
237 * Please note that `this.callOverridden()` only works if the class was previously
238 * created with {@link Ext#define)
240 * @param {Object} cls The class to override
241 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
242 * containing one or more methods.
246 override: function(cls, overrides) {
247 if (cls.prototype.$className) {
248 return cls.override(overrides);
251 Ext.apply(cls.prototype, overrides);
256 // A full set of static methods to do type checking
260 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
261 * value (second argument) otherwise.
263 * @param {Object} value The value to test
264 * @param {Object} defaultValue The value to return if the original value is empty
265 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
266 * @return {Object} value, if non-empty, else defaultValue
268 valueFrom: function(value, defaultValue, allowBlank){
269 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
273 * Returns the type of the given variable in string format. List of possible values are:
275 * - `undefined`: If the given value is `undefined`
276 * - `null`: If the given value is `null`
277 * - `string`: If the given value is a string
278 * - `number`: If the given value is a number
279 * - `boolean`: If the given value is a boolean value
280 * - `date`: If the given value is a `Date` object
281 * - `function`: If the given value is a function reference
282 * - `object`: If the given value is an object
283 * - `array`: If the given value is an array
284 * - `regexp`: If the given value is a regular expression
285 * - `element`: If the given value is a DOM Element
286 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
287 * - `whitespace`: If the given value is a DOM text node and contains only whitespace
289 * @param {Object} value
293 typeOf: function(value) {
294 if (value === null) {
298 var type = typeof value;
300 if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
304 var typeToString = toString.call(value);
306 switch(typeToString) {
307 case '[object Array]':
309 case '[object Date]':
311 case '[object Boolean]':
313 case '[object Number]':
315 case '[object RegExp]':
319 if (type === 'function') {
323 if (type === 'object') {
324 if (value.nodeType !== undefined) {
325 if (value.nodeType === 3) {
326 return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
339 sourceMethod: 'typeOf',
340 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
346 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
350 * - a zero-length array
351 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
353 * @param {Object} value The value to test
354 * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
358 isEmpty: function(value, allowEmptyString) {
359 return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
363 * Returns true if the passed value is a JavaScript Array, false otherwise.
365 * @param {Object} target The target to test
369 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
370 return toString.call(value) === '[object Array]';
374 * Returns true if the passed value is a JavaScript Date object, false otherwise.
375 * @param {Object} object The object to test
378 isDate: function(value) {
379 return toString.call(value) === '[object Date]';
383 * Returns true if the passed value is a JavaScript Object, false otherwise.
384 * @param {Object} value The value to test
388 isObject: (toString.call(null) === '[object Object]') ?
390 // check ownerDocument here as well to exclude DOM nodes
391 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
394 return toString.call(value) === '[object Object]';
398 * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
399 * @param {Object} value The value to test
402 isPrimitive: function(value) {
403 var type = typeof value;
405 return type === 'string' || type === 'number' || type === 'boolean';
409 * Returns true if the passed value is a JavaScript Function, false otherwise.
410 * @param {Object} value The value to test
415 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
416 // Object.prorotype.toString (slower)
417 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
418 return toString.call(value) === '[object Function]';
419 } : function(value) {
420 return typeof value === 'function';
424 * Returns true if the passed value is a number. Returns false for non-finite numbers.
425 * @param {Object} value The value to test
428 isNumber: function(value) {
429 return typeof value === 'number' && isFinite(value);
433 * Validates that a value is numeric.
434 * @param {Object} value Examples: 1, '1', '2.34'
435 * @return {Boolean} True if numeric, false otherwise
437 isNumeric: function(value) {
438 return !isNaN(parseFloat(value)) && isFinite(value);
442 * Returns true if the passed value is a string.
443 * @param {Object} value The value to test
446 isString: function(value) {
447 return typeof value === 'string';
451 * Returns true if the passed value is a boolean.
453 * @param {Object} value The value to test
456 isBoolean: function(value) {
457 return typeof value === 'boolean';
461 * Returns true if the passed value is an HTMLElement
462 * @param {Object} value The value to test
465 isElement: function(value) {
466 return value ? value.nodeType === 1 : false;
470 * Returns true if the passed value is a TextNode
471 * @param {Object} value The value to test
474 isTextNode: function(value) {
475 return value ? value.nodeName === "#text" : false;
479 * Returns true if the passed value is defined.
480 * @param {Object} value The value to test
483 isDefined: function(value) {
484 return typeof value !== 'undefined';
488 * Returns true if the passed value is iterable, false otherwise
489 * @param {Object} value The value to test
492 isIterable: function(value) {
493 return (value && typeof value !== 'string') ? value.length !== undefined : false;
500 * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
501 * @param {Object} item The variable to clone
502 * @return {Object} clone
504 clone: function(item) {
505 if (item === null || item === undefined) {
510 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
512 if (item.nodeType && item.cloneNode) {
513 return item.cloneNode(true);
516 var type = toString.call(item);
519 if (type === '[object Date]') {
520 return new Date(item.getTime());
523 var i, j, k, clone, key;
526 if (type === '[object Array]') {
532 clone[i] = Ext.clone(item[i]);
536 else if (type === '[object Object]' && item.constructor === Object) {
540 clone[key] = Ext.clone(item[key]);
544 for (j = enumerables.length; j--;) {
551 return clone || item;
556 * Generate a unique reference of Ext in the global scope, useful for sandboxing
558 getUniqueGlobalNamespace: function() {
559 var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
561 if (uniqueGlobalNamespace === undefined) {
565 uniqueGlobalNamespace = 'ExtBox' + (++i);
566 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
568 Ext.global[uniqueGlobalNamespace] = Ext;
569 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
572 return uniqueGlobalNamespace;
578 functionFactory: function() {
579 var args = Array.prototype.slice.call(arguments);
581 if (args.length > 0) {
582 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
583 args[args.length - 1];
586 return Function.prototype.constructor.apply(Function.prototype, args);
591 * Old alias to {@link Ext#typeOf}
592 * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
596 Ext.type = Ext.typeOf;
601 * @author Jacky Nguyen <jacky@sencha.com>
602 * @docauthor Jacky Nguyen <jacky@sencha.com>
605 * A utility class that wrap around a string version number and provide convenient
606 * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
608 var version = new Ext.Version('1.0.2beta');
609 console.log("Version is " + version); // Version is 1.0.2beta
611 console.log(version.getMajor()); // 1
612 console.log(version.getMinor()); // 0
613 console.log(version.getPatch()); // 2
614 console.log(version.getBuild()); // 0
615 console.log(version.getRelease()); // beta
617 console.log(version.isGreaterThan('1.0.1')); // True
618 console.log(version.isGreaterThan('1.0.2alpha')); // True
619 console.log(version.isGreaterThan('1.0.2RC')); // False
620 console.log(version.isGreaterThan('1.0.2')); // False
621 console.log(version.isLessThan('1.0.2')); // True
623 console.log(version.match(1.0)); // True
624 console.log(version.match('1.0.2')); // True
630 // Current core version
631 var version = '4.0.7', Version;
632 Ext.Version = Version = Ext.extend(Object, {
635 * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
636 * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
637 * @return {Ext.Version} this
639 constructor: function(version) {
640 var parts, releaseStartIndex;
642 if (version instanceof Version) {
646 this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
648 releaseStartIndex = this.version.search(/([^\d\.])/);
650 if (releaseStartIndex !== -1) {
651 this.release = this.version.substr(releaseStartIndex, version.length);
652 this.shortVersion = this.version.substr(0, releaseStartIndex);
655 this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
657 parts = this.version.split('.');
659 this.major = parseInt(parts.shift() || 0, 10);
660 this.minor = parseInt(parts.shift() || 0, 10);
661 this.patch = parseInt(parts.shift() || 0, 10);
662 this.build = parseInt(parts.shift() || 0, 10);
668 * Override the native toString method
670 * @return {String} version
672 toString: function() {
677 * Override the native valueOf method
679 * @return {String} version
681 valueOf: function() {
686 * Returns the major component value
687 * @return {Number} major
689 getMajor: function() {
690 return this.major || 0;
694 * Returns the minor component value
695 * @return {Number} minor
697 getMinor: function() {
698 return this.minor || 0;
702 * Returns the patch component value
703 * @return {Number} patch
705 getPatch: function() {
706 return this.patch || 0;
710 * Returns the build component value
711 * @return {Number} build
713 getBuild: function() {
714 return this.build || 0;
718 * Returns the release component value
719 * @return {Number} release
721 getRelease: function() {
722 return this.release || '';
726 * Returns whether this version if greater than the supplied argument
727 * @param {String/Number} target The version to compare with
728 * @return {Boolean} True if this version if greater than the target, false otherwise
730 isGreaterThan: function(target) {
731 return Version.compare(this.version, target) === 1;
735 * Returns whether this version if smaller than the supplied argument
736 * @param {String/Number} target The version to compare with
737 * @return {Boolean} True if this version if smaller than the target, false otherwise
739 isLessThan: function(target) {
740 return Version.compare(this.version, target) === -1;
744 * Returns whether this version equals to the supplied argument
745 * @param {String/Number} target The version to compare with
746 * @return {Boolean} True if this version equals to the target, false otherwise
748 equals: function(target) {
749 return Version.compare(this.version, target) === 0;
753 * Returns whether this version matches the supplied argument. Example:
755 * var version = new Ext.Version('1.0.2beta');
756 * console.log(version.match(1)); // True
757 * console.log(version.match(1.0)); // True
758 * console.log(version.match('1.0.2')); // True
759 * console.log(version.match('1.0.2RC')); // False
761 * @param {String/Number} target The version to compare with
762 * @return {Boolean} True if this version matches the target, false otherwise
764 match: function(target) {
765 target = String(target);
766 return this.version.substr(0, target.length) === target;
770 * Returns this format: [major, minor, patch, build, release]. Useful for comparison
773 toArray: function() {
774 return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
778 * Returns shortVersion version without dots and release
781 getShortVersion: function() {
782 return this.shortVersion;
801 * Converts a version component to a comparable value
804 * @param {Object} value The value to convert
807 getComponentValue: function(value) {
808 return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
812 * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
813 * they are handled in the following order:
814 * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
817 * @param {String} current The current version to compare to
818 * @param {String} target The target version to compare to
819 * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
821 compare: function(current, target) {
822 var currentValue, targetValue, i;
824 current = new Version(current).toArray();
825 target = new Version(target).toArray();
827 for (i = 0; i < Math.max(current.length, target.length); i++) {
828 currentValue = this.getComponentValue(current[i]);
829 targetValue = this.getComponentValue(target[i]);
831 if (currentValue < targetValue) {
833 } else if (currentValue > targetValue) {
851 lastRegisteredVersion: null,
854 * Set version number for the given package name.
856 * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
857 * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
860 setVersion: function(packageName, version) {
861 Ext.versions[packageName] = new Version(version);
862 Ext.lastRegisteredVersion = Ext.versions[packageName];
868 * Get the version number of the supplied package name; will return the last registered version
869 * (last Ext.setVersion call) if there's no package name given.
871 * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
872 * @return {Ext.Version} The version
874 getVersion: function(packageName) {
875 if (packageName === undefined) {
876 return Ext.lastRegisteredVersion;
879 return Ext.versions[packageName];
883 * Create a closure for deprecated code.
885 // This means Ext.oldMethod is only supported in 4.0.0beta and older.
886 // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
887 // the closure will not be invoked
888 Ext.deprecate('extjs', '4.0.0beta', function() {
889 Ext.oldMethod = Ext.newMethod;
894 * @param {String} packageName The package name
895 * @param {String} since The last version before it's deprecated
896 * @param {Function} closure The callback function to be executed with the specified version is less than the current version
897 * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
900 deprecate: function(packageName, since, closure, scope) {
901 if (Version.compare(Ext.getVersion(packageName), since) < 1) {
905 }); // End Versioning
907 Ext.setVersion('core', version);
914 * A collection of useful static methods to deal with strings
919 trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
921 formatRe: /\{(\d+)\}/g,
922 escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
925 * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
926 * @param {String} value The string to encode
927 * @return {String} The encoded text
930 htmlEncode: (function() {
936 }, keys = [], p, regex;
938 for (p in entities) {
942 regex = new RegExp('(' + keys.join('|') + ')', 'g');
944 return function(value) {
945 return (!value) ? value : String(value).replace(regex, function(match, capture) {
946 return entities[capture];
952 * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
953 * @param {String} value The string to decode
954 * @return {String} The decoded text
957 htmlDecode: (function() {
963 }, keys = [], p, regex;
965 for (p in entities) {
969 regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
971 return function(value) {
972 return (!value) ? value : String(value).replace(regex, function(match, capture) {
973 if (capture in entities) {
974 return entities[capture];
976 return String.fromCharCode(parseInt(capture.substr(2), 10));
983 * Appends content to the query string of a URL, handling logic for whether to place
984 * a question mark or ampersand.
985 * @param {String} url The URL to append to.
986 * @param {String} string The content to append to the URL.
987 * @return (String) The resulting URL
989 urlAppend : function(url, string) {
990 if (!Ext.isEmpty(string)) {
991 return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
998 * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
1000 var s = ' foo bar ';
1001 alert('-' + s + '-'); //alerts "- foo bar -"
1002 alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
1004 * @param {String} string The string to escape
1005 * @return {String} The trimmed string
1007 trim: function(string) {
1008 return string.replace(Ext.String.trimRegex, "");
1012 * Capitalize the given string
1013 * @param {String} string
1016 capitalize: function(string) {
1017 return string.charAt(0).toUpperCase() + string.substr(1);
1021 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1022 * @param {String} value The string to truncate
1023 * @param {Number} length The maximum length to allow before truncating
1024 * @param {Boolean} word True to try to find a common word break
1025 * @return {String} The converted text
1027 ellipsis: function(value, len, word) {
1028 if (value && value.length > len) {
1030 var vs = value.substr(0, len - 2),
1031 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1032 if (index !== -1 && index >= (len - 15)) {
1033 return vs.substr(0, index) + "...";
1036 return value.substr(0, len - 3) + "...";
1042 * Escapes the passed string for use in a regular expression
1043 * @param {String} string
1046 escapeRegex: function(string) {
1047 return string.replace(Ext.String.escapeRegexRe, "\\$1");
1051 * Escapes the passed string for ' and \
1052 * @param {String} string The string to escape
1053 * @return {String} The escaped string
1055 escape: function(string) {
1056 return string.replace(Ext.String.escapeRe, "\\$1");
1060 * Utility function that allows you to easily switch a string between two alternating values. The passed value
1061 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
1062 * they are already different, the first value passed in is returned. Note that this method returns the new value
1063 * but does not change the current string.
1065 // alternate sort directions
1066 sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1068 // instead of conditional logic:
1069 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1071 * @param {String} string The current string
1072 * @param {String} value The value to compare to the current string
1073 * @param {String} other The new value to use if the string already equals the first value passed in
1074 * @return {String} The new value
1076 toggle: function(string, value, other) {
1077 return string === value ? other : value;
1081 * Pads the left side of a string with a specified character. This is especially useful
1082 * for normalizing number and date strings. Example usage:
1085 var s = Ext.String.leftPad('123', 5, '0');
1086 // s now contains the string: '00123'
1088 * @param {String} string The original string
1089 * @param {Number} size The total length of the output string
1090 * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1091 * @return {String} The padded string
1093 leftPad: function(string, size, character) {
1094 var result = String(string);
1095 character = character || " ";
1096 while (result.length < size) {
1097 result = character + result;
1103 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
1104 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
1106 var cls = 'my-class', text = 'Some text';
1107 var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text);
1108 // s now contains the string: '<div class="my-class">Some text</div>'
1110 * @param {String} string The tokenized string to be formatted
1111 * @param {String} value1 The value to replace token {0}
1112 * @param {String} value2 Etc...
1113 * @return {String} The formatted string
1115 format: function(format) {
1116 var args = Ext.Array.toArray(arguments, 1);
1117 return format.replace(Ext.String.formatRe, function(m, i) {
1123 * Returns a string with a specified number of repititions a given string pattern.
1124 * The pattern be separated by a different string.
1126 * var s = Ext.String.repeat('---', 4); // = '------------'
1127 * var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
1129 * @param {String} pattern The pattern to repeat.
1130 * @param {Number} count The number of times to repeat the pattern (may be 0).
1131 * @param {String} sep An option string to separate each pattern.
1133 repeat: function(pattern, count, sep) {
1134 for (var buf = [], i = count; i--; ) {
1137 return buf.join(sep || '');
1144 * A collection of useful static methods to deal with numbers
1150 var isToFixedBroken = (0.9).toFixed() !== '1';
1154 * Checks whether or not the passed number is within a desired range. If the number is already within the
1155 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1156 * exceeded. Note that this method returns the constrained value but does not change the current number.
1157 * @param {Number} number The number to check
1158 * @param {Number} min The minimum number in the range
1159 * @param {Number} max The maximum number in the range
1160 * @return {Number} The constrained value if outside the range, otherwise the current value
1162 constrain: function(number, min, max) {
1163 number = parseFloat(number);
1166 number = Math.max(number, min);
1169 number = Math.min(number, max);
1175 * Snaps the passed number between stopping points based upon a passed increment value.
1176 * @param {Number} value The unsnapped value.
1177 * @param {Number} increment The increment by which the value must move.
1178 * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1179 * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1180 * @return {Number} The value of the nearest snap target.
1182 snap : function(value, increment, minValue, maxValue) {
1183 var newValue = value,
1186 if (!(increment && value)) {
1189 m = value % increment;
1192 if (m * 2 >= increment) {
1193 newValue += increment;
1194 } else if (m * 2 < -increment) {
1195 newValue -= increment;
1198 return Ext.Number.constrain(newValue, minValue, maxValue);
1202 * Formats a number using fixed-point notation
1203 * @param {Number} value The number to format
1204 * @param {Number} precision The number of digits to show after the decimal point
1206 toFixed: function(value, precision) {
1207 if (isToFixedBroken) {
1208 precision = precision || 0;
1209 var pow = Math.pow(10, precision);
1210 return (Math.round(value * pow) / pow).toFixed(precision);
1213 return value.toFixed(precision);
1217 * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1220 Ext.Number.from('1.23', 1); // returns 1.23
1221 Ext.Number.from('abc', 1); // returns 1
1223 * @param {Object} value
1224 * @param {Number} defaultValue The value to return if the original value is non-numeric
1225 * @return {Number} value, if numeric, defaultValue otherwise
1227 from: function(value, defaultValue) {
1228 if (isFinite(value)) {
1229 value = parseFloat(value);
1232 return !isNaN(value) ? value : defaultValue;
1239 * @deprecated 4.0.0 Please use {@link Ext.Number#from} instead.
1242 * @alias Ext.Number#from
1244 Ext.num = function() {
1245 return Ext.Number.from.apply(this, arguments);
1250 * @author Jacky Nguyen <jacky@sencha.com>
1251 * @docauthor Jacky Nguyen <jacky@sencha.com>
1253 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1257 var arrayPrototype = Array.prototype,
1258 slice = arrayPrototype.slice,
1259 supportsSplice = function () {
1264 if (!array.splice) {
1268 // This detects a bug in IE8 splice method:
1269 // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1275 array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");
1277 lengthBefore = array.length; //41
1278 array.splice(13, 0, "XXX"); // add one element
1280 if (lengthBefore+1 != array.length) {
1287 supportsForEach = 'forEach' in arrayPrototype,
1288 supportsMap = 'map' in arrayPrototype,
1289 supportsIndexOf = 'indexOf' in arrayPrototype,
1290 supportsEvery = 'every' in arrayPrototype,
1291 supportsSome = 'some' in arrayPrototype,
1292 supportsFilter = 'filter' in arrayPrototype,
1293 supportsSort = function() {
1294 var a = [1,2,3,4,5].sort(function(){ return 0; });
1295 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1297 supportsSliceOnNodeList = true,
1301 // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1302 if (typeof document !== 'undefined') {
1303 slice.call(document.getElementsByTagName('body'));
1306 supportsSliceOnNodeList = false;
1309 function fixArrayIndex (array, index) {
1310 return (index < 0) ? Math.max(0, array.length + index)
1311 : Math.min(array.length, index);
1315 Does the same work as splice, but with a slightly more convenient signature. The splice
1316 method has bugs in IE8, so this is the implementation we use on that platform.
1318 The rippling of items in the array can be tricky. Consider two use cases:
1323 +---+---+---+---+---+---+---+---+
1324 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1325 +---+---+---+---+---+---+---+---+
1328 / / \/ \/ \ +--------------------------+
1329 / / /\ /\ +--------------------------+ \
1330 / / / \/ +--------------------------+ \ \
1331 / / / /+--------------------------+ \ \ \
1334 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1335 | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1336 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1340 In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1341 that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1342 must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1344 function replaceSim (array, index, removeCount, insert) {
1345 var add = insert ? insert.length : 0,
1346 length = array.length,
1347 pos = fixArrayIndex(array, index);
1349 // we try to use Array.push when we can for efficiency...
1350 if (pos === length) {
1352 array.push.apply(array, insert);
1355 var remove = Math.min(removeCount, length - pos),
1356 tailOldPos = pos + remove,
1357 tailNewPos = tailOldPos + add - remove,
1358 tailCount = length - tailOldPos,
1359 lengthAfterRemove = length - remove,
1362 if (tailNewPos < tailOldPos) { // case A
1363 for (i = 0; i < tailCount; ++i) {
1364 array[tailNewPos+i] = array[tailOldPos+i];
1366 } else if (tailNewPos > tailOldPos) { // case B
1367 for (i = tailCount; i--; ) {
1368 array[tailNewPos+i] = array[tailOldPos+i];
1370 } // else, add == remove (nothing to do)
1372 if (add && pos === lengthAfterRemove) {
1373 array.length = lengthAfterRemove; // truncate array
1374 array.push.apply(array, insert);
1376 array.length = lengthAfterRemove + add; // reserves space
1377 for (i = 0; i < add; ++i) {
1378 array[pos+i] = insert[i];
1386 function replaceNative (array, index, removeCount, insert) {
1387 if (insert && insert.length) {
1388 if (index < array.length) {
1389 array.splice.apply(array, [index, removeCount].concat(insert));
1391 array.push.apply(array, insert);
1394 array.splice(index, removeCount);
1399 function eraseSim (array, index, removeCount) {
1400 return replaceSim(array, index, removeCount);
1403 function eraseNative (array, index, removeCount) {
1404 array.splice(index, removeCount);
1408 function spliceSim (array, index, removeCount) {
1409 var pos = fixArrayIndex(array, index),
1410 removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1412 if (arguments.length < 4) {
1413 replaceSim(array, pos, removeCount);
1415 replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1421 function spliceNative (array) {
1422 return array.splice.apply(array, slice.call(arguments, 1));
1425 var erase = supportsSplice ? eraseNative : eraseSim,
1426 replace = supportsSplice ? replaceNative : replaceSim,
1427 splice = supportsSplice ? spliceNative : spliceSim;
1429 // NOTE: from here on, use erase, replace or splice (not native methods)...
1431 ExtArray = Ext.Array = {
1433 * Iterates an array or an iterable value and invoke the given callback function for each item.
1435 * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1437 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1438 * console.log(name);
1441 * var sum = function() {
1444 * Ext.Array.each(arguments, function(value) {
1451 * sum(1, 2, 3); // returns 6
1453 * The iteration can be stopped by returning false in the function callback.
1455 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1456 * if (name === 'Singapore') {
1457 * return false; // break here
1461 * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1463 * @param {Array/NodeList/Object} iterable The value to be iterated. If this
1464 * argument is not iterable, the callback function is called once.
1465 * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1466 * the current `index`.
1467 * @param {Object} fn.item The item at the current `index` in the passed `array`
1468 * @param {Number} fn.index The current `index` within the `array`
1469 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1470 * @param {Boolean} fn.return Return false to stop iteration.
1471 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1472 * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1474 * @return {Boolean} See description for the `fn` parameter.
1476 each: function(array, fn, scope, reverse) {
1477 array = ExtArray.from(array);
1482 if (reverse !== true) {
1483 for (i = 0; i < ln; i++) {
1484 if (fn.call(scope || array[i], array[i], i, array) === false) {
1490 for (i = ln - 1; i > -1; i--) {
1491 if (fn.call(scope || array[i], array[i], i, array) === false) {
1501 * Iterates an array and invoke the given callback function for each item. Note that this will simply
1502 * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
1503 * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
1504 * could be much better in modern browsers comparing with {@link Ext.Array#each}
1506 * @param {Array} array The array to iterate
1507 * @param {Function} fn The callback function.
1508 * @param {Object} fn.item The item at the current `index` in the passed `array`
1509 * @param {Number} fn.index The current `index` within the `array`
1510 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1511 * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1513 forEach: function(array, fn, scope) {
1514 if (supportsForEach) {
1515 return array.forEach(fn, scope);
1521 for (; i < ln; i++) {
1522 fn.call(scope, array[i], i, array);
1527 * Get the index of the provided `item` in the given `array`, a supplement for the
1528 * missing arrayPrototype.indexOf in Internet Explorer.
1530 * @param {Array} array The array to check
1531 * @param {Object} item The item to look for
1532 * @param {Number} from (Optional) The index at which to begin the search
1533 * @return {Number} The index of item in the array (or -1 if it is not found)
1535 indexOf: function(array, item, from) {
1536 if (supportsIndexOf) {
1537 return array.indexOf(item, from);
1540 var i, length = array.length;
1542 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1543 if (array[i] === item) {
1552 * Checks whether or not the given `array` contains the specified `item`
1554 * @param {Array} array The array to check
1555 * @param {Object} item The item to look for
1556 * @return {Boolean} True if the array contains the item, false otherwise
1558 contains: function(array, item) {
1559 if (supportsIndexOf) {
1560 return array.indexOf(item) !== -1;
1565 for (i = 0, ln = array.length; i < ln; i++) {
1566 if (array[i] === item) {
1575 * Converts any iterable (numeric indices and a length property) into a true array.
1578 * var args = Ext.Array.toArray(arguments),
1579 * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1581 * alert(args.join(' '));
1582 * alert(fromSecondToLastArgs.join(' '));
1585 * test('just', 'testing', 'here'); // alerts 'just testing here';
1586 * // alerts 'testing here';
1588 * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1589 * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1590 * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1592 * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1594 * @param {Object} iterable the iterable object to be turned into a true Array.
1595 * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1596 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1597 * index of the iterable value
1598 * @return {Array} array
1600 toArray: function(iterable, start, end){
1601 if (!iterable || !iterable.length) {
1605 if (typeof iterable === 'string') {
1606 iterable = iterable.split('');
1609 if (supportsSliceOnNodeList) {
1610 return slice.call(iterable, start || 0, end || iterable.length);
1617 end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1619 for (i = start; i < end; i++) {
1620 array.push(iterable[i]);
1627 * Plucks the value of a property from each item in the Array. Example:
1629 * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1631 * @param {Array/NodeList} array The Array of items to pluck the value from.
1632 * @param {String} propertyName The property name to pluck from each element.
1633 * @return {Array} The value from each item in the Array.
1635 pluck: function(array, propertyName) {
1639 for (i = 0, ln = array.length; i < ln; i++) {
1642 ret.push(item[propertyName]);
1649 * Creates a new array with the results of calling a provided function on every element in this array.
1651 * @param {Array} array
1652 * @param {Function} fn Callback function for each item
1653 * @param {Object} scope Callback function scope
1654 * @return {Array} results
1656 map: function(array, fn, scope) {
1658 return array.map(fn, scope);
1665 for (; i < len; i++) {
1666 results[i] = fn.call(scope, array[i], i, array);
1673 * Executes the specified function for each array element until the function returns a falsy value.
1674 * If such an item is found, the function will return false immediately.
1675 * Otherwise, it will return true.
1677 * @param {Array} array
1678 * @param {Function} fn Callback function for each item
1679 * @param {Object} scope Callback function scope
1680 * @return {Boolean} True if no false value is returned by the callback function.
1682 every: function(array, fn, scope) {
1685 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1688 if (supportsEvery) {
1689 return array.every(fn, scope);
1695 for (; i < ln; ++i) {
1696 if (!fn.call(scope, array[i], i, array)) {
1705 * Executes the specified function for each array element until the function returns a truthy value.
1706 * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1708 * @param {Array} array
1709 * @param {Function} fn Callback function for each item
1710 * @param {Object} scope Callback function scope
1711 * @return {Boolean} True if the callback function returns a truthy value.
1713 some: function(array, fn, scope) {
1716 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1720 return array.some(fn, scope);
1726 for (; i < ln; ++i) {
1727 if (fn.call(scope, array[i], i, array)) {
1736 * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1738 * See {@link Ext.Array#filter}
1740 * @param {Array} array
1741 * @return {Array} results
1743 clean: function(array) {
1749 for (; i < ln; i++) {
1752 if (!Ext.isEmpty(item)) {
1761 * Returns a new array with unique items
1763 * @param {Array} array
1764 * @return {Array} results
1766 unique: function(array) {
1772 for (; i < ln; i++) {
1775 if (ExtArray.indexOf(clone, item) === -1) {
1784 * Creates a new array with all of the elements of this array for which
1785 * the provided filtering function returns true.
1787 * @param {Array} array
1788 * @param {Function} fn Callback function for each item
1789 * @param {Object} scope Callback function scope
1790 * @return {Array} results
1792 filter: function(array, fn, scope) {
1793 if (supportsFilter) {
1794 return array.filter(fn, scope);
1801 for (; i < ln; i++) {
1802 if (fn.call(scope, array[i], i, array)) {
1803 results.push(array[i]);
1811 * Converts a value to an array if it's not already an array; returns:
1813 * - An empty array if given value is `undefined` or `null`
1814 * - Itself if given value is already an array
1815 * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1816 * - An array with one item which is the given value, otherwise
1818 * @param {Object} value The value to convert to an array if it's not already is an array
1819 * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
1821 * @return {Array} array
1823 from: function(value, newReference) {
1824 if (value === undefined || value === null) {
1828 if (Ext.isArray(value)) {
1829 return (newReference) ? slice.call(value) : value;
1832 if (value && value.length !== undefined && typeof value !== 'string') {
1833 return Ext.toArray(value);
1840 * Removes the specified item from the array if it exists
1842 * @param {Array} array The array
1843 * @param {Object} item The item to remove
1844 * @return {Array} The passed array itself
1846 remove: function(array, item) {
1847 var index = ExtArray.indexOf(array, item);
1850 erase(array, index, 1);
1857 * Push an item into the array only if the array doesn't contain it yet
1859 * @param {Array} array The array
1860 * @param {Object} item The item to include
1862 include: function(array, item) {
1863 if (!ExtArray.contains(array, item)) {
1869 * Clone a flat array without referencing the previous one. Note that this is different
1870 * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1871 * for Array.prototype.slice.call(array)
1873 * @param {Array} array The array
1874 * @return {Array} The clone array
1876 clone: function(array) {
1877 return slice.call(array);
1881 * Merge multiple arrays into one with unique items.
1883 * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1885 * @param {Array} array1
1886 * @param {Array} array2
1887 * @param {Array} etc
1888 * @return {Array} merged
1891 var args = slice.call(arguments),
1895 for (i = 0, ln = args.length; i < ln; i++) {
1896 array = array.concat(args[i]);
1899 return ExtArray.unique(array);
1903 * Merge multiple arrays into one with unique items that exist in all of the arrays.
1905 * @param {Array} array1
1906 * @param {Array} array2
1907 * @param {Array} etc
1908 * @return {Array} intersect
1910 intersect: function() {
1912 arrays = slice.call(arguments),
1913 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1915 if (!arrays.length) {
1919 // Find the smallest array
1920 for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1921 if (!minArray || array.length < minArray.length) {
1927 minArray = ExtArray.unique(minArray);
1928 erase(arrays, x, 1);
1930 // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1931 // an item in the small array, we're likely to find it before reaching the end
1932 // of the inner loop and can terminate the search early.
1933 for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1936 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1937 for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1945 if (count === arraysLn) {
1954 * Perform a set difference A-B by subtracting all items in array B from array A.
1956 * @param {Array} arrayA
1957 * @param {Array} arrayB
1958 * @return {Array} difference
1960 difference: function(arrayA, arrayB) {
1961 var clone = slice.call(arrayA),
1965 for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1966 for (j = 0; j < ln; j++) {
1967 if (clone[j] === arrayB[i]) {
1979 * Returns a shallow copy of a part of an array. This is equivalent to the native
1980 * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1981 * is "arguments" since the arguments object does not supply a slice method but can
1982 * be the context object to Array.prototype.slice.
1984 * @param {Array} array The array (or arguments object).
1985 * @param {Number} begin The index at which to begin. Negative values are offsets from
1986 * the end of the array.
1987 * @param {Number} end The index at which to end. The copied items do not include
1988 * end. Negative values are offsets from the end of the array. If end is omitted,
1989 * all items up to the end of the array are copied.
1990 * @return {Array} The copied piece of the array.
1992 // Note: IE6 will return [] on slice.call(x, undefined).
1993 slice: ([1,2].slice(1, undefined).length ?
1994 function (array, begin, end) {
1995 return slice.call(array, begin, end);
1997 // at least IE6 uses arguments.length for variadic signature
1998 function (array, begin, end) {
1999 // After tested for IE 6, the one below is of the best performance
2000 // see http://jsperf.com/slice-fix
2001 if (typeof begin === 'undefined') {
2002 return slice.call(array);
2004 if (typeof end === 'undefined') {
2005 return slice.call(array, begin);
2007 return slice.call(array, begin, end);
2012 * Sorts the elements of an Array.
2013 * By default, this method sorts the elements alphabetically and ascending.
2015 * @param {Array} array The array to sort.
2016 * @param {Function} sortFn (optional) The comparison function.
2017 * @return {Array} The sorted array.
2019 sort: function(array, sortFn) {
2022 return array.sort(sortFn);
2024 return array.sort();
2028 var length = array.length,
2033 for (; i < length; i++) {
2035 for (j = i + 1; j < length; j++) {
2037 comparison = sortFn(array[j], array[min]);
2038 if (comparison < 0) {
2041 } else if (array[j] < array[min]) {
2047 array[i] = array[min];
2056 * Recursively flattens into 1-d Array. Injects Arrays inline.
2058 * @param {Array} array The array to flatten
2059 * @return {Array} The 1-d array.
2061 flatten: function(array) {
2064 function rFlatten(a) {
2067 for (i = 0, ln = a.length; i < ln; i++) {
2070 if (Ext.isArray(v)) {
2080 return rFlatten(array);
2084 * Returns the minimum value in the Array.
2086 * @param {Array/NodeList} array The Array from which to select the minimum value.
2087 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2088 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2089 * @return {Object} minValue The minimum value
2091 min: function(array, comparisonFn) {
2095 for (i = 0, ln = array.length; i < ln; i++) {
2099 if (comparisonFn(min, item) === 1) {
2114 * Returns the maximum value in the Array.
2116 * @param {Array/NodeList} array The Array from which to select the maximum value.
2117 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2118 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2119 * @return {Object} maxValue The maximum value
2121 max: function(array, comparisonFn) {
2125 for (i = 0, ln = array.length; i < ln; i++) {
2129 if (comparisonFn(max, item) === -1) {
2144 * Calculates the mean of all items in the array.
2146 * @param {Array} array The Array to calculate the mean value of.
2147 * @return {Number} The mean.
2149 mean: function(array) {
2150 return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2154 * Calculates the sum of all items in the given array.
2156 * @param {Array} array The Array to calculate the sum value of.
2157 * @return {Number} The sum.
2159 sum: function(array) {
2163 for (i = 0,ln = array.length; i < ln; i++) {
2173 _replaceSim: replaceSim, // for unit testing
2174 _spliceSim: spliceSim,
2178 * Removes items from an array. This is functionally equivalent to the splice method
2179 * of Array, but works around bugs in IE8's splice method and does not copy the
2180 * removed elements in order to return them (because very often they are ignored).
2182 * @param {Array} array The Array on which to replace.
2183 * @param {Number} index The index in the array at which to operate.
2184 * @param {Number} removeCount The number of items to remove at index.
2185 * @return {Array} The array passed.
2191 * Inserts items in to an array.
2193 * @param {Array} array The Array on which to replace.
2194 * @param {Number} index The index in the array at which to operate.
2195 * @param {Array} items The array of items to insert at index.
2196 * @return {Array} The array passed.
2198 insert: function (array, index, items) {
2199 return replace(array, index, 0, items);
2203 * Replaces items in an array. This is functionally equivalent to the splice method
2204 * of Array, but works around bugs in IE8's splice method and is often more convenient
2205 * to call because it accepts an array of items to insert rather than use a variadic
2208 * @param {Array} array The Array on which to replace.
2209 * @param {Number} index The index in the array at which to operate.
2210 * @param {Number} removeCount The number of items to remove at index (can be 0).
2211 * @param {Array} insert (optional) An array of items to insert at index.
2212 * @return {Array} The array passed.
2218 * Replaces items in an array. This is equivalent to the splice method of Array, but
2219 * works around bugs in IE8's splice method. The signature is exactly the same as the
2220 * splice method except that the array is the first argument. All arguments following
2221 * removeCount are inserted in the array at index.
2223 * @param {Array} array The Array on which to replace.
2224 * @param {Number} index The index in the array at which to operate.
2225 * @param {Number} removeCount The number of items to remove at index (can be 0).
2226 * @return {Array} An array containing the removed items.
2235 * @alias Ext.Array#each
2237 Ext.each = ExtArray.each;
2242 * @alias Ext.Array#merge
2244 ExtArray.union = ExtArray.merge;
2247 * Old alias to {@link Ext.Array#min}
2248 * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2251 * @alias Ext.Array#min
2253 Ext.min = ExtArray.min;
2256 * Old alias to {@link Ext.Array#max}
2257 * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2260 * @alias Ext.Array#max
2262 Ext.max = ExtArray.max;
2265 * Old alias to {@link Ext.Array#sum}
2266 * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2269 * @alias Ext.Array#sum
2271 Ext.sum = ExtArray.sum;
2274 * Old alias to {@link Ext.Array#mean}
2275 * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2278 * @alias Ext.Array#mean
2280 Ext.mean = ExtArray.mean;
2283 * Old alias to {@link Ext.Array#flatten}
2284 * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2287 * @alias Ext.Array#flatten
2289 Ext.flatten = ExtArray.flatten;
2292 * Old alias to {@link Ext.Array#clean}
2293 * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2296 * @alias Ext.Array#clean
2298 Ext.clean = ExtArray.clean;
2301 * Old alias to {@link Ext.Array#unique}
2302 * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2305 * @alias Ext.Array#unique
2307 Ext.unique = ExtArray.unique;
2310 * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2311 * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2314 * @alias Ext.Array#pluck
2316 Ext.pluck = ExtArray.pluck;
2321 * @alias Ext.Array#toArray
2323 Ext.toArray = function() {
2324 return ExtArray.toArray.apply(ExtArray, arguments);
2329 * @class Ext.Function
2331 * A collection of useful static methods to deal with function callbacks
2337 * A very commonly used method throughout the framework. It acts as a wrapper around another method
2338 * which originally accepts 2 arguments for `name` and `value`.
2339 * The wrapped function then allows "flexible" value setting of either:
2341 * - `name` and `value` as 2 arguments
2342 * - one single object argument with multiple key - value pairs
2346 * var setValue = Ext.Function.flexSetter(function(name, value) {
2347 * this[name] = value;
2351 * // Setting a single name - value
2352 * setValue('name1', 'value1');
2354 * // Settings multiple name - value pairs
2361 * @param {Function} setter
2362 * @returns {Function} flexSetter
2364 flexSetter: function(fn) {
2365 return function(a, b) {
2372 if (typeof a !== 'string') {
2374 if (a.hasOwnProperty(k)) {
2375 fn.call(this, k, a[k]);
2379 if (Ext.enumerables) {
2380 for (i = Ext.enumerables.length; i--;) {
2381 k = Ext.enumerables[i];
2382 if (a.hasOwnProperty(k)) {
2383 fn.call(this, k, a[k]);
2388 fn.call(this, a, b);
2396 * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2397 * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2399 * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2401 * @param {Function} fn The function to delegate.
2402 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2403 * **If omitted, defaults to the browser window.**
2404 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2405 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2406 * if a number the args are inserted at the specified position
2407 * @return {Function} The new function
2409 bind: function(fn, scope, args, appendArgs) {
2410 if (arguments.length === 2) {
2412 return fn.apply(scope, arguments);
2417 slice = Array.prototype.slice;
2420 var callArgs = args || arguments;
2422 if (appendArgs === true) {
2423 callArgs = slice.call(arguments, 0);
2424 callArgs = callArgs.concat(args);
2426 else if (typeof appendArgs == 'number') {
2427 callArgs = slice.call(arguments, 0); // copy arguments first
2428 Ext.Array.insert(callArgs, appendArgs, args);
2431 return method.apply(scope || window, callArgs);
2436 * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2437 * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2438 * This is especially useful when creating callbacks.
2442 * var originalFunction = function(){
2443 * alert(Ext.Array.from(arguments).join(' '));
2446 * var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2448 * callback(); // alerts 'Hello World'
2449 * callback('by Me'); // alerts 'Hello World by Me'
2451 * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2453 * @param {Function} fn The original function
2454 * @param {Array} args The arguments to pass to new callback
2455 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2456 * @return {Function} The new callback function
2458 pass: function(fn, args, scope) {
2460 args = Ext.Array.from(args);
2464 return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2469 * Create an alias to the provided method property with name `methodName` of `object`.
2470 * Note that the execution scope will still be bound to the provided `object` itself.
2472 * @param {Object/Function} object
2473 * @param {String} methodName
2474 * @return {Function} aliasFn
2476 alias: function(object, methodName) {
2478 return object[methodName].apply(object, arguments);
2483 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2484 * the original one is not called. The resulting function returns the results of the original function.
2485 * The passed function is called with the parameters of the original function. Example usage:
2487 * var sayHi = function(name){
2488 * alert('Hi, ' + name);
2491 * sayHi('Fred'); // alerts "Hi, Fred"
2493 * // create a new function that validates input without
2494 * // directly modifying the original function:
2495 * var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2496 * return name == 'Brian';
2499 * sayHiToFriend('Fred'); // no alert
2500 * sayHiToFriend('Brian'); // alerts "Hi, Brian"
2502 * @param {Function} origFn The original function.
2503 * @param {Function} newFn The function to call before the original
2504 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2505 * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2506 * @param {Object} returnValue (optional) The value to return if the passed function return false (defaults to null).
2507 * @return {Function} The new function
2509 createInterceptor: function(origFn, newFn, scope, returnValue) {
2510 var method = origFn;
2511 if (!Ext.isFunction(newFn)) {
2519 newFn.method = origFn;
2520 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2526 * Creates a delegate (callback) which, when called, executes after a specific delay.
2528 * @param {Function} fn The function which will be called on a delay when the returned function is called.
2529 * Optionally, a replacement (or additional) argument list may be specified.
2530 * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2531 * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2532 * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2533 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2534 * if a number the args are inserted at the specified position.
2535 * @return {Function} A function which, when called, executes the original function after the specified delay.
2537 createDelayed: function(fn, delay, scope, args, appendArgs) {
2538 if (scope || args) {
2539 fn = Ext.Function.bind(fn, scope, args, appendArgs);
2543 setTimeout(function() {
2544 fn.apply(me, arguments);
2550 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2552 * var sayHi = function(name){
2553 * alert('Hi, ' + name);
2556 * // executes immediately:
2559 * // executes after 2 seconds:
2560 * Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2562 * // this syntax is sometimes useful for deferring
2563 * // execution of an anonymous function:
2564 * Ext.Function.defer(function(){
2565 * alert('Anonymous');
2568 * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2570 * @param {Function} fn The function to defer.
2571 * @param {Number} millis The number of milliseconds for the setTimeout call
2572 * (if less than or equal to 0 the function is executed immediately)
2573 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2574 * **If omitted, defaults to the browser window.**
2575 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2576 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2577 * if a number the args are inserted at the specified position
2578 * @return {Number} The timeout id that can be used with clearTimeout
2580 defer: function(fn, millis, obj, args, appendArgs) {
2581 fn = Ext.Function.bind(fn, obj, args, appendArgs);
2583 return setTimeout(fn, millis);
2590 * Create a combined function call sequence of the original function + the passed function.
2591 * The resulting function returns the results of the original function.
2592 * The passed function is called with the parameters of the original function. Example usage:
2594 * var sayHi = function(name){
2595 * alert('Hi, ' + name);
2598 * sayHi('Fred'); // alerts "Hi, Fred"
2600 * var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2601 * alert('Bye, ' + name);
2604 * sayGoodbye('Fred'); // both alerts show
2606 * @param {Function} origFn The original function.
2607 * @param {Function} newFn The function to sequence
2608 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2609 * If omitted, defaults to the scope in which the original function is called or the browser window.
2610 * @return {Function} The new function
2612 createSequence: function(origFn, newFn, scope) {
2613 if (!Ext.isFunction(newFn)) {
2618 var retval = origFn.apply(this || window, arguments);
2619 newFn.apply(scope || this || window, arguments);
2626 * Creates a delegate function, optionally with a bound scope which, when called, buffers
2627 * the execution of the passed function for the configured number of milliseconds.
2628 * If called again within that period, the impending invocation will be canceled, and the
2629 * timeout period will begin again.
2631 * @param {Function} fn The function to invoke on a buffered timer.
2632 * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2634 * @param {Object} scope (optional) The scope (`this` reference) in which
2635 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2636 * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2637 * passed by the caller.
2638 * @return {Function} A function which invokes the passed function after buffering for the specified time.
2640 createBuffered: function(fn, buffer, scope, args) {
2646 clearTimeout(timerId);
2649 timerId = setTimeout(function(){
2650 fn.apply(scope || me, args || arguments);
2657 * Creates a throttled version of the passed function which, when called repeatedly and
2658 * rapidly, invokes the passed function only after a certain interval has elapsed since the
2659 * previous invocation.
2661 * This is useful for wrapping functions which may be called repeatedly, such as
2662 * a handler of a mouse move event when the processing is expensive.
2664 * @param {Function} fn The function to execute at a regular time interval.
2665 * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2666 * @param {Object} scope (optional) The scope (`this` reference) in which
2667 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2668 * @returns {Function} A function which invokes the passed function at the specified interval.
2670 createThrottled: function(fn, interval, scope) {
2671 var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2672 fn.apply(scope || this, lastArgs);
2673 lastCallTime = new Date().getTime();
2677 elapsed = new Date().getTime() - lastCallTime;
2678 lastArgs = arguments;
2680 clearTimeout(timer);
2681 if (!lastCallTime || (elapsed >= interval)) {
2684 timer = setTimeout(execute, interval - elapsed);
2690 * Adds behavior to an existing method that is executed before the
2691 * original behavior of the function. For example:
2695 * add: function(ingredient) {
2696 * this.contents.push(ingredient);
2699 * Ext.Function.interceptBefore(soup, "add", function(ingredient){
2700 * if (!this.contents.length && ingredient !== "water") {
2701 * // Always add water to start with
2702 * this.contents.push("water");
2705 * soup.add("onions");
2707 * soup.contents; // will contain: water, onions, salt
2709 * @param {Object} object The target object
2710 * @param {String} methodName Name of the method to override
2711 * @param {Function} fn Function with the new behavior. It will
2712 * be called with the same arguments as the original method. The
2713 * return value of this function will be the return value of the
2715 * @return {Function} The new function just created.
2717 interceptBefore: function(object, methodName, fn) {
2718 var method = object[methodName] || Ext.emptyFn;
2720 return object[methodName] = function() {
2721 var ret = fn.apply(this, arguments);
2722 method.apply(this, arguments);
2729 * Adds behavior to an existing method that is executed after the
2730 * original behavior of the function. For example:
2734 * add: function(ingredient) {
2735 * this.contents.push(ingredient);
2738 * Ext.Function.interceptAfter(soup, "add", function(ingredient){
2739 * // Always add a bit of extra salt
2740 * this.contents.push("salt");
2742 * soup.add("water");
2743 * soup.add("onions");
2744 * soup.contents; // will contain: water, salt, onions, salt
2746 * @param {Object} object The target object
2747 * @param {String} methodName Name of the method to override
2748 * @param {Function} fn Function with the new behavior. It will
2749 * be called with the same arguments as the original method. The
2750 * return value of this function will be the return value of the
2752 * @return {Function} The new function just created.
2754 interceptAfter: function(object, methodName, fn) {
2755 var method = object[methodName] || Ext.emptyFn;
2757 return object[methodName] = function() {
2758 method.apply(this, arguments);
2759 return fn.apply(this, arguments);
2767 * @alias Ext.Function#defer
2769 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2774 * @alias Ext.Function#pass
2776 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2781 * @alias Ext.Function#bind
2783 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2786 * @author Jacky Nguyen <jacky@sencha.com>
2787 * @docauthor Jacky Nguyen <jacky@sencha.com>
2790 * A collection of useful static methods to deal with objects.
2797 var ExtObject = Ext.Object = {
2800 * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
2801 * query strings. For example:
2803 * var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2805 * // objects then equals:
2807 * { name: 'hobbies', value: 'reading' },
2808 * { name: 'hobbies', value: 'cooking' },
2809 * { name: 'hobbies', value: 'swimming' },
2812 * var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2820 * }, true); // Recursive
2822 * // objects then equals:
2824 * { name: 'dateOfBirth[day]', value: 3 },
2825 * { name: 'dateOfBirth[month]', value: 8 },
2826 * { name: 'dateOfBirth[year]', value: 1987 },
2827 * { name: 'dateOfBirth[extra][hour]', value: 4 },
2828 * { name: 'dateOfBirth[extra][minute]', value: 30 },
2831 * @param {String} name
2832 * @param {Object/Array} value
2833 * @param {Boolean} [recursive=false] True to traverse object recursively
2836 toQueryObjects: function(name, value, recursive) {
2837 var self = ExtObject.toQueryObjects,
2841 if (Ext.isArray(value)) {
2842 for (i = 0, ln = value.length; i < ln; i++) {
2844 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2854 else if (Ext.isObject(value)) {
2856 if (value.hasOwnProperty(i)) {
2858 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2880 * Takes an object and converts it to an encoded query string.
2884 * Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2885 * Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2886 * Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2887 * Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2888 * Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2892 * Ext.Object.toQueryString({
2893 * username: 'Jacky',
2899 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2900 * }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2902 * // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2903 * // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2905 * @param {Object} object The object to encode
2906 * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
2907 * (PHP / Ruby on Rails servers and similar).
2908 * @return {String} queryString
2910 toQueryString: function(object, recursive) {
2911 var paramObjects = [],
2913 i, j, ln, paramObject, value;
2916 if (object.hasOwnProperty(i)) {
2917 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2921 for (j = 0, ln = paramObjects.length; j < ln; j++) {
2922 paramObject = paramObjects[j];
2923 value = paramObject.value;
2925 if (Ext.isEmpty(value)) {
2928 else if (Ext.isDate(value)) {
2929 value = Ext.Date.toString(value);
2932 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2935 return params.join('&');
2939 * Converts a query string back into an object.
2943 * Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2944 * Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2945 * Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2946 * Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2950 * Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
2953 * username: 'Jacky',
2959 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2962 * @param {String} queryString The query string to decode
2963 * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
2964 * PHP / Ruby on Rails servers and similar.
2967 fromQueryString: function(queryString, recursive) {
2968 var parts = queryString.replace(/^\?/, '').split('&'),
2970 temp, components, name, value, i, ln,
2971 part, j, subLn, matchedKeys, matchedName,
2974 for (i = 0, ln = parts.length; i < ln; i++) {
2977 if (part.length > 0) {
2978 components = part.split('=');
2979 name = decodeURIComponent(components[0]);
2980 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2983 if (object.hasOwnProperty(name)) {
2984 if (!Ext.isArray(object[name])) {
2985 object[name] = [object[name]];
2988 object[name].push(value);
2991 object[name] = value;
2995 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2996 matchedName = name.match(/^([^\[]+)/);
3001 sourceClass: "Ext.Object",
3002 sourceMethod: "fromQueryString",
3003 queryString: queryString,
3004 recursive: recursive,
3005 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
3010 name = matchedName[0];
3013 if (matchedKeys === null) {
3014 object[name] = value;
3018 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
3019 key = matchedKeys[j];
3020 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
3028 for (j = 0, subLn = keys.length; j < subLn; j++) {
3031 if (j === subLn - 1) {
3032 if (Ext.isArray(temp) && key === '') {
3040 if (temp[key] === undefined || typeof temp[key] === 'string') {
3041 nextKey = keys[j+1];
3043 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
3057 * Iterates through an object and invokes the given callback function for each iteration.
3058 * The iteration can be stopped by returning `false` in the callback function. For example:
3062 * hairColor: 'black'
3063 * loves: ['food', 'sleeping', 'wife']
3066 * Ext.Object.each(person, function(key, value, myself) {
3067 * console.log(key + ":" + value);
3069 * if (key === 'hairColor') {
3070 * return false; // stop the iteration
3074 * @param {Object} object The object to iterate
3075 * @param {Function} fn The callback function.
3076 * @param {String} fn.key
3077 * @param {Object} fn.value
3078 * @param {Object} fn.object The object itself
3079 * @param {Object} [scope] The execution scope (`this`) of the callback function
3081 each: function(object, fn, scope) {
3082 for (var property in object) {
3083 if (object.hasOwnProperty(property)) {
3084 if (fn.call(scope || object, property, object[property], object) === false) {
3092 * Merges any number of objects recursively without referencing them or their children.
3095 * companyName: 'Ext JS',
3096 * products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
3100 * location: 'Palo Alto',
3106 * companyName: 'Sencha Inc.',
3107 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3110 * location: 'Redwood City'
3114 * var sencha = Ext.Object.merge(extjs, newStuff);
3116 * // extjs and sencha then equals to
3118 * companyName: 'Sencha Inc.',
3119 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3123 * location: 'Redwood City'
3128 * @param {Object...} object Any number of objects to merge.
3129 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3131 merge: function(source, key, value) {
3132 if (typeof key === 'string') {
3133 if (value && value.constructor === Object) {
3134 if (source[key] && source[key].constructor === Object) {
3135 ExtObject.merge(source[key], value);
3138 source[key] = Ext.clone(value);
3142 source[key] = value;
3149 ln = arguments.length,
3152 for (; i < ln; i++) {
3153 object = arguments[i];
3155 for (property in object) {
3156 if (object.hasOwnProperty(property)) {
3157 ExtObject.merge(source, property, object[property]);
3166 * Returns the first matching key corresponding to the given value.
3167 * If no matching value is found, null is returned.
3174 * alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
3176 * @param {Object} object
3177 * @param {Object} value The value to find
3179 getKey: function(object, value) {
3180 for (var property in object) {
3181 if (object.hasOwnProperty(property) && object[property] === value) {
3190 * Gets all values of the given object as an array.
3192 * var values = Ext.Object.getValues({
3195 * }); // ['Jacky', 'food']
3197 * @param {Object} object
3198 * @return {Array} An array of values from the object
3200 getValues: function(object) {
3204 for (property in object) {
3205 if (object.hasOwnProperty(property)) {
3206 values.push(object[property]);
3214 * Gets all keys of the given object as an array.
3216 * var values = Ext.Object.getKeys({
3219 * }); // ['name', 'loves']
3221 * @param {Object} object
3222 * @return {String[]} An array of keys from the object
3225 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3229 for (property in object) {
3230 if (object.hasOwnProperty(property)) {
3231 keys.push(property);
3239 * Gets the total number of this object's own properties
3241 * var size = Ext.Object.getSize({
3244 * }); // size equals 2
3246 * @param {Object} object
3247 * @return {Number} size
3249 getSize: function(object) {
3253 for (property in object) {
3254 if (object.hasOwnProperty(property)) {
3265 * A convenient alias method for {@link Ext.Object#merge}.
3269 * @alias Ext.Object#merge
3271 Ext.merge = Ext.Object.merge;
3274 * Alias for {@link Ext.Object#toQueryString}.
3278 * @alias Ext.Object#toQueryString
3279 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
3281 Ext.urlEncode = function() {
3282 var args = Ext.Array.from(arguments),
3285 // Support for the old `pre` argument
3286 if ((typeof args[1] === 'string')) {
3287 prefix = args[1] + '&';
3291 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3295 * Alias for {@link Ext.Object#fromQueryString}.
3299 * @alias Ext.Object#fromQueryString
3300 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
3302 Ext.urlDecode = function() {
3303 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3310 * A set of useful static methods to deal with date
3311 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3312 * this object for convenience
3314 * The date parsing and formatting syntax contains a subset of
3315 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3316 * supported will provide results equivalent to their PHP versions.
3318 * The following is a list of all currently supported formats:
3320 Format Description Example returned values
3321 ------ ----------------------------------------------------------------------- -----------------------
3322 d Day of the month, 2 digits with leading zeros 01 to 31
3323 D A short textual representation of the day of the week Mon to Sun
3324 j Day of the month without leading zeros 1 to 31
3325 l A full textual representation of the day of the week Sunday to Saturday
3326 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
3327 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
3328 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
3329 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
3330 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
3331 F A full textual representation of a month, such as January or March January to December
3332 m Numeric representation of a month, with leading zeros 01 to 12
3333 M A short textual representation of a month Jan to Dec
3334 n Numeric representation of a month, without leading zeros 1 to 12
3335 t Number of days in the given month 28 to 31
3336 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
3337 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
3338 belongs to the previous or next year, that year is used instead)
3339 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
3340 y A two digit representation of a year Examples: 99 or 03
3341 a Lowercase Ante meridiem and Post meridiem am or pm
3342 A Uppercase Ante meridiem and Post meridiem AM or PM
3343 g 12-hour format of an hour without leading zeros 1 to 12
3344 G 24-hour format of an hour without leading zeros 0 to 23
3345 h 12-hour format of an hour with leading zeros 01 to 12
3346 H 24-hour format of an hour with leading zeros 00 to 23
3347 i Minutes, with leading zeros 00 to 59
3348 s Seconds, with leading zeros 00 to 59
3349 u Decimal fraction of a second Examples:
3350 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
3351 100 (i.e. 0.100s) or
3352 999 (i.e. 0.999s) or
3353 999876543210 (i.e. 0.999876543210s)
3354 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
3355 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
3356 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
3357 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
3360 1) If unspecified, the month / day defaults to the current month / day, 1991 or
3361 the time defaults to midnight, while the timezone defaults to the 1992-10 or
3362 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3363 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
3364 are optional. 1995-07-18T17:21:28-02:00 or
3365 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
3366 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
3367 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
3368 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
3369 date-time granularity which are supported, or see 2000-02-13T21:25:33
3370 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
3371 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
3372 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3373 \/Date(1238606590509+0800)\/
3376 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3379 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3381 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3382 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
3383 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
3384 console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
3387 * Here are some standard date/time patterns that you might find helpful. They
3388 * are not part of the source of Ext.Date, but to use them you can simply copy this
3389 * block of code into any script that is included after Ext.Date and they will also become
3390 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
3392 Ext.Date.patterns = {
3393 ISO8601Long:"Y-m-d H:i:s",
3394 ISO8601Short:"Y-m-d",
3396 LongDate: "l, F d, Y",
3397 FullDateTime: "l, F d, Y g:i:s A",
3400 LongTime: "g:i:s A",
3401 SortableDateTime: "Y-m-d\\TH:i:s",
3402 UniversalSortableDateTime: "Y-m-d H:i:sO",
3409 var dt = new Date();
3410 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3412 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3413 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3418 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3419 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3420 * They generate precompiled functions from format patterns instead of parsing and
3421 * processing each pattern every time a date is formatted. These functions are available
3422 * on every Date object.
3427 // create private copy of Ext's Ext.util.Format.format() method
3428 // - to remove unnecessary dependency
3429 // - to resolve namespace conflict with MS-Ajax's implementation
3430 function xf(format) {
3431 var args = Array.prototype.slice.call(arguments, 1);
3432 return format.replace(/\{(\d+)\}/g, function(m, i) {
3439 * Returns the current timestamp
3440 * @return {Date} The current timestamp
3443 now: Date.now || function() {
3451 toString: function(date) {
3452 var pad = Ext.String.leftPad;
3454 return date.getFullYear() + "-"
3455 + pad(date.getMonth() + 1, 2, '0') + "-"
3456 + pad(date.getDate(), 2, '0') + "T"
3457 + pad(date.getHours(), 2, '0') + ":"
3458 + pad(date.getMinutes(), 2, '0') + ":"
3459 + pad(date.getSeconds(), 2, '0');
3463 * Returns the number of milliseconds between two dates
3464 * @param {Date} dateA The first date
3465 * @param {Date} dateB (optional) The second date, defaults to now
3466 * @return {Number} The difference in milliseconds
3468 getElapsed: function(dateA, dateB) {
3469 return Math.abs(dateA - (dateB || new Date()));
3473 * Global flag which determines if strict date parsing should be used.
3474 * Strict date parsing will not roll-over invalid dates, which is the
3475 * default behaviour of javascript Date objects.
3476 * (see {@link #parse} for more information)
3477 * Defaults to <tt>false</tt>.
3483 formatCodeToRegex: function(character, currentGroup) {
3484 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3485 var p = utilDate.parseCodes[character];
3488 p = typeof p == 'function'? p() : p;
3489 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3492 return p ? Ext.applyIf({
3493 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3497 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3502 * <p>An object hash in which each property is a date parsing function. The property name is the
3503 * format string which that function parses.</p>
3504 * <p>This object is automatically populated with date parsing functions as
3505 * date formats are requested for Ext standard formatting strings.</p>
3506 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3507 * may be used as a format string to {@link #parse}.<p>
3508 * <p>Example:</p><pre><code>
3509 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3511 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3512 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3513 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3514 * (i.e. prevent javascript Date "rollover") (The default must be false).
3515 * Invalid date strings should return null when parsed.</div></li>
3517 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3518 * formatting function must be placed into the {@link #formatFunctions} property.
3519 * @property parseFunctions
3523 "MS": function(input, strict) {
3524 // note: the timezone offset is ignored since the MS Ajax server sends
3525 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3526 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3527 var r = (input || '').match(re);
3528 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3534 * <p>An object hash in which each property is a date formatting function. The property name is the
3535 * format string which corresponds to the produced formatted date string.</p>
3536 * <p>This object is automatically populated with date formatting functions as
3537 * date formats are requested for Ext standard formatting strings.</p>
3538 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3539 * may be used as a format string to {@link #format}. Example:</p><pre><code>
3540 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3542 * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3543 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3545 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3546 * parsing function must be placed into the {@link #parseFunctions} property.
3547 * @property formatFunctions
3552 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3553 return '\\/Date(' + this.getTime() + ')\\/';
3560 * Date interval constant
3566 * Date interval constant
3572 * Date interval constant
3577 /** Date interval constant
3583 * Date interval constant
3589 * Date interval constant
3595 * Date interval constant
3601 * <p>An object hash containing default date values used during date parsing.</p>
3602 * <p>The following properties are available:<div class="mdetail-params"><ul>
3603 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3604 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3605 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3606 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3607 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3608 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3609 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3611 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3612 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3613 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3614 * It is the responsiblity of the developer to account for this.</b></p>
3617 // set default day value to the first day of the month
3618 Ext.Date.defaults.d = 1;
3620 // parse a February date string containing only year and month values.
3621 // setting the default day value to 1 prevents weird date rollover issues
3622 // when attempting to parse the following date string on, for example, March 31st 2009.
3623 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3625 * @property defaults
3631 * @property {String[]} dayNames
3632 * An array of textual day names.
3633 * Override these values for international dates.
3636 Ext.Date.dayNames = [
3654 * @property {String[]} monthNames
3655 * An array of textual month names.
3656 * Override these values for international dates.
3659 Ext.Date.monthNames = [
3682 * @property {Object} monthNumbers
3683 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3684 * Override these values for international dates.
3687 Ext.Date.monthNumbers = {
3688 'ShortJanNameInYourLang':0,
3689 'ShortFebNameInYourLang':1,
3709 * @property {String} defaultFormat
3710 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3711 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
3712 * <p>This may be overridden in a locale file.</p>
3714 defaultFormat : "m/d/Y",
3716 * Get the short month name for the given month number.
3717 * Override this function for international dates.
3718 * @param {Number} month A zero-based javascript month number.
3719 * @return {String} The short month name.
3721 getShortMonthName : function(month) {
3722 return utilDate.monthNames[month].substring(0, 3);
3726 * Get the short day name for the given day number.
3727 * Override this function for international dates.
3728 * @param {Number} day A zero-based javascript day number.
3729 * @return {String} The short day name.
3731 getShortDayName : function(day) {
3732 return utilDate.dayNames[day].substring(0, 3);
3736 * Get the zero-based javascript month number for the given short/full month name.
3737 * Override this function for international dates.
3738 * @param {String} name The short/full month name.
3739 * @return {Number} The zero-based javascript month number.
3741 getMonthNumber : function(name) {
3742 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3743 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3747 * Checks if the specified format contains hour information
3748 * @param {String} format The format to check
3749 * @return {Boolean} True if the format contains hour information
3752 formatContainsHourInfo : (function(){
3753 var stripEscapeRe = /(\\.)/g,
3754 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3755 return function(format){
3756 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3761 * Checks if the specified format contains information about
3762 * anything other than the time.
3763 * @param {String} format The format to check
3764 * @return {Boolean} True if the format contains information about
3765 * date/day information.
3768 formatContainsDateInfo : (function(){
3769 var stripEscapeRe = /(\\.)/g,
3770 dateInfoRe = /([djzmnYycU]|MS)/;
3772 return function(format){
3773 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3778 * The base format-code to formatting-function hashmap used by the {@link #format} method.
3779 * Formatting functions are strings (or functions which return strings) which
3780 * will return the appropriate value when evaluated in the context of the Date object
3781 * from which the {@link #format} method is called.
3782 * Add to / override these mappings for custom date formatting.
3783 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3786 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3787 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3792 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3793 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3794 j: "this.getDate()",
3795 l: "Ext.Date.dayNames[this.getDay()]",
3796 N: "(this.getDay() ? this.getDay() : 7)",
3797 S: "Ext.Date.getSuffix(this)",
3799 z: "Ext.Date.getDayOfYear(this)",
3800 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3801 F: "Ext.Date.monthNames[this.getMonth()]",
3802 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3803 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3804 n: "(this.getMonth() + 1)",
3805 t: "Ext.Date.getDaysInMonth(this)",
3806 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3807 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3808 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3809 y: "('' + this.getFullYear()).substring(2, 4)",
3810 a: "(this.getHours() < 12 ? 'am' : 'pm')",
3811 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3812 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3813 G: "this.getHours()",
3814 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3815 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3816 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3817 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3818 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3819 O: "Ext.Date.getGMTOffset(this)",
3820 P: "Ext.Date.getGMTOffset(this, true)",
3821 T: "Ext.Date.getTimezone(this)",
3822 Z: "(this.getTimezoneOffset() * -60)",
3824 c: function() { // ISO-8601 -- GMT format
3825 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3826 var e = c.charAt(i);
3827 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3829 return code.join(" + ");
3832 c: function() { // ISO-8601 -- UTC format
3834 "this.getUTCFullYear()", "'-'",
3835 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3836 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3838 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3839 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3840 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3846 U: "Math.round(this.getTime() / 1000)"
3850 * Checks if the passed Date parameters will cause a javascript Date "rollover".
3851 * @param {Number} year 4-digit year
3852 * @param {Number} month 1-based month-of-year
3853 * @param {Number} day Day of month
3854 * @param {Number} hour (optional) Hour
3855 * @param {Number} minute (optional) Minute
3856 * @param {Number} second (optional) Second
3857 * @param {Number} millisecond (optional) Millisecond
3858 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3860 isValid : function(y, m, d, h, i, s, ms) {
3867 // Special handling for year < 100
3868 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3870 return y == dt.getFullYear() &&
3871 m == dt.getMonth() + 1 &&
3872 d == dt.getDate() &&
3873 h == dt.getHours() &&
3874 i == dt.getMinutes() &&
3875 s == dt.getSeconds() &&
3876 ms == dt.getMilliseconds();
3880 * Parses the passed string using the specified date format.
3881 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3882 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3883 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3884 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3885 * Keep in mind that the input date string must precisely match the specified format string
3886 * in order for the parse operation to be successful (failed parse operations return a null value).
3887 * <p>Example:</p><pre><code>
3888 //dt = Fri May 25 2007 (current date)
3889 var dt = new Date();
3891 //dt = Thu May 25 2006 (today's month/day in 2006)
3892 dt = Ext.Date.parse("2006", "Y");
3894 //dt = Sun Jan 15 2006 (all date parts specified)
3895 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3897 //dt = Sun Jan 15 2006 15:20:01
3898 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3900 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3901 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3903 * @param {String} input The raw date string.
3904 * @param {String} format The expected date string format.
3905 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3906 (defaults to false). Invalid date strings will return null when parsed.
3907 * @return {Date} The parsed Date.
3909 parse : function(input, format, strict) {
3910 var p = utilDate.parseFunctions;
3911 if (p[format] == null) {
3912 utilDate.createParser(format);
3914 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3918 parseDate: function(input, format, strict){
3919 return utilDate.parse(input, format, strict);
3924 getFormatCode : function(character) {
3925 var f = utilDate.formatCodes[character];
3928 f = typeof f == 'function'? f() : f;
3929 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3932 // note: unknown characters are treated as literals
3933 return f || ("'" + Ext.String.escape(character) + "'");
3937 createFormat : function(format) {
3942 for (var i = 0; i < format.length; ++i) {
3943 ch = format.charAt(i);
3944 if (!special && ch == "\\") {
3946 } else if (special) {
3948 code.push("'" + Ext.String.escape(ch) + "'");
3950 code.push(utilDate.getFormatCode(ch));
3953 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3957 createParser : (function() {
3959 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3960 "def = Ext.Date.defaults,",
3961 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3966 "if(u != null){", // i.e. unix time is defined
3967 "v = new Date(u * 1000);", // give top priority to UNIX time
3969 // create Date object representing midnight of the current day;
3970 // this will provide us with our date defaults
3971 // (note: clearTime() handles Daylight Saving Time automatically)
3972 "dt = Ext.Date.clearTime(new Date);",
3974 // date calculations (note: these calculations create a dependency on Ext.Number.from())
3975 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3976 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3977 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3979 // time calculations (note: these calculations create a dependency on Ext.Number.from())
3980 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3981 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3982 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3983 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3985 "if(z >= 0 && y >= 0){",
3986 // both the year and zero-based day of year are defined and >= 0.
3987 // these 2 values alone provide sufficient info to create a full date object
3989 // create Date object representing January 1st for the given year
3990 // handle years < 100 appropriately
3991 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3993 // then add day of year, checking for Date "rollover" if necessary
3994 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3995 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3996 "v = null;", // invalid date, so return null
3998 // plain old Date object
3999 // handle years < 100 properly
4000 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
4006 // favour UTC offset over GMT offset
4008 // reset to UTC, then add offset
4009 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
4011 // reset to GMT, then add offset
4012 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
4019 return function(format) {
4020 var regexNum = utilDate.parseRegexes.length,
4027 for (var i = 0; i < format.length; ++i) {
4028 ch = format.charAt(i);
4029 if (!special && ch == "\\") {
4031 } else if (special) {
4033 regex.push(Ext.String.escape(ch));
4035 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
4036 currentGroup += obj.g;
4038 if (obj.g && obj.c) {
4044 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4045 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
4053 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4054 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4055 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4059 c:"d = parseInt(results[{0}], 10);\n",
4060 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4064 c:"d = parseInt(results[{0}], 10);\n",
4065 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4068 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
4072 s:"(?:" + a.join("|") +")"
4079 s:"(?:" + utilDate.dayNames.join("|") + ")"
4085 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4095 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4099 c:"z = parseInt(results[{0}], 10);\n",
4100 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4105 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4110 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4111 s:"(" + utilDate.monthNames.join("|") + ")"
4115 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4116 return Ext.applyIf({
4117 s:"(" + a.join("|") + ")"
4118 }, utilDate.formatCodeToRegex("F"));
4122 c:"m = parseInt(results[{0}], 10) - 1;\n",
4123 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4127 c:"m = parseInt(results[{0}], 10) - 1;\n",
4128 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4133 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4141 return utilDate.formatCodeToRegex("Y");
4145 c:"y = parseInt(results[{0}], 10);\n",
4146 s:"(\\d{4})" // 4-digit year
4150 c:"var ty = parseInt(results[{0}], 10);\n"
4151 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4155 * In the am/pm parsing routines, we allow both upper and lower case
4156 * even though it doesn't exactly match the spec. It gives much more flexibility
4157 * in being able to specify case insensitive regexes.
4161 c:"if (/(am)/i.test(results[{0}])) {\n"
4162 + "if (!h || h == 12) { h = 0; }\n"
4163 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4168 c:"if (/(am)/i.test(results[{0}])) {\n"
4169 + "if (!h || h == 12) { h = 0; }\n"
4170 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4174 return utilDate.formatCodeToRegex("G");
4178 c:"h = parseInt(results[{0}], 10);\n",
4179 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4182 return utilDate.formatCodeToRegex("H");
4186 c:"h = parseInt(results[{0}], 10);\n",
4187 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4191 c:"i = parseInt(results[{0}], 10);\n",
4192 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4196 c:"s = parseInt(results[{0}], 10);\n",
4197 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4201 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4202 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4207 "o = results[{0}];",
4208 "var sn = o.substring(0,1),", // get + / - sign
4209 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4210 "mn = o.substring(3,5) % 60;", // get minutes
4211 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
4213 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4218 "o = results[{0}];",
4219 "var sn = o.substring(0,1),", // get + / - sign
4220 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4221 "mn = o.substring(4,6) % 60;", // get minutes
4222 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
4224 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4229 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4233 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4234 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4235 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4240 utilDate.formatCodeToRegex("Y", 1), // year
4241 utilDate.formatCodeToRegex("m", 2), // month
4242 utilDate.formatCodeToRegex("d", 3), // day
4243 utilDate.formatCodeToRegex("h", 4), // hour
4244 utilDate.formatCodeToRegex("i", 5), // minute
4245 utilDate.formatCodeToRegex("s", 6), // second
4246 {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4247 {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
4248 "if(results[8]) {", // timezone specified
4249 "if(results[8] == 'Z'){",
4251 "}else if (results[8].indexOf(':') > -1){",
4252 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4254 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4260 for (var i = 0, l = arr.length; i < l; ++i) {
4261 calc.push(arr[i].c);
4268 arr[0].s, // year (required)
4269 "(?:", "-", arr[1].s, // month (optional)
4270 "(?:", "-", arr[2].s, // day (optional)
4272 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4273 arr[3].s, ":", arr[4].s, // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
4274 "(?::", arr[5].s, ")?", // seconds (optional)
4275 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4276 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4285 c:"u = parseInt(results[{0}], 10);\n",
4286 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4290 //Old Ext.Date prototype methods.
4292 dateFormat: function(date, format) {
4293 return utilDate.format(date, format);
4297 * Formats a date given the supplied format string.
4298 * @param {Date} date The date to format
4299 * @param {String} format The format string
4300 * @return {String} The formatted date
4302 format: function(date, format) {
4303 if (utilDate.formatFunctions[format] == null) {
4304 utilDate.createFormat(format);
4306 var result = utilDate.formatFunctions[format].call(date);
4311 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4313 * Note: The date string returned by the javascript Date object's toString() method varies
4314 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4315 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4316 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4317 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4318 * from the GMT offset portion of the date string.
4319 * @param {Date} date The date
4320 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4322 getTimezone : function(date) {
4323 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4325 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4326 // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
4327 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4328 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4329 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4331 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4332 // step 1: (?:\((.*)\) -- find timezone in parentheses
4333 // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
4334 // step 3: remove all non uppercase characters found in step 1 and 2
4335 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4339 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4340 * @param {Date} date The date
4341 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4342 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4344 getGMTOffset : function(date, colon) {
4345 var offset = date.getTimezoneOffset();
4346 return (offset > 0 ? "-" : "+")
4347 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4348 + (colon ? ":" : "")
4349 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4353 * Get the numeric day number of the year, adjusted for leap year.
4354 * @param {Date} date The date
4355 * @return {Number} 0 to 364 (365 in leap years).
4357 getDayOfYear: function(date) {
4359 d = Ext.Date.clone(date),
4360 m = date.getMonth(),
4363 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4364 num += utilDate.getDaysInMonth(d);
4366 return num + date.getDate() - 1;
4370 * Get the numeric ISO-8601 week number of the year.
4371 * (equivalent to the format specifier 'W', but without a leading zero).
4372 * @param {Date} date The date
4373 * @return {Number} 1 to 53
4376 getWeekOfYear : (function() {
4377 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4378 var ms1d = 864e5, // milliseconds in a day
4379 ms7d = 7 * ms1d; // milliseconds in a week
4381 return function(date) { // return a closure so constants get calculated only once
4382 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4383 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4384 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4386 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4391 * Checks if the current date falls within a leap year.
4392 * @param {Date} date The date
4393 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4395 isLeapYear : function(date) {
4396 var year = date.getFullYear();
4397 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4401 * Get the first day of the current month, adjusted for leap year. The returned value
4402 * is the numeric day index within the week (0-6) which can be used in conjunction with
4403 * the {@link #monthNames} array to retrieve the textual day name.
4406 var dt = new Date('1/10/2007'),
4407 firstDay = Ext.Date.getFirstDayOfMonth(dt);
4408 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4410 * @param {Date} date The date
4411 * @return {Number} The day number (0-6).
4413 getFirstDayOfMonth : function(date) {
4414 var day = (date.getDay() - (date.getDate() - 1)) % 7;
4415 return (day < 0) ? (day + 7) : day;
4419 * Get the last day of the current month, adjusted for leap year. The returned value
4420 * is the numeric day index within the week (0-6) which can be used in conjunction with
4421 * the {@link #monthNames} array to retrieve the textual day name.
4424 var dt = new Date('1/10/2007'),
4425 lastDay = Ext.Date.getLastDayOfMonth(dt);
4426 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4428 * @param {Date} date The date
4429 * @return {Number} The day number (0-6).
4431 getLastDayOfMonth : function(date) {
4432 return utilDate.getLastDateOfMonth(date).getDay();
4437 * Get the date of the first day of the month in which this date resides.
4438 * @param {Date} date The date
4441 getFirstDateOfMonth : function(date) {
4442 return new Date(date.getFullYear(), date.getMonth(), 1);
4446 * Get the date of the last day of the month in which this date resides.
4447 * @param {Date} date The date
4450 getLastDateOfMonth : function(date) {
4451 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4455 * Get the number of days in the current month, adjusted for leap year.
4456 * @param {Date} date The date
4457 * @return {Number} The number of days in the month.
4460 getDaysInMonth: (function() {
4461 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4463 return function(date) { // return a closure for efficiency
4464 var m = date.getMonth();
4466 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4471 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4472 * @param {Date} date The date
4473 * @return {String} 'st, 'nd', 'rd' or 'th'.
4475 getSuffix : function(date) {
4476 switch (date.getDate()) {
4493 * Creates and returns a new Date instance with the exact same date value as the called instance.
4494 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4495 * variable will also be changed. When the intention is to create a new variable that will not
4496 * modify the original instance, you should create a clone.
4498 * Example of correctly cloning a date:
4501 var orig = new Date('10/1/2006');
4504 console.log(orig); //returns 'Thu Oct 05 2006'!
4507 var orig = new Date('10/1/2006'),
4508 copy = Ext.Date.clone(orig);
4510 console.log(orig); //returns 'Thu Oct 01 2006'
4512 * @param {Date} date The date
4513 * @return {Date} The new Date instance.
4515 clone : function(date) {
4516 return new Date(date.getTime());
4520 * Checks if the current date is affected by Daylight Saving Time (DST).
4521 * @param {Date} date The date
4522 * @return {Boolean} True if the current date is affected by DST.
4524 isDST : function(date) {
4525 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4526 // courtesy of @geoffrey.mcgill
4527 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4531 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4532 * automatically adjusting for Daylight Saving Time (DST) where applicable.
4533 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4534 * @param {Date} date The date
4535 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4536 * @return {Date} this or the clone.
4538 clearTime : function(date, clone) {
4540 return Ext.Date.clearTime(Ext.Date.clone(date));
4543 // get current date before clearing time
4544 var d = date.getDate();
4550 date.setMilliseconds(0);
4552 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4553 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4554 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4556 // increment hour until cloned date == current date
4557 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4560 date.setHours(c.getHours());
4567 * Provides a convenient method for performing basic date arithmetic. This method
4568 * does not modify the Date instance being called - it creates and returns
4569 * a new Date instance containing the resulting date value.
4574 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4575 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4577 // Negative values will be subtracted:
4578 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4579 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4583 * @param {Date} date The date to modify
4584 * @param {String} interval A valid date interval enum value.
4585 * @param {Number} value The amount to add to the current date.
4586 * @return {Date} The new Date instance.
4588 add : function(date, interval, value) {
4589 var d = Ext.Date.clone(date),
4591 if (!interval || value === 0) return d;
4593 switch(interval.toLowerCase()) {
4594 case Ext.Date.MILLI:
4595 d.setMilliseconds(d.getMilliseconds() + value);
4597 case Ext.Date.SECOND:
4598 d.setSeconds(d.getSeconds() + value);
4600 case Ext.Date.MINUTE:
4601 d.setMinutes(d.getMinutes() + value);
4604 d.setHours(d.getHours() + value);
4607 d.setDate(d.getDate() + value);
4609 case Ext.Date.MONTH:
4610 var day = date.getDate();
4612 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4615 d.setMonth(date.getMonth() + value);
4618 d.setFullYear(date.getFullYear() + value);
4625 * Checks if a date falls on or between the given start and end dates.
4626 * @param {Date} date The date to check
4627 * @param {Date} start Start date
4628 * @param {Date} end End date
4629 * @return {Boolean} true if this date falls on or between the given start and end dates.
4631 between : function(date, start, end) {
4632 var t = date.getTime();
4633 return start.getTime() <= t && t <= end.getTime();
4636 //Maintains compatibility with old static and prototype window.Date methods.
4637 compat: function() {
4638 var nativeDate = window.Date,
4640 statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
4641 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4644 Ext.Array.forEach(statics, function(s) {
4645 nativeDate[s] = utilDate[s];
4648 //Append to prototype
4649 Ext.Array.forEach(proto, function(s) {
4650 nativeDate.prototype[s] = function() {
4651 var args = Array.prototype.slice.call(arguments);
4653 return utilDate[s].apply(utilDate, args);
4659 var utilDate = Ext.Date;
4664 * @author Jacky Nguyen <jacky@sencha.com>
4665 * @docauthor Jacky Nguyen <jacky@sencha.com>
4668 * The root of all classes created with {@link Ext#define}.
4670 * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
4671 * All prototype and static members of this class are inherited by all other classes.
4673 (function(flexSetter) {
4675 var Base = Ext.Base = function() {};
4677 $className: 'Ext.Base',
4682 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4683 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4684 * for a detailed comparison
4686 * Ext.define('My.Cat', {
4688 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4691 * constructor: function() {
4692 * alert(this.self.speciesName); / dependent on 'this'
4697 * clone: function() {
4698 * return new this.self();
4703 * Ext.define('My.SnowLeopard', {
4706 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4710 * var cat = new My.Cat(); // alerts 'Cat'
4711 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
4713 * var clone = snowLeopard.clone();
4714 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4721 // Default constructor, simply returns `this`
4722 constructor: function() {
4726 //<feature classSystem.config>
4728 * Initialize configuration for this class. a typical example:
4730 * Ext.define('My.awesome.Class', {
4731 * // The default config
4737 * constructor: function(config) {
4738 * this.initConfig(config);
4744 * var awesome = new My.awesome.Class({
4745 * name: 'Super Awesome'
4748 * alert(awesome.getName()); // 'Super Awesome'
4751 * @param {Object} config
4752 * @return {Object} mixins The mixin prototypes as key - value pairs
4754 initConfig: function(config) {
4755 if (!this.$configInited) {
4756 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4758 this.applyConfig(this.config);
4760 this.$configInited = true;
4769 setConfig: function(config) {
4770 this.applyConfig(config || {});
4778 applyConfig: flexSetter(function(name, value) {
4779 var setter = 'set' + Ext.String.capitalize(name);
4781 if (typeof this[setter] === 'function') {
4782 this[setter].call(this, value);
4790 * Call the parent's overridden method. For example:
4792 * Ext.define('My.own.A', {
4793 * constructor: function(test) {
4798 * Ext.define('My.own.B', {
4799 * extend: 'My.own.A',
4801 * constructor: function(test) {
4804 * this.callParent([test + 1]);
4808 * Ext.define('My.own.C', {
4809 * extend: 'My.own.B',
4811 * constructor: function() {
4812 * alert("Going to call parent's overriden constructor...");
4814 * this.callParent(arguments);
4818 * var a = new My.own.A(1); // alerts '1'
4819 * var b = new My.own.B(1); // alerts '1', then alerts '2'
4820 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4821 * // alerts '2', then alerts '3'
4824 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4825 * from the current method, for example: `this.callParent(arguments)`
4826 * @return {Object} Returns the result from the superclass' method
4828 callParent: function(args) {
4829 var method = this.callParent.caller,
4830 parentClass, methodName;
4832 if (!method.$owner) {
4834 if (!method.caller) {
4836 sourceClass: Ext.getClassName(this),
4837 sourceMethod: "callParent",
4838 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4843 method = method.caller;
4846 parentClass = method.$owner.superclass;
4847 methodName = method.$name;
4850 if (!(methodName in parentClass)) {
4852 sourceClass: Ext.getClassName(this),
4853 sourceMethod: methodName,
4854 msg: "this.callParent() was called but there's no such method (" + methodName +
4855 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4860 return parentClass[methodName].apply(this, args || []);
4865 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4866 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4867 * `this` points to during run-time
4869 * Ext.define('My.Cat', {
4872 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4875 * constructor: function() {
4876 * var statics = this.statics();
4878 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
4879 * // equivalent to: My.Cat.speciesName
4881 * alert(this.self.speciesName); // dependent on 'this'
4883 * statics.totalCreated++;
4888 * clone: function() {
4889 * var cloned = new this.self; // dependent on 'this'
4891 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
4898 * Ext.define('My.SnowLeopard', {
4902 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4905 * constructor: function() {
4906 * this.callParent();
4910 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
4912 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4914 * var clone = snowLeopard.clone();
4915 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4916 * alert(clone.groupName); // alerts 'Cat'
4918 * alert(My.Cat.totalCreated); // alerts 3
4921 * @return {Ext.Class}
4923 statics: function() {
4924 var method = this.statics.caller,
4931 return method.$owner;
4935 * Call the original method that was previously overridden with {@link Ext.Base#override}
4937 * Ext.define('My.Cat', {
4938 * constructor: function() {
4939 * alert("I'm a cat!");
4946 * constructor: function() {
4947 * alert("I'm going to be a cat!");
4949 * var instance = this.callOverridden();
4951 * alert("Meeeeoooowwww");
4957 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4958 * // alerts "I'm a cat!"
4959 * // alerts "Meeeeoooowwww"
4961 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4962 * @return {Object} Returns the result after calling the overridden method
4965 callOverridden: function(args) {
4966 var method = this.callOverridden.caller;
4969 if (!method.$owner) {
4971 sourceClass: Ext.getClassName(this),
4972 sourceMethod: "callOverridden",
4973 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4977 if (!method.$previous) {
4979 sourceClass: Ext.getClassName(this),
4980 sourceMethod: "callOverridden",
4981 msg: "this.callOverridden was called in '" + method.$name +
4982 "' but this method has never been overridden"
4987 return method.$previous.apply(this, args || []);
4990 destroy: function() {}
4993 // These static properties will be copied to every newly created class with {@link Ext#define}
4994 Ext.apply(Ext.Base, {
4996 * Create a new instance of this Class.
4998 * Ext.define('My.cool.Class', {
5002 * My.cool.Class.create({
5006 * All parameters are passed to the constructor of the class.
5008 * @return {Object} the created instance.
5012 create: function() {
5013 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
5020 own: function(name, value) {
5021 if (typeof value == 'function') {
5022 this.ownMethod(name, value);
5025 this.prototype[name] = value;
5033 ownMethod: function(name, fn) {
5036 if (typeof fn.$owner !== 'undefined' && fn !== Ext.emptyFn) {
5040 return originalFn.apply(this, arguments);
5046 className = Ext.getClassName(this);
5048 fn.displayName = className + '#' + name;
5054 this.prototype[name] = fn;
5058 * Add / override static properties of this class.
5060 * Ext.define('My.cool.Class', {
5064 * My.cool.Class.addStatics({
5065 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
5066 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
5067 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
5070 * @param {Object} members
5071 * @return {Ext.Base} this
5075 addStatics: function(members) {
5076 for (var name in members) {
5077 if (members.hasOwnProperty(name)) {
5078 this[name] = members[name];
5087 * @param {Object} members
5089 addInheritableStatics: function(members) {
5090 var inheritableStatics,
5091 hasInheritableStatics,
5092 prototype = this.prototype,
5095 inheritableStatics = prototype.$inheritableStatics;
5096 hasInheritableStatics = prototype.$hasInheritableStatics;
5098 if (!inheritableStatics) {
5099 inheritableStatics = prototype.$inheritableStatics = [];
5100 hasInheritableStatics = prototype.$hasInheritableStatics = {};
5104 var className = Ext.getClassName(this);
5107 for (name in members) {
5108 if (members.hasOwnProperty(name)) {
5109 member = members[name];
5111 if (typeof member == 'function') {
5112 member.displayName = className + '.' + name;
5115 this[name] = member;
5117 if (!hasInheritableStatics[name]) {
5118 hasInheritableStatics[name] = true;
5119 inheritableStatics.push(name);
5128 * Add methods / properties to the prototype of this class.
5130 * Ext.define('My.awesome.Cat', {
5131 * constructor: function() {
5136 * My.awesome.Cat.implement({
5137 * meow: function() {
5138 * alert('Meowww...');
5142 * var kitty = new My.awesome.Cat;
5145 * @param {Object} members
5149 implement: function(members) {
5150 var prototype = this.prototype,
5151 enumerables = Ext.enumerables,
5154 var className = Ext.getClassName(this);
5156 for (name in members) {
5157 if (members.hasOwnProperty(name)) {
5158 member = members[name];
5160 if (typeof member === 'function') {
5161 member.$owner = this;
5162 member.$name = name;
5165 member.displayName = className + '#' + name;
5170 prototype[name] = member;
5175 for (i = enumerables.length; i--;) {
5176 name = enumerables[i];
5178 if (members.hasOwnProperty(name)) {
5179 member = members[name];
5180 member.$owner = this;
5181 member.$name = name;
5182 prototype[name] = member;
5189 * Borrow another class' members to the prototype of this class.
5191 * Ext.define('Bank', {
5193 * printMoney: function() {
5198 * Ext.define('Thief', {
5202 * Thief.borrow(Bank, ['money', 'printMoney']);
5204 * var steve = new Thief();
5206 * alert(steve.money); // alerts '$$$'
5207 * steve.printMoney(); // alerts '$$$$$$$'
5209 * @param {Ext.Base} fromClass The class to borrow members from
5210 * @param {String/String[]} members The names of the members to borrow
5211 * @return {Ext.Base} this
5215 borrow: function(fromClass, members) {
5216 var fromPrototype = fromClass.prototype,
5219 members = Ext.Array.from(members);
5221 for (i = 0, ln = members.length; i < ln; i++) {
5222 member = members[i];
5224 this.own(member, fromPrototype[member]);
5231 * Override prototype members of this class. Overridden methods can be invoked via
5232 * {@link Ext.Base#callOverridden}
5234 * Ext.define('My.Cat', {
5235 * constructor: function() {
5236 * alert("I'm a cat!");
5243 * constructor: function() {
5244 * alert("I'm going to be a cat!");
5246 * var instance = this.callOverridden();
5248 * alert("Meeeeoooowwww");
5254 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5255 * // alerts "I'm a cat!"
5256 * // alerts "Meeeeoooowwww"
5258 * @param {Object} members
5259 * @return {Ext.Base} this
5263 override: function(members) {
5264 var prototype = this.prototype,
5265 enumerables = Ext.enumerables,
5266 name, i, member, previous;
5268 if (arguments.length === 2) {
5270 member = arguments[1];
5272 if (typeof member == 'function') {
5273 if (typeof prototype[name] == 'function') {
5274 previous = prototype[name];
5275 member.$previous = previous;
5278 this.ownMethod(name, member);
5281 prototype[name] = member;
5287 for (name in members) {
5288 if (members.hasOwnProperty(name)) {
5289 member = members[name];
5291 if (typeof member === 'function') {
5292 if (typeof prototype[name] === 'function') {
5293 previous = prototype[name];
5294 member.$previous = previous;
5297 this.ownMethod(name, member);
5300 prototype[name] = member;
5306 for (i = enumerables.length; i--;) {
5307 name = enumerables[i];
5309 if (members.hasOwnProperty(name)) {
5310 if (typeof prototype[name] !== 'undefined') {
5311 previous = prototype[name];
5312 members[name].$previous = previous;
5315 this.ownMethod(name, members[name]);
5323 //<feature classSystem.mixins>
5325 * Used internally by the mixins pre-processor
5329 mixin: function(name, cls) {
5330 var mixin = cls.prototype,
5331 my = this.prototype,
5334 for (key in mixin) {
5335 if (mixin.hasOwnProperty(key)) {
5336 if (typeof my[key] === 'undefined' && key !== 'mixins' && key !== 'mixinId') {
5337 if (typeof mixin[key] === 'function') {
5340 if (typeof fn.$owner === 'undefined') {
5341 this.ownMethod(key, fn);
5348 my[key] = mixin[key];
5351 //<feature classSystem.config>
5352 else if (key === 'config' && my.config && mixin.config) {
5353 Ext.Object.merge(my.config, mixin.config);
5359 if (typeof mixin.onClassMixedIn !== 'undefined') {
5360 mixin.onClassMixedIn.call(cls, this);
5363 if (!my.hasOwnProperty('mixins')) {
5364 if ('mixins' in my) {
5365 my.mixins = Ext.Object.merge({}, my.mixins);
5372 my.mixins[name] = mixin;
5377 * Get the current class' name in string format.
5379 * Ext.define('My.cool.Class', {
5380 * constructor: function() {
5381 * alert(this.self.getName()); // alerts 'My.cool.Class'
5385 * My.cool.Class.getName(); // 'My.cool.Class'
5387 * @return {String} className
5391 getName: function() {
5392 return Ext.getClassName(this);
5396 * Create aliases for existing prototype methods. Example:
5398 * Ext.define('My.cool.Class', {
5399 * method1: function() { ... },
5400 * method2: function() { ... }
5403 * var test = new My.cool.Class();
5405 * My.cool.Class.createAlias({
5406 * method3: 'method1',
5407 * method4: 'method2'
5410 * test.method3(); // test.method1()
5412 * My.cool.Class.createAlias('method5', 'method3');
5414 * test.method5(); // test.method3() -> test.method1()
5416 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5417 * {@link Ext.Function#flexSetter flexSetter}
5418 * @param {String/Object} origin The original method name
5423 createAlias: flexSetter(function(alias, origin) {
5424 this.prototype[alias] = function() {
5425 return this[origin].apply(this, arguments);
5430 })(Ext.Function.flexSetter);
5433 * @author Jacky Nguyen <jacky@sencha.com>
5434 * @docauthor Jacky Nguyen <jacky@sencha.com>
5437 * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
5438 * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
5439 * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
5441 * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
5442 * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
5444 * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
5445 * from, see {@link Ext.Base}.
5451 baseStaticProperties = [],
5454 for (baseStaticProperty in Base) {
5455 if (Base.hasOwnProperty(baseStaticProperty)) {
5456 baseStaticProperties.push(baseStaticProperty);
5461 * @method constructor
5462 * Creates new class.
5463 * @param {Object} classData An object represent the properties of this class
5464 * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
5465 * Note that the creation process can be asynchronous depending on the pre-processors used.
5466 * @return {Ext.Base} The newly created class
5468 Ext.Class = Class = function(newClass, classData, onClassCreated) {
5469 if (typeof newClass != 'function') {
5470 onClassCreated = classData;
5471 classData = newClass;
5472 newClass = function() {
5473 return this.constructor.apply(this, arguments);
5481 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5482 registeredPreprocessors = Class.getPreprocessors(),
5485 preprocessor, staticPropertyName, process, i, j, ln;
5487 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5488 staticPropertyName = baseStaticProperties[i];
5489 newClass[staticPropertyName] = Base[staticPropertyName];
5492 delete classData.preprocessors;
5494 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5495 preprocessor = preprocessorStack[j];
5497 if (typeof preprocessor == 'string') {
5498 preprocessor = registeredPreprocessors[preprocessor];
5500 if (!preprocessor.always) {
5501 if (classData.hasOwnProperty(preprocessor.name)) {
5502 preprocessors.push(preprocessor.fn);
5506 preprocessors.push(preprocessor.fn);
5510 preprocessors.push(preprocessor);
5514 classData.onClassCreated = onClassCreated || Ext.emptyFn;
5516 classData.onBeforeClassCreated = function(cls, data) {
5517 onClassCreated = data.onClassCreated;
5519 delete data.onBeforeClassCreated;
5520 delete data.onClassCreated;
5522 cls.implement(data);
5524 onClassCreated.call(cls, cls);
5527 process = function(cls, data) {
5528 preprocessor = preprocessors[index++];
5530 if (!preprocessor) {
5531 data.onBeforeClassCreated.apply(this, arguments);
5535 if (preprocessor.call(this, cls, data, process) !== false) {
5536 process.apply(this, arguments);
5540 process.call(Class, newClass, classData);
5551 * Register a new pre-processor to be used during the class creation process
5554 * @param {String} name The pre-processor's name
5555 * @param {Function} fn The callback function to be executed. Typical format:
5557 * function(cls, data, fn) {
5560 * // Execute this when the processing is finished.
5561 * // Asynchronous processing is perfectly ok
5563 * fn.call(this, cls, data);
5567 * @param {Function} fn.cls The created class
5568 * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
5569 * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
5570 * regardless of whether the processing is synchronous or aynchronous
5572 * @return {Ext.Class} this
5575 registerPreprocessor: function(name, fn, always) {
5576 this.preprocessors[name] = {
5578 always: always || false,
5586 * Retrieve a pre-processor callback function by its name, which has been registered before
5588 * @param {String} name
5589 * @return {Function} preprocessor
5592 getPreprocessor: function(name) {
5593 return this.preprocessors[name];
5596 getPreprocessors: function() {
5597 return this.preprocessors;
5601 * Retrieve the array stack of default pre-processors
5603 * @return {Function[]} defaultPreprocessors
5606 getDefaultPreprocessors: function() {
5607 return this.defaultPreprocessors || [];
5611 * Set the default array stack of default pre-processors
5613 * @param {Function/Function[]} preprocessors
5614 * @return {Ext.Class} this
5617 setDefaultPreprocessors: function(preprocessors) {
5618 this.defaultPreprocessors = Ext.Array.from(preprocessors);
5624 * Inserts this pre-processor at a specific position in the stack, optionally relative to
5625 * any existing pre-processor. For example:
5627 * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5631 * fn.call(this, cls, data);
5633 * }).setDefaultPreprocessorPosition('debug', 'last');
5635 * @param {String} name The pre-processor name. Note that it needs to be registered with
5636 * {@link #registerPreprocessor registerPreprocessor} before this
5637 * @param {String} offset The insertion position. Four possible values are:
5638 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5639 * @param {String} relativeName
5640 * @return {Ext.Class} this
5643 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5644 var defaultPreprocessors = this.defaultPreprocessors,
5647 if (typeof offset == 'string') {
5648 if (offset === 'first') {
5649 defaultPreprocessors.unshift(name);
5653 else if (offset === 'last') {
5654 defaultPreprocessors.push(name);
5659 offset = (offset === 'after') ? 1 : -1;
5662 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5665 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5673 * @cfg {String} extend
5674 * The parent class that this class extends. For example:
5676 * Ext.define('Person', {
5677 * say: function(text) { alert(text); }
5680 * Ext.define('Developer', {
5682 * say: function(text) { this.callParent(["print "+text]); }
5685 Class.registerPreprocessor('extend', function(cls, data) {
5686 var extend = data.extend,
5688 basePrototype = base.prototype,
5689 prototype = function() {},
5690 parent, i, k, ln, staticName, parentStatics,
5691 parentPrototype, clsPrototype;
5693 if (extend && extend !== Object) {
5700 parentPrototype = parent.prototype;
5702 prototype.prototype = parentPrototype;
5703 clsPrototype = cls.prototype = new prototype();
5705 if (!('$class' in parent)) {
5706 for (i in basePrototype) {
5707 if (!parentPrototype[i]) {
5708 parentPrototype[i] = basePrototype[i];
5713 clsPrototype.self = cls;
5715 cls.superclass = clsPrototype.superclass = parentPrototype;
5719 //<feature classSystem.inheritableStatics>
5720 // Statics inheritance
5721 parentStatics = parentPrototype.$inheritableStatics;
5723 if (parentStatics) {
5724 for (k = 0, ln = parentStatics.length; k < ln; k++) {
5725 staticName = parentStatics[k];
5727 if (!cls.hasOwnProperty(staticName)) {
5728 cls[staticName] = parent[staticName];
5734 //<feature classSystem.config>
5735 // Merge the parent class' config object without referencing it
5736 if (parentPrototype.config) {
5737 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5740 clsPrototype.config = {};
5744 //<feature classSystem.onClassExtended>
5745 if (clsPrototype.$onExtended) {
5746 clsPrototype.$onExtended.call(cls, cls, data);
5749 if (data.onClassExtended) {
5750 clsPrototype.$onExtended = data.onClassExtended;
5751 delete data.onClassExtended;
5757 //<feature classSystem.statics>
5759 * @cfg {Object} statics
5760 * List of static methods for this class. For example:
5762 * Ext.define('Computer', {
5764 * factory: function(brand) {
5765 * // 'this' in static methods refer to the class itself
5766 * return new this(brand);
5770 * constructor: function() { ... }
5773 * var dellComputer = Computer.factory('Dell');
5775 Class.registerPreprocessor('statics', function(cls, data) {
5776 cls.addStatics(data.statics);
5778 delete data.statics;
5782 //<feature classSystem.inheritableStatics>
5784 * @cfg {Object} inheritableStatics
5785 * List of inheritable static methods for this class.
5786 * Otherwise just like {@link #statics} but subclasses inherit these methods.
5788 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5789 cls.addInheritableStatics(data.inheritableStatics);
5791 delete data.inheritableStatics;
5795 //<feature classSystem.config>
5797 * @cfg {Object} config
5798 * List of configuration options with their default values, for which automatically
5799 * accessor methods are generated. For example:
5801 * Ext.define('SmartPhone', {
5803 * hasTouchScreen: false,
5804 * operatingSystem: 'Other',
5807 * constructor: function(cfg) {
5808 * this.initConfig(cfg);
5812 * var iPhone = new SmartPhone({
5813 * hasTouchScreen: true,
5814 * operatingSystem: 'iOS'
5817 * iPhone.getPrice(); // 500;
5818 * iPhone.getOperatingSystem(); // 'iOS'
5819 * iPhone.getHasTouchScreen(); // true;
5820 * iPhone.hasTouchScreen(); // true
5822 Class.registerPreprocessor('config', function(cls, data) {
5823 var prototype = cls.prototype;
5825 Ext.Object.each(data.config, function(name) {
5826 var cName = name.charAt(0).toUpperCase() + name.substr(1),
5828 apply = 'apply' + cName,
5829 setter = 'set' + cName,
5830 getter = 'get' + cName;
5832 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5833 data[apply] = function(val) {
5838 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5839 data[setter] = function(val) {
5840 var ret = this[apply].call(this, val, this[pName]);
5842 if (typeof ret != 'undefined') {
5850 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5851 data[getter] = function() {
5857 Ext.Object.merge(prototype.config, data.config);
5862 //<feature classSystem.mixins>
5864 * @cfg {Object} mixins
5865 * List of classes to mix into this class. For example:
5867 * Ext.define('CanSing', {
5868 * sing: function() {
5869 * alert("I'm on the highway to hell...")
5873 * Ext.define('Musician', {
5877 * canSing: 'CanSing'
5881 Class.registerPreprocessor('mixins', function(cls, data) {
5882 var mixins = data.mixins,
5887 Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
5888 if (mixins instanceof Array) {
5889 for (i = 0,ln = mixins.length; i < ln; i++) {
5891 name = mixin.prototype.mixinId || mixin.$className;
5893 cls.mixin(name, mixin);
5897 for (name in mixins) {
5898 if (mixins.hasOwnProperty(name)) {
5899 cls.mixin(name, mixins[name]);
5908 Class.setDefaultPreprocessors([
5910 //<feature classSystem.statics>
5913 //<feature classSystem.inheritableStatics>
5914 ,'inheritableStatics'
5916 //<feature classSystem.config>
5919 //<feature classSystem.mixins>
5924 //<feature classSystem.backwardsCompatible>
5925 // Backwards compatible
5926 Ext.extend = function(subclass, superclass, members) {
5927 if (arguments.length === 2 && Ext.isObject(superclass)) {
5928 members = superclass;
5929 superclass = subclass;
5936 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5939 members.extend = superclass;
5940 members.preprocessors = [
5942 //<feature classSystem.statics>
5945 //<feature classSystem.inheritableStatics>
5946 ,'inheritableStatics'
5948 //<feature classSystem.mixins>
5951 //<feature classSystem.config>
5957 cls = new Class(subclass, members);
5960 cls = new Class(members);
5963 cls.prototype.override = function(o) {
5965 if (o.hasOwnProperty(m)) {
5978 * @author Jacky Nguyen <jacky@sencha.com>
5979 * @docauthor Jacky Nguyen <jacky@sencha.com>
5980 * @class Ext.ClassManager
5982 * Ext.ClassManager manages all classes and handles mapping from string class name to
5983 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5984 * these convenient shorthands:
5986 * - {@link Ext#define Ext.define}
5987 * - {@link Ext#create Ext.create}
5988 * - {@link Ext#widget Ext.widget}
5989 * - {@link Ext#getClass Ext.getClass}
5990 * - {@link Ext#getClassName Ext.getClassName}
5994 * Ext.define(className, properties);
5996 * in which `properties` is an object represent a collection of properties that apply to the class. See
5997 * {@link Ext.ClassManager#create} for more detailed instructions.
5999 * Ext.define('Person', {
6002 * constructor: function(name) {
6010 * eat: function(foodType) {
6011 * alert("I'm eating: " + foodType);
6017 * var aaron = new Person("Aaron");
6018 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
6020 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
6021 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
6025 * Ext.define('Developer', {
6028 * constructor: function(name, isGeek) {
6029 * this.isGeek = isGeek;
6031 * // Apply a method from the parent class' prototype
6032 * this.callParent([name]);
6038 * code: function(language) {
6039 * alert("I'm coding in: " + language);
6047 * var jacky = new Developer("Jacky", true);
6048 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
6049 * // alert("I'm eating: Bugs");
6051 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
6055 * Ext.define('CanPlayGuitar', {
6056 * playGuitar: function() {
6057 * alert("F#...G...D...A");
6061 * Ext.define('CanComposeSongs', {
6062 * composeSongs: function() { ... }
6065 * Ext.define('CanSing', {
6066 * sing: function() {
6067 * alert("I'm on the highway to hell...")
6071 * Ext.define('Musician', {
6075 * canPlayGuitar: 'CanPlayGuitar',
6076 * canComposeSongs: 'CanComposeSongs',
6077 * canSing: 'CanSing'
6081 * Ext.define('CoolPerson', {
6085 * canPlayGuitar: 'CanPlayGuitar',
6086 * canSing: 'CanSing'
6089 * sing: function() {
6090 * alert("Ahem....");
6092 * this.mixins.canSing.sing.call(this);
6094 * alert("[Playing guitar at the same time...]");
6096 * this.playGuitar();
6100 * var me = new CoolPerson("Jacky");
6102 * me.sing(); // alert("Ahem...");
6103 * // alert("I'm on the highway to hell...");
6104 * // alert("[Playing guitar at the same time...]");
6105 * // alert("F#...G...D...A");
6109 * Ext.define('SmartPhone', {
6111 * hasTouchScreen: false,
6112 * operatingSystem: 'Other',
6116 * isExpensive: false,
6118 * constructor: function(config) {
6119 * this.initConfig(config);
6124 * applyPrice: function(price) {
6125 * this.isExpensive = (price > 500);
6130 * applyOperatingSystem: function(operatingSystem) {
6131 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
6135 * return operatingSystem;
6139 * var iPhone = new SmartPhone({
6140 * hasTouchScreen: true,
6141 * operatingSystem: 'iOS'
6144 * iPhone.getPrice(); // 500;
6145 * iPhone.getOperatingSystem(); // 'iOS'
6146 * iPhone.getHasTouchScreen(); // true;
6147 * iPhone.hasTouchScreen(); // true
6149 * iPhone.isExpensive; // false;
6150 * iPhone.setPrice(600);
6151 * iPhone.getPrice(); // 600
6152 * iPhone.isExpensive; // true;
6154 * iPhone.setOperatingSystem('AlienOS');
6155 * iPhone.getOperatingSystem(); // 'Other'
6159 * Ext.define('Computer', {
6161 * factory: function(brand) {
6162 * // 'this' in static methods refer to the class itself
6163 * return new this(brand);
6167 * constructor: function() { ... }
6170 * var dellComputer = Computer.factory('Dell');
6172 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
6173 * static properties within class methods
6177 (function(Class, alias) {
6179 var slice = Array.prototype.slice;
6181 var Manager = Ext.ClassManager = {
6184 * @property {Object} classes
6185 * All classes which were defined through the ClassManager. Keys are the
6186 * name of the classes and the values are references to the classes.
6199 namespaceRewrites: [{
6208 alternateToName: {},
6214 enableNamespaceParseCache: true,
6217 namespaceParseCache: {},
6224 instantiationCounts: {},
6228 * Checks if a class has already been created.
6230 * @param {String} className
6231 * @return {Boolean} exist
6233 isCreated: function(className) {
6234 var i, ln, part, root, parts;
6237 if (typeof className !== 'string' || className.length < 1) {
6239 sourceClass: "Ext.ClassManager",
6240 sourceMethod: "exist",
6241 msg: "Invalid classname, must be a string and must not be empty"
6246 if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6251 parts = this.parseNamespace(className);
6253 for (i = 0, ln = parts.length; i < ln; i++) {
6256 if (typeof part !== 'string') {
6259 if (!root || !root[part]) {
6267 Ext.Loader.historyPush(className);
6269 this.existCache[className] = true;
6275 * Supports namespace rewriting
6278 parseNamespace: function(namespace) {
6280 if (typeof namespace !== 'string') {
6282 sourceClass: "Ext.ClassManager",
6283 sourceMethod: "parseNamespace",
6284 msg: "Invalid namespace, must be a string"
6289 var cache = this.namespaceParseCache;
6291 if (this.enableNamespaceParseCache) {
6292 if (cache.hasOwnProperty(namespace)) {
6293 return cache[namespace];
6298 rewrites = this.namespaceRewrites,
6299 rewrite, from, to, i, ln, root = Ext.global;
6301 for (i = 0, ln = rewrites.length; i < ln; i++) {
6302 rewrite = rewrites[i];
6303 from = rewrite.from;
6306 if (namespace === from || namespace.substring(0, from.length) === from) {
6307 namespace = namespace.substring(from.length);
6309 if (typeof to !== 'string') {
6312 parts = parts.concat(to.split('.'));
6321 parts = parts.concat(namespace.split('.'));
6323 if (this.enableNamespaceParseCache) {
6324 cache[namespace] = parts;
6331 * Creates a namespace and assign the `value` to the created object
6333 * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6335 * alert(MyCompany.pkg.Example === someObject); // alerts true
6337 * @param {String} name
6338 * @param {Object} value
6340 setNamespace: function(name, value) {
6341 var root = Ext.global,
6342 parts = this.parseNamespace(name),
6343 ln = parts.length - 1,
6347 for (i = 0; i < ln; i++) {
6350 if (typeof part !== 'string') {
6367 * The new Ext.ns, supports namespace rewriting
6370 createNamespaces: function() {
6371 var root = Ext.global,
6372 parts, part, i, j, ln, subLn;
6374 for (i = 0, ln = arguments.length; i < ln; i++) {
6375 parts = this.parseNamespace(arguments[i]);
6377 for (j = 0, subLn = parts.length; j < subLn; j++) {
6380 if (typeof part !== 'string') {
6396 * Sets a name reference to a class.
6398 * @param {String} name
6399 * @param {Object} value
6400 * @return {Ext.ClassManager} this
6402 set: function(name, value) {
6403 var targetName = this.getName(value);
6405 this.classes[name] = this.setNamespace(name, value);
6407 if (targetName && targetName !== name) {
6408 this.maps.alternateToName[name] = targetName;
6415 * Retrieve a class by its name.
6417 * @param {String} name
6418 * @return {Ext.Class} class
6420 get: function(name) {
6421 if (this.classes.hasOwnProperty(name)) {
6422 return this.classes[name];
6425 var root = Ext.global,
6426 parts = this.parseNamespace(name),
6429 for (i = 0, ln = parts.length; i < ln; i++) {
6432 if (typeof part !== 'string') {
6435 if (!root || !root[part]) {
6447 * Register the alias for a class.
6449 * @param {Ext.Class/String} cls a reference to a class or a className
6450 * @param {String} alias Alias to use when referring to this class
6452 setAlias: function(cls, alias) {
6453 var aliasToNameMap = this.maps.aliasToName,
6454 nameToAliasesMap = this.maps.nameToAliases,
6457 if (typeof cls === 'string') {
6460 className = this.getName(cls);
6463 if (alias && aliasToNameMap[alias] !== className) {
6465 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
6466 Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
6467 "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
6471 aliasToNameMap[alias] = className;
6474 if (!nameToAliasesMap[className]) {
6475 nameToAliasesMap[className] = [];
6479 Ext.Array.include(nameToAliasesMap[className], alias);
6486 * Get a reference to the class by its alias.
6488 * @param {String} alias
6489 * @return {Ext.Class} class
6491 getByAlias: function(alias) {
6492 return this.get(this.getNameByAlias(alias));
6496 * Get the name of a class by its alias.
6498 * @param {String} alias
6499 * @return {String} className
6501 getNameByAlias: function(alias) {
6502 return this.maps.aliasToName[alias] || '';
6506 * Get the name of a class by its alternate name.
6508 * @param {String} alternate
6509 * @return {String} className
6511 getNameByAlternate: function(alternate) {
6512 return this.maps.alternateToName[alternate] || '';
6516 * Get the aliases of a class by the class name
6518 * @param {String} name
6519 * @return {String[]} aliases
6521 getAliasesByName: function(name) {
6522 return this.maps.nameToAliases[name] || [];
6526 * Get the name of the class by its reference or its instance.
6528 * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6530 * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6532 * @param {Ext.Class/Object} object
6533 * @return {String} className
6535 getName: function(object) {
6536 return object && object.$className || '';
6540 * Get the class of the provided object; returns null if it's not an instance
6541 * of any class created with Ext.define.
6543 * var component = new Ext.Component();
6545 * Ext.ClassManager.getClass(component); // returns Ext.Component
6547 * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6549 * @param {Object} object
6550 * @return {Ext.Class} class
6552 getClass: function(object) {
6553 return object && object.self || null;
6559 * {@link Ext#define Ext.define} and {@link Ext.ClassManager#create Ext.ClassManager.create} are almost aliases
6560 * of each other, with the only exception that Ext.define allows definition of {@link Ext.Class#override overrides}.
6561 * To avoid trouble, always use Ext.define.
6563 * Ext.define('My.awesome.Class', {
6564 * someProperty: 'something',
6565 * someMethod: function() { ... }
6569 * alert('Created!');
6570 * alert(this === My.awesome.Class); // alerts true
6572 * var myInstance = new this();
6575 * @param {String} className The class name to create in string dot-namespaced format, for example:
6576 * `My.very.awesome.Class`, `FeedViewer.plugin.CoolPager`. It is highly recommended to follow this simple convention:
6578 * - The root and the class name are 'CamelCased'
6579 * - Everything else is lower-cased
6581 * @param {Object} data The key-value pairs of properties to apply to this class. Property names can be of any valid
6582 * strings, except those in the reserved list below:
6584 * - {@link Ext.Base#self self}
6585 * - {@link Ext.Class#alias alias}
6586 * - {@link Ext.Class#alternateClassName alternateClassName}
6587 * - {@link Ext.Class#config config}
6588 * - {@link Ext.Class#extend extend}
6589 * - {@link Ext.Class#inheritableStatics inheritableStatics}
6590 * - {@link Ext.Class#mixins mixins}
6591 * - {@link Ext.Class#override override} (only when using {@link Ext#define Ext.define})
6592 * - {@link Ext.Class#requires requires}
6593 * - {@link Ext.Class#singleton singleton}
6594 * - {@link Ext.Class#statics statics}
6595 * - {@link Ext.Class#uses uses}
6597 * @param {Function} [createdFn] callback to execute after the class is created, the execution scope of which
6598 * (`this`) will be the newly created class itself.
6600 * @return {Ext.Base}
6602 create: function(className, data, createdFn) {
6606 if (typeof className !== 'string') {
6609 sourceMethod: "define",
6610 msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6615 data.$className = className;
6617 return new Class(data, function() {
6618 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6619 registeredPostprocessors = manager.postprocessors,
6621 postprocessors = [],
6622 postprocessor, process, i, ln;
6624 delete data.postprocessors;
6626 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6627 postprocessor = postprocessorStack[i];
6629 if (typeof postprocessor === 'string') {
6630 postprocessor = registeredPostprocessors[postprocessor];
6632 if (!postprocessor.always) {
6633 if (data[postprocessor.name] !== undefined) {
6634 postprocessors.push(postprocessor.fn);
6638 postprocessors.push(postprocessor.fn);
6642 postprocessors.push(postprocessor);
6646 process = function(clsName, cls, clsData) {
6647 postprocessor = postprocessors[index++];
6649 if (!postprocessor) {
6650 manager.set(className, cls);
6652 Ext.Loader.historyPush(className);
6655 createdFn.call(cls, cls);
6661 if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6662 process.apply(this, arguments);
6666 process.call(manager, className, this, data);
6671 * Instantiate a class by its alias.
6673 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6674 * attempt to load the class via synchronous loading.
6676 * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6678 * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6680 * @param {String} alias
6681 * @param {Object...} args Additional arguments after the alias will be passed to the
6682 * class constructor.
6683 * @return {Object} instance
6685 instantiateByAlias: function() {
6686 var alias = arguments[0],
6687 args = slice.call(arguments),
6688 className = this.getNameByAlias(alias);
6691 className = this.maps.aliasToName[alias];
6697 sourceMethod: "createByAlias",
6698 msg: "Cannot create an instance of unrecognized alias: " + alias
6704 if (Ext.global.console) {
6705 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6706 "Ext.require('" + alias + "') above Ext.onReady");
6710 Ext.syncRequire(className);
6713 args[0] = className;
6715 return this.instantiate.apply(this, args);
6719 * Instantiate a class by either full name, alias or alternate name.
6721 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6722 * attempt to load the class via synchronous loading.
6724 * For example, all these three lines return the same result:
6727 * var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6730 * var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6732 * // full class name
6733 * var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6735 * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6737 * @param {String} name
6738 * @param {Object...} args Additional arguments after the name will be passed to the class' constructor.
6739 * @return {Object} instance
6741 instantiate: function() {
6742 var name = arguments[0],
6743 args = slice.call(arguments, 1),
6747 if (typeof name !== 'function') {
6749 if ((typeof name !== 'string' || name.length < 1)) {
6752 sourceMethod: "create",
6753 msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6758 cls = this.get(name);
6764 // No record of this class name, it's possibly an alias, so look it up
6766 possibleName = this.getNameByAlias(name);
6769 name = possibleName;
6771 cls = this.get(name);
6775 // Still no record of this class name, it's possibly an alternate name, so look it up
6777 possibleName = this.getNameByAlternate(name);
6780 name = possibleName;
6782 cls = this.get(name);
6786 // Still not existing at this point, try to load it via synchronous mode as the last resort
6789 if (Ext.global.console) {
6790 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6791 "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6795 Ext.syncRequire(name);
6797 cls = this.get(name);
6804 sourceMethod: "create",
6805 msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6809 if (typeof cls !== 'function') {
6812 sourceMethod: "create",
6813 msg: "'" + name + "' is a singleton and cannot be instantiated"
6819 if (!this.instantiationCounts[name]) {
6820 this.instantiationCounts[name] = 0;
6823 this.instantiationCounts[name]++;
6826 return this.getInstantiator(args.length)(cls, args);
6834 dynInstantiate: function(name, args) {
6835 args = Ext.Array.from(args, true);
6838 return this.instantiate.apply(this, args);
6845 getInstantiator: function(length) {
6846 if (!this.instantiators[length]) {
6850 for (i = 0; i < length; i++) {
6851 args.push('a['+i+']');
6854 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6857 return this.instantiators[length];
6868 defaultPostprocessors: [],
6871 * Register a post-processor function.
6873 * @param {String} name
6874 * @param {Function} postprocessor
6876 registerPostprocessor: function(name, fn, always) {
6877 this.postprocessors[name] = {
6879 always: always || false,
6887 * Set the default post processors array stack which are applied to every class.
6889 * @param {String/String[]} The name of a registered post processor or an array of registered names.
6890 * @return {Ext.ClassManager} this
6892 setDefaultPostprocessors: function(postprocessors) {
6893 this.defaultPostprocessors = Ext.Array.from(postprocessors);
6899 * Insert this post-processor at a specific position in the stack, optionally relative to
6900 * any existing post-processor
6902 * @param {String} name The post-processor name. Note that it needs to be registered with
6903 * {@link Ext.ClassManager#registerPostprocessor} before this
6904 * @param {String} offset The insertion position. Four possible values are:
6905 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6906 * @param {String} relativeName
6907 * @return {Ext.ClassManager} this
6909 setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6910 var defaultPostprocessors = this.defaultPostprocessors,
6913 if (typeof offset === 'string') {
6914 if (offset === 'first') {
6915 defaultPostprocessors.unshift(name);
6919 else if (offset === 'last') {
6920 defaultPostprocessors.push(name);
6925 offset = (offset === 'after') ? 1 : -1;
6928 index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6931 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6938 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6939 * or class names. Expressions support wildcards:
6941 * // returns ['Ext.window.Window']
6942 * var window = Ext.ClassManager.getNamesByExpression('widget.window');
6944 * // returns ['widget.panel', 'widget.window', ...]
6945 * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6947 * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6948 * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6950 * @param {String} expression
6951 * @return {String[]} classNames
6953 getNamesByExpression: function(expression) {
6954 var nameToAliasesMap = this.maps.nameToAliases,
6956 name, alias, aliases, possibleName, regex, i, ln;
6959 if (typeof expression !== 'string' || expression.length < 1) {
6961 sourceClass: "Ext.ClassManager",
6962 sourceMethod: "getNamesByExpression",
6963 msg: "Expression " + expression + " is invalid, must be a non-empty string"
6968 if (expression.indexOf('*') !== -1) {
6969 expression = expression.replace(/\*/g, '(.*?)');
6970 regex = new RegExp('^' + expression + '$');
6972 for (name in nameToAliasesMap) {
6973 if (nameToAliasesMap.hasOwnProperty(name)) {
6974 aliases = nameToAliasesMap[name];
6976 if (name.search(regex) !== -1) {
6980 for (i = 0, ln = aliases.length; i < ln; i++) {
6983 if (alias.search(regex) !== -1) {
6993 possibleName = this.getNameByAlias(expression);
6996 names.push(possibleName);
6998 possibleName = this.getNameByAlternate(expression);
7001 names.push(possibleName);
7003 names.push(expression);
7012 var defaultPostprocessors = Manager.defaultPostprocessors;
7013 //<feature classSystem.alias>
7016 * @cfg {String[]} alias
7018 * List of short aliases for class names. Most useful for defining xtypes for widgets:
7020 * Ext.define('MyApp.CoolPanel', {
7021 * extend: 'Ext.panel.Panel',
7022 * alias: ['widget.coolpanel'],
7026 * // Using Ext.create
7027 * Ext.widget('widget.coolpanel');
7028 * // Using the shorthand for widgets and in xtypes
7029 * Ext.widget('panel', {
7031 * {xtype: 'coolpanel', html: 'Foo'},
7032 * {xtype: 'coolpanel', html: 'Bar'}
7036 Manager.registerPostprocessor('alias', function(name, cls, data) {
7037 var aliases = data.alias,
7042 for (i = 0, ln = aliases.length; i < ln; i++) {
7045 this.setAlias(cls, alias);
7050 * @cfg {Boolean} singleton
7052 * When set to true, the class will be instantiated as singleton. For example:
7054 * Ext.define('Logger', {
7056 * log: function(msg) {
7061 * Logger.log('Hello');
7063 Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
7064 fn.call(this, name, new cls(), data);
7069 * @cfg {String/String[]} alternateClassName
7071 * Defines alternate names for this class. For example:
7073 * Ext.define('Developer', {
7074 * alternateClassName: ['Coder', 'Hacker'],
7075 * code: function(msg) {
7076 * alert('Typing... ' + msg);
7080 * var joe = Ext.create('Developer');
7081 * joe.code('stackoverflow');
7083 * var rms = Ext.create('Hacker');
7084 * rms.code('hack hack');
7086 Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
7087 var alternates = data.alternateClassName,
7090 if (!(alternates instanceof Array)) {
7091 alternates = [alternates];
7094 for (i = 0, ln = alternates.length; i < ln; i++) {
7095 alternate = alternates[i];
7098 if (typeof alternate !== 'string') {
7101 sourceMethod: "define",
7102 msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
7107 this.set(alternate, cls);
7111 Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
7117 * @alias Ext.ClassManager#instantiate
7119 create: alias(Manager, 'instantiate'),
7123 * API to be stablized
7125 * @param {Object} item
7126 * @param {String} namespace
7128 factory: function(item, namespace) {
7129 if (item instanceof Array) {
7132 for (i = 0, ln = item.length; i < ln; i++) {
7133 item[i] = Ext.factory(item[i], namespace);
7139 var isString = (typeof item === 'string');
7141 if (isString || (item instanceof Object && item.constructor === Object)) {
7142 var name, config = {};
7148 name = item.className;
7150 delete config.className;
7153 if (namespace !== undefined && name.indexOf(namespace) === -1) {
7154 name = namespace + '.' + Ext.String.capitalize(name);
7157 return Ext.create(name, config);
7160 if (typeof item === 'function') {
7161 return Ext.create(item);
7168 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
7170 * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
7171 * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
7175 * @param {String} name xtype of the widget to create.
7176 * @param {Object...} args arguments for the widget constructor.
7177 * @return {Object} widget instance
7179 widget: function(name) {
7180 var args = slice.call(arguments);
7181 args[0] = 'widget.' + name;
7183 return Manager.instantiateByAlias.apply(Manager, args);
7189 * @alias Ext.ClassManager#instantiateByAlias
7191 createByAlias: alias(Manager, 'instantiateByAlias'),
7194 * @cfg {String} override
7197 * Defines an override applied to a class. Note that **overrides can only be created using
7198 * {@link Ext#define}.** {@link Ext.ClassManager#create} only creates classes.
7200 * To define an override, include the override property. The content of an override is
7201 * aggregated with the specified class in order to extend or modify that class. This can be
7202 * as simple as setting default property values or it can extend and/or replace methods.
7203 * This can also extend the statics of the class.
7205 * One use for an override is to break a large class into manageable pieces.
7207 * // File: /src/app/Panel.js
7209 * Ext.define('My.app.Panel', {
7210 * extend: 'Ext.panel.Panel',
7212 * 'My.app.PanelPart2',
7213 * 'My.app.PanelPart3'
7216 * constructor: function (config) {
7217 * this.callSuper(arguments); // calls Ext.panel.Panel's constructor
7222 * method: function () {
7228 * // File: /src/app/PanelPart2.js
7229 * Ext.define('My.app.PanelPart2', {
7230 * override: 'My.app.Panel',
7232 * constructor: function (config) {
7233 * this.callSuper(arguments); // calls My.app.Panel's constructor
7238 * Another use of overrides is to provide optional parts of classes that can be
7239 * independently required. In this case, the class may even be unaware of the
7240 * override altogether.
7242 * Ext.define('My.ux.CoolTip', {
7243 * override: 'Ext.tip.ToolTip',
7245 * constructor: function (config) {
7246 * this.callSuper(arguments); // calls Ext.tip.ToolTip's constructor
7251 * The above override can now be required as normal.
7253 * Ext.define('My.app.App', {
7259 * Overrides can also contain statics:
7261 * Ext.define('My.app.BarMod', {
7262 * override: 'Ext.foo.Bar',
7265 * method: function (x) {
7266 * return this.callSuper([x * 2]); // call Ext.foo.Bar.method
7271 * IMPORTANT: An override is only included in a build if the class it overrides is
7272 * required. Otherwise, the override, like the target class, is not included.
7279 * @alias Ext.ClassManager#create
7281 define: function (className, data, createdFn) {
7282 if (!data.override) {
7283 return Manager.create.apply(Manager, arguments);
7286 var requires = data.requires,
7288 overrideName = className;
7290 className = data.override;
7292 // hoist any 'requires' or 'uses' from the body onto the faux class:
7293 data = Ext.apply({}, data);
7294 delete data.requires;
7296 delete data.override;
7298 // make sure className is in the requires list:
7299 if (typeof requires == 'string') {
7300 requires = [ className, requires ];
7301 } else if (requires) {
7302 requires = requires.slice(0);
7303 requires.unshift(className);
7305 requires = [ className ];
7308 // TODO - we need to rework this to allow the override to not require the target class
7309 // and rather 'wait' for it in such a way that if the target class is not in the build,
7310 // neither are any of its overrides.
7312 // Also, this should process the overrides for a class ASAP (ideally before any derived
7313 // classes) if the target class 'requires' the overrides. Without some special handling, the
7314 // overrides so required will be processed before the class and have to be bufferred even
7317 // TODO - we should probably support the "config" processor on an override (to config new
7318 // functionaliy like Aria) and maybe inheritableStatics (although static is now supported
7319 // by callSuper). If inheritableStatics causes those statics to be included on derived class
7320 // constructors, that probably means "no" to this since an override can come after other
7321 // classes extend the target.
7322 return Manager.create(overrideName, {
7326 constructor: function () {
7328 throw new Error("Cannot create override '" + overrideName + "'");
7332 var cls = Manager.get(className);
7333 if (cls.override) { // if (normal class)
7335 } else { // else (singleton)
7336 cls.self.override(data);
7340 // called once the override is applied and with the context of the
7341 // overridden class (the override itself is a meaningless, name-only
7343 createdFn.call(cls);
7351 * @alias Ext.ClassManager#getName
7353 getClassName: alias(Manager, 'getName'),
7356 * Returns the displayName property or className or object.
7357 * When all else fails, returns "Anonymous".
7358 * @param {Object} object
7361 getDisplayName: function(object) {
7362 if (object.displayName) {
7363 return object.displayName;
7366 if (object.$name && object.$class) {
7367 return Ext.getClassName(object.$class) + '#' + object.$name;
7370 if (object.$className) {
7371 return object.$className;
7380 * @alias Ext.ClassManager#getClass
7382 getClass: alias(Manager, 'getClass'),
7385 * Creates namespaces to be used for scoping variables and classes so that they are not global.
7386 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
7388 * Ext.namespace('Company', 'Company.data');
7390 * // equivalent and preferable to the above syntax
7391 * Ext.namespace('Company.data');
7393 * Company.Widget = function() { ... };
7395 * Company.data.CustomStore = function(config) { ... };
7399 * @param {String} namespace1
7400 * @param {String} namespace2
7401 * @param {String} etc
7402 * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7404 namespace: alias(Manager, 'createNamespaces')
7408 * Old name for {@link Ext#widget}.
7409 * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7414 Ext.createWidget = Ext.widget;
7417 * Convenient alias for {@link Ext#namespace Ext.namespace}
7420 * @alias Ext#namespace
7422 Ext.ns = Ext.namespace;
7424 Class.registerPreprocessor('className', function(cls, data) {
7425 if (data.$className) {
7426 cls.$className = data.$className;
7428 cls.displayName = cls.$className;
7433 Class.setDefaultPreprocessorPosition('className', 'first');
7435 Class.registerPreprocessor('xtype', function(cls, data) {
7436 var xtypes = Ext.Array.from(data.xtype),
7437 widgetPrefix = 'widget.',
7438 aliases = Ext.Array.from(data.alias),
7441 data.xtype = xtypes[0];
7442 data.xtypes = xtypes;
7444 aliases = data.alias = Ext.Array.from(data.alias);
7446 for (i = 0,ln = xtypes.length; i < ln; i++) {
7450 if (typeof xtype != 'string' || xtype.length < 1) {
7451 throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
7455 aliases.push(widgetPrefix + xtype);
7458 data.alias = aliases;
7461 Class.setDefaultPreprocessorPosition('xtype', 'last');
7463 Class.registerPreprocessor('alias', function(cls, data) {
7464 var aliases = Ext.Array.from(data.alias),
7465 xtypes = Ext.Array.from(data.xtypes),
7466 widgetPrefix = 'widget.',
7467 widgetPrefixLength = widgetPrefix.length,
7468 i, ln, alias, xtype;
7470 for (i = 0, ln = aliases.length; i < ln; i++) {
7474 if (typeof alias != 'string') {
7475 throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
7479 if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
7480 xtype = alias.substring(widgetPrefixLength);
7481 Ext.Array.include(xtypes, xtype);
7484 cls.xtype = data.xtype = xtype;
7489 data.alias = aliases;
7490 data.xtypes = xtypes;
7493 Class.setDefaultPreprocessorPosition('alias', 'last');
7495 })(Ext.Class, Ext.Function.alias);
7500 * @author Jacky Nguyen <jacky@sencha.com>
7501 * @docauthor Jacky Nguyen <jacky@sencha.com>
7503 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7504 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7505 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7508 * # Asynchronous Loading
7512 * + No web server needed: you can run the application via the file system protocol
7513 * (i.e: `file://path/to/your/index.html`)
7514 * + Best possible debugging experience: error messages come with the exact file name and line number
7517 * + Dependencies need to be specified before-hand
7519 * ### Method 1: Explicitly include what you need:
7522 * Ext.require({String/Array} expressions);
7524 * // Example: Single alias
7525 * Ext.require('widget.window');
7527 * // Example: Single class name
7528 * Ext.require('Ext.window.Window');
7530 * // Example: Multiple aliases / class names mix
7531 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7534 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7536 * ### Method 2: Explicitly exclude what you don't need:
7538 * // Syntax: Note that it must be in this chaining format.
7539 * Ext.exclude({String/Array} expressions)
7540 * .require({String/Array} expressions);
7542 * // Include everything except Ext.data.*
7543 * Ext.exclude('Ext.data.*').require('*');Â
7545 * // Include all widgets except widget.checkbox*,
7546 * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7547 * Ext.exclude('widget.checkbox*').require('widget.*');
7549 * # Synchronous Loading on Demand
7552 * + There's no need to specify dependencies before-hand, which is always the convenience of including
7556 * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7557 * + Must be from the same domain due to XHR restriction
7558 * + Need a web server, same reason as above
7560 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7562 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7564 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7566 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7568 * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7569 * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7570 * the given class and all its dependencies.
7572 * # Hybrid Loading - The Best of Both Worlds
7574 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7576 * ### Step 1: Start writing your application using synchronous approach.
7578 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7580 * Ext.onReady(function(){
7581 * var window = Ext.createWidget('window', {
7588 * title: 'Hello Dialog',
7590 * title: 'Navigation',
7591 * collapsible: true,
7597 * title: 'TabPanel',
7605 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7607 * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7608 * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7610 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7612 * Ext.require('Ext.window.Window');
7613 * Ext.require('Ext.layout.container.Border');
7617 * Everything should now load via asynchronous mode.
7621 * It's important to note that dynamic loading should only be used during development on your local machines.
7622 * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7623 * the whole process of transitioning from / to between development / maintenance and production as easy as
7624 * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7625 * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7626 * array into one, then include it on top of your application.
7628 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7630 (function(Manager, Class, flexSetter, alias) {
7634 isNonBrowser = typeof window === 'undefined',
7635 isNodeJS = isNonBrowser && (typeof require === 'function'),
7636 isPhantomJS = (typeof phantom !== 'undefined' && phantom.fs),
7638 dependencyProperties = ['extend', 'mixins', 'requires'],
7641 Loader = Ext.Loader = {
7645 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7648 * Flag indicating whether there are still files being loaded
7654 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7656 * requires: [...], // The required classes for this queue item
7657 * callback: function() { ... } // The function to execute when all classes specified in requires exist
7664 * Maintain the list of files that have already been handled so that they never get double-loaded
7670 * Maintain the list of listeners to execute when all required scripts are fully loaded
7676 * Contains optional dependencies to be loaded last
7679 optionalRequires: [],
7682 * Map of fully qualified class names to an array of dependent classes.
7698 hasFileLoadError: false,
7703 classNameToFilePathMap: {},
7706 * @property {String[]} history
7707 * An array of class names to keep track of the dependency loading order.
7708 * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7718 * @cfg {Boolean} enabled
7719 * Whether or not to enable the dynamic dependency loading feature.
7724 * @cfg {Boolean} disableCaching
7725 * Appends current timestamp to script files to prevent caching.
7727 disableCaching: true,
7730 * @cfg {String} disableCachingParam
7731 * The get parameter name for the cache buster's timestamp.
7733 disableCachingParam: '_dc',
7736 * @cfg {Object} paths
7737 * The mapping from namespaces to file paths
7740 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7741 * // loaded from ./layout/Container.js
7743 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
7744 * // ./src/my_own_folder/layout/Container.js
7747 * Note that all relative paths are relative to the current HTML document.
7748 * If not being specified, for example, `Other.awesome.Class`
7749 * will simply be loaded from `./Other/awesome/Class.js`
7757 * Set the configuration for the loader. This should be called right after ext-core.js
7758 * (or ext-core-debug.js) is included in the page, e.g.:
7760 * <script type="text/javascript" src="ext-core-debug.js"></script>
7761 * <script type="text/javascript">
7762 * Ext.Loader.setConfig({
7765 * 'My': 'my_own_path'
7769 * <script type="text/javascript">
7772 * Ext.onReady(function() {
7773 * // application code here
7777 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7779 * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
7780 * @param {Object} value (optional) The new value to set, needed if first parameter is String.
7781 * @return {Ext.Loader} this
7783 setConfig: function(name, value) {
7784 if (Ext.isObject(name) && arguments.length === 1) {
7785 Ext.Object.merge(this.config, name);
7788 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7795 * Get the config value corresponding to the specified name.
7796 * If no name is given, will return the config object.
7797 * @param {String} name The config property name
7800 getConfig: function(name) {
7802 return this.config[name];
7809 * Sets the path of a namespace. For Example:
7811 * Ext.Loader.setPath('Ext', '.');
7813 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7814 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7815 * @return {Ext.Loader} this
7818 setPath: flexSetter(function(name, path) {
7822 path = require('fs').realpathSync(path);
7826 this.config.paths[name] = path;
7832 * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7835 * Ext.Loader.setPath('My', '/path/to/My');
7837 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7839 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7841 * Ext.Loader.setPath({
7842 * 'My': '/path/to/lib',
7843 * 'My.awesome': '/other/path/for/awesome/stuff',
7844 * 'My.awesome.more': '/more/awesome/path'
7847 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7849 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7851 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7853 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7855 * @param {String} className
7856 * @return {String} path
7858 getPath: function(className) {
7860 paths = this.config.paths,
7861 prefix = this.getPrefix(className);
7863 if (prefix.length > 0) {
7864 if (prefix === className) {
7865 return paths[prefix];
7868 path = paths[prefix];
7869 className = className.substring(prefix.length + 1);
7872 if (path.length > 0) {
7876 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7881 * @param {String} className
7883 getPrefix: function(className) {
7884 var paths = this.config.paths,
7885 prefix, deepestPrefix = '';
7887 if (paths.hasOwnProperty(className)) {
7891 for (prefix in paths) {
7892 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7893 if (prefix.length > deepestPrefix.length) {
7894 deepestPrefix = prefix;
7899 return deepestPrefix;
7903 * Refresh all items in the queue. If all dependencies for an item exist during looping,
7904 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7908 refreshQueue: function() {
7909 var ln = this.queue.length,
7910 i, item, j, requires;
7913 this.triggerReady();
7917 for (i = 0; i < ln; i++) {
7918 item = this.queue[i];
7921 requires = item.requires;
7923 // Don't bother checking when the number of files loaded
7924 // is still less than the array length
7925 if (requires.length > this.numLoadedFiles) {
7932 if (Manager.isCreated(requires[j])) {
7933 // Take out from the queue
7934 Ext.Array.erase(requires, j, 1);
7939 } while (j < requires.length);
7941 if (item.requires.length === 0) {
7942 Ext.Array.erase(this.queue, i, 1);
7943 item.callback.call(item.scope);
7944 this.refreshQueue();
7954 * Inject a script element to document's head, call onLoad and onError accordingly
7957 injectScriptElement: function(url, onLoad, onError, scope) {
7958 var script = document.createElement('script'),
7960 onLoadFn = function() {
7961 me.cleanupScriptElement(script);
7964 onErrorFn = function() {
7965 me.cleanupScriptElement(script);
7966 onError.call(scope);
7969 script.type = 'text/javascript';
7971 script.onload = onLoadFn;
7972 script.onerror = onErrorFn;
7973 script.onreadystatechange = function() {
7974 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7979 this.documentHead.appendChild(script);
7987 cleanupScriptElement: function(script) {
7988 script.onload = null;
7989 script.onreadystatechange = null;
7990 script.onerror = null;
7996 * Load a script file, supports both asynchronous and synchronous approaches
7998 * @param {String} url
7999 * @param {Function} onLoad
8000 * @param {Object} scope
8001 * @param {Boolean} synchronous
8004 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
8006 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
8007 fileName = url.split('/').pop(),
8008 isCrossOriginRestricted = false,
8009 xhr, status, onScriptError;
8011 scope = scope || this;
8013 this.isLoading = true;
8016 onScriptError = function() {
8017 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
8020 if (!Ext.isReady && Ext.onDocumentReady) {
8021 Ext.onDocumentReady(function() {
8022 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
8026 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
8030 if (typeof XMLHttpRequest !== 'undefined') {
8031 xhr = new XMLHttpRequest();
8033 xhr = new ActiveXObject('Microsoft.XMLHTTP');
8037 xhr.open('GET', noCacheUrl, false);
8040 isCrossOriginRestricted = true;
8043 status = (xhr.status === 1223) ? 204 : xhr.status;
8045 if (!isCrossOriginRestricted) {
8046 isCrossOriginRestricted = (status === 0);
8049 if (isCrossOriginRestricted
8054 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
8055 "being loaded from a different domain or from the local file system whereby cross origin " +
8056 "requests are not allowed due to security reasons. Use asynchronous loading with " +
8057 "Ext.require instead.", synchronous);
8059 else if (status >= 200 && status < 300
8064 // Firebug friendly, file names are still shown even though they're eval'ed code
8065 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
8070 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
8071 "verify that the file exists. " +
8072 "XHR status code: " + status, synchronous);
8075 // Prevent potential IE memory leak
8081 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
8082 * Can be chained with more `require` and `exclude` methods, e.g.:
8084 * Ext.exclude('Ext.data.*').require('*');
8086 * Ext.exclude('widget.button*').require('widget.*');
8088 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
8090 * @param {String/String[]} excludes
8091 * @return {Object} object contains `require` method for chaining
8093 exclude: function(excludes) {
8097 require: function(expressions, fn, scope) {
8098 return me.require(expressions, fn, scope, excludes);
8101 syncRequire: function(expressions, fn, scope) {
8102 return me.syncRequire(expressions, fn, scope, excludes);
8108 * Synchronously loads all classes by the given names and all their direct dependencies;
8109 * optionally executes the given callback function when finishes, within the optional scope.
8111 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
8113 * @param {String/String[]} expressions Can either be a string or an array of string
8114 * @param {Function} fn (Optional) The callback function
8115 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8116 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8118 syncRequire: function() {
8119 this.syncModeEnabled = true;
8120 this.require.apply(this, arguments);
8121 this.refreshQueue();
8122 this.syncModeEnabled = false;
8126 * Loads all classes by the given names and all their direct dependencies;
8127 * optionally executes the given callback function when finishes, within the optional scope.
8129 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
8131 * @param {String/String[]} expressions Can either be a string or an array of string
8132 * @param {Function} fn (Optional) The callback function
8133 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8134 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8136 require: function(expressions, fn, scope, excludes) {
8137 var filePath, expression, exclude, className, excluded = {},
8138 excludedClassNames = [],
8139 possibleClassNames = [],
8140 possibleClassName, classNames = [],
8143 expressions = Ext.Array.from(expressions);
8144 excludes = Ext.Array.from(excludes);
8146 fn = fn || Ext.emptyFn;
8148 scope = scope || Ext.global;
8150 for (i = 0, ln = excludes.length; i < ln; i++) {
8151 exclude = excludes[i];
8153 if (typeof exclude === 'string' && exclude.length > 0) {
8154 excludedClassNames = Manager.getNamesByExpression(exclude);
8156 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
8157 excluded[excludedClassNames[j]] = true;
8162 for (i = 0, ln = expressions.length; i < ln; i++) {
8163 expression = expressions[i];
8165 if (typeof expression === 'string' && expression.length > 0) {
8166 possibleClassNames = Manager.getNamesByExpression(expression);
8168 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
8169 possibleClassName = possibleClassNames[j];
8171 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
8172 Ext.Array.include(classNames, possibleClassName);
8178 // If the dynamic dependency feature is not being used, throw an error
8179 // if the dependencies are not defined
8180 if (!this.config.enabled) {
8181 if (classNames.length > 0) {
8183 sourceClass: "Ext.Loader",
8184 sourceMethod: "require",
8185 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
8186 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
8191 if (classNames.length === 0) {
8197 requires: classNames,
8202 classNames = classNames.slice();
8204 for (i = 0, ln = classNames.length; i < ln; i++) {
8205 className = classNames[i];
8207 if (!this.isFileLoaded.hasOwnProperty(className)) {
8208 this.isFileLoaded[className] = false;
8210 filePath = this.getPath(className);
8212 this.classNameToFilePathMap[className] = filePath;
8214 this.numPendingFiles++;
8221 // Temporary support for hammerjs
8223 var f = fs.open(filePath),
8228 line = f.readLine();
8229 if (line.length === 0) {
8239 this.onFileLoaded(className, filePath);
8242 return Manager.get(className);
8248 this.loadScriptFile(
8250 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
8251 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
8253 this.syncModeEnabled
8263 * @param {String} className
8264 * @param {String} filePath
8266 onFileLoaded: function(className, filePath) {
8267 this.numLoadedFiles++;
8269 this.isFileLoaded[className] = true;
8271 this.numPendingFiles--;
8273 if (this.numPendingFiles === 0) {
8274 this.refreshQueue();
8278 if (this.numPendingFiles <= 1) {
8279 window.status = "Finished loading all dependencies, onReady fired!";
8282 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
8287 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
8288 var queue = this.queue,
8290 i, ln, j, subLn, missingClasses = [], missingPaths = [];
8292 for (i = 0, ln = queue.length; i < ln; i++) {
8293 requires = queue[i].requires;
8295 for (j = 0, subLn = requires.length; j < ln; j++) {
8296 if (this.isFileLoaded[requires[j]]) {
8297 missingClasses.push(requires[j]);
8302 if (missingClasses.length < 1) {
8306 missingClasses = Ext.Array.filter(missingClasses, function(item) {
8307 return !this.requiresMap.hasOwnProperty(item);
8310 for (i = 0,ln = missingClasses.length; i < ln; i++) {
8311 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
8315 sourceClass: "Ext.Loader",
8316 sourceMethod: "onFileLoaded",
8317 msg: "The following classes are not declared even if their files have been " +
8318 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
8319 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
8328 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
8329 this.numPendingFiles--;
8330 this.hasFileLoadError = true;
8334 sourceClass: "Ext.Loader",
8335 classToLoad: className,
8337 loadingType: isSynchronous ? 'synchronous' : 'async',
8346 addOptionalRequires: function(requires) {
8347 var optionalRequires = this.optionalRequires,
8350 requires = Ext.Array.from(requires);
8352 for (i = 0, ln = requires.length; i < ln; i++) {
8353 require = requires[i];
8355 Ext.Array.include(optionalRequires, require);
8364 triggerReady: function(force) {
8365 var readyListeners = this.readyListeners,
8366 optionalRequires, listener;
8368 if (this.isLoading || force) {
8369 this.isLoading = false;
8371 if (this.optionalRequires.length) {
8372 // Clone then empty the array to eliminate potential recursive loop issue
8373 optionalRequires = Ext.Array.clone(this.optionalRequires);
8375 // Empty the original array
8376 this.optionalRequires.length = 0;
8378 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
8382 while (readyListeners.length) {
8383 listener = readyListeners.shift();
8384 listener.fn.call(listener.scope);
8386 if (this.isLoading) {
8396 * Adds new listener to be executed when all required scripts are fully loaded.
8398 * @param {Function} fn The function callback to be executed
8399 * @param {Object} scope The execution scope (`this`) of the callback function
8400 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
8402 onReady: function(fn, scope, withDomReady, options) {
8405 if (withDomReady !== false && Ext.onDocumentReady) {
8409 Ext.onDocumentReady(oldFn, scope, options);
8413 if (!this.isLoading) {
8417 this.readyListeners.push({
8426 * @param {String} className
8428 historyPush: function(className) {
8429 if (className && this.isFileLoaded.hasOwnProperty(className)) {
8430 Ext.Array.include(this.history, className);
8440 * @alias Ext.Loader#require
8442 Ext.require = alias(Loader, 'require');
8446 * @method syncRequire
8447 * @alias Ext.Loader#syncRequire
8449 Ext.syncRequire = alias(Loader, 'syncRequire');
8454 * @alias Ext.Loader#exclude
8456 Ext.exclude = alias(Loader, 'exclude');
8461 * @alias Ext.Loader#onReady
8463 Ext.onReady = function(fn, scope, options) {
8464 Loader.onReady(fn, scope, true, options);
8468 * @cfg {String[]} requires
8470 * List of classes that have to be loaded before instantiating this class.
8473 * Ext.define('Mother', {
8474 * requires: ['Child'],
8475 * giveBirth: function() {
8476 * // we can be sure that child class is available.
8477 * return new Child();
8481 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
8484 className = Manager.getName(cls),
8485 i, j, ln, subLn, value, propertyName, propertyValue;
8488 Basically loop through the dependencyProperties, look for string class names and push
8489 them into a stack, regardless of whether the property's value is a string, array or object. For example:
8491 extend: 'Ext.MyClass',
8492 requires: ['Ext.some.OtherClass'],
8494 observable: 'Ext.util.Observable';
8497 which will later be transformed into:
8499 extend: Ext.MyClass,
8500 requires: [Ext.some.OtherClass],
8502 observable: Ext.util.Observable;
8507 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8508 propertyName = dependencyProperties[i];
8510 if (data.hasOwnProperty(propertyName)) {
8511 propertyValue = data[propertyName];
8513 if (typeof propertyValue === 'string') {
8514 dependencies.push(propertyValue);
8516 else if (propertyValue instanceof Array) {
8517 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8518 value = propertyValue[j];
8520 if (typeof value === 'string') {
8521 dependencies.push(value);
8525 else if (typeof propertyValue != 'function') {
8526 for (j in propertyValue) {
8527 if (propertyValue.hasOwnProperty(j)) {
8528 value = propertyValue[j];
8530 if (typeof value === 'string') {
8531 dependencies.push(value);
8539 if (dependencies.length === 0) {
8540 // Loader.historyPush(className);
8545 var deadlockPath = [],
8546 requiresMap = Loader.requiresMap,
8550 Automatically detect deadlocks before-hand,
8551 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
8553 - A extends B, then B extends A
8554 - A requires B, B requires C, then C requires A
8556 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
8557 no matter how deep the path is.
8561 requiresMap[className] = dependencies;
8563 detectDeadlock = function(cls) {
8564 deadlockPath.push(cls);
8566 if (requiresMap[cls]) {
8567 if (Ext.Array.contains(requiresMap[cls], className)) {
8569 sourceClass: "Ext.Loader",
8570 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
8571 deadlockPath[1] + "' " + "mutually require each other. Path: " +
8572 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
8576 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
8577 detectDeadlock(requiresMap[cls][i]);
8582 detectDeadlock(className);
8587 Loader.require(dependencies, function() {
8588 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8589 propertyName = dependencyProperties[i];
8591 if (data.hasOwnProperty(propertyName)) {
8592 propertyValue = data[propertyName];
8594 if (typeof propertyValue === 'string') {
8595 data[propertyName] = Manager.get(propertyValue);
8597 else if (propertyValue instanceof Array) {
8598 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8599 value = propertyValue[j];
8601 if (typeof value === 'string') {
8602 data[propertyName][j] = Manager.get(value);
8606 else if (typeof propertyValue != 'function') {
8607 for (var k in propertyValue) {
8608 if (propertyValue.hasOwnProperty(k)) {
8609 value = propertyValue[k];
8611 if (typeof value === 'string') {
8612 data[propertyName][k] = Manager.get(value);
8620 continueFn.call(me, cls, data);
8626 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8629 * @cfg {String[]} uses
8631 * List of classes to load together with this class. These aren't neccessarily loaded before
8632 * this class is instantiated. For example:
8634 * Ext.define('Mother', {
8636 * giveBirth: function() {
8637 * // This code might, or might not work:
8638 * // return new Child();
8640 * // Instead use Ext.create() to load the class at the spot if not loaded already:
8641 * return Ext.create('Child');
8645 Manager.registerPostprocessor('uses', function(name, cls, data) {
8646 var uses = Ext.Array.from(data.uses),
8650 for (i = 0, ln = uses.length; i < ln; i++) {
8653 if (typeof item === 'string') {
8658 Loader.addOptionalRequires(items);
8661 Manager.setDefaultPostprocessorPosition('uses', 'last');
8663 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8666 * @author Brian Moeskau <brian@sencha.com>
8667 * @docauthor Brian Moeskau <brian@sencha.com>
8669 * A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8670 * errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8671 * uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8672 * the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8673 * with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8674 * execution will halt.
8676 * Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8677 * handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8678 * although in a real application it's usually a better idea to override the handling function and perform
8679 * logging or some other method of reporting the errors in a way that is meaningful to the application.
8681 * At its simplest you can simply raise an error as a simple string from within any code:
8685 * Ext.Error.raise('Something bad happened!');
8687 * If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8688 * displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8689 * additional metadata about the error being raised. The {@link #raise} method can also take a config object.
8690 * In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8691 * added to the error object and, if the console is available, logged to the console for inspection.
8695 * Ext.define('Ext.Foo', {
8696 * doSomething: function(option){
8697 * if (someCondition === false) {
8699 * msg: 'You cannot do that!',
8700 * option: option, // whatever was passed into the method
8701 * 'error code': 100 // other arbitrary info
8707 * If a console is available (that supports the `console.dir` function) you'll see console output like:
8709 * An error was raised with the following data:
8710 * option: Object { foo: "bar"}
8713 * msg: "You cannot do that!"
8714 * sourceClass: "Ext.Foo"
8715 * sourceMethod: "doSomething"
8717 * uncaught exception: You cannot do that!
8719 * As you can see, the error will report exactly where it was raised and will include as much information as the
8720 * raising code can usefully provide.
8722 * If you want to handle all application errors globally you can simply override the static {@link #handle} method
8723 * and provide whatever handling logic you need. If the method returns true then the error is considered handled
8724 * and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8728 * Ext.Error.handle = function(err) {
8729 * if (err.someProperty == 'NotReallyAnError') {
8730 * // maybe log something to the application here if applicable
8733 * // any non-true return value (including none) will cause the error to be thrown
8737 Ext.Error = Ext.extend(Error, {
8740 * @property {Boolean} ignore
8741 * Static flag that can be used to globally disable error reporting to the browser if set to true
8742 * (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8743 * and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8744 * be preferable to supply a custom error {@link #handle handling} function instead.
8748 * Ext.Error.ignore = true;
8755 * @property {Boolean} notify
8756 * Static flag that can be used to globally control error notification to the user. Unlike
8757 * Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8758 * set to false to disable the alert notification (default is true for IE6 and IE7).
8760 * Only the first error will generate an alert. Internally this flag is set to false when the
8761 * first error occurs prior to displaying the alert.
8763 * This flag is not used in a release build.
8767 * Ext.Error.notify = false;
8771 //notify: Ext.isIE6 || Ext.isIE7,
8774 * Raise an error that can include additional data and supports automatic console logging if available.
8775 * You can pass a string error message or an object with the `msg` attribute which will be used as the
8776 * error message. The object can contain any other name-value attributes (or objects) to be logged
8777 * along with the error.
8779 * Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8780 * execution will halt.
8784 * Ext.Error.raise('A simple string error message');
8788 * Ext.define('Ext.Foo', {
8789 * doSomething: function(option){
8790 * if (someCondition === false) {
8792 * msg: 'You cannot do that!',
8793 * option: option, // whatever was passed into the method
8794 * 'error code': 100 // other arbitrary info
8800 * @param {String/Object} err The error message string, or an object containing the attribute "msg" that will be
8801 * used as the error message. Any other data included in the object will also be logged to the browser console,
8805 raise: function(err){
8807 if (Ext.isString(err)) {
8811 var method = this.raise.caller;
8815 err.sourceMethod = method.$name;
8817 if (method.$owner) {
8818 err.sourceClass = method.$owner.$className;
8822 if (Ext.Error.handle(err) !== true) {
8823 var msg = Ext.Error.prototype.toString.call(err);
8832 throw new Ext.Error(err);
8837 * Globally handle any Ext errors that may be raised, optionally providing custom logic to
8838 * handle different errors individually. Return true from the function to bypass throwing the
8839 * error to the browser, otherwise the error will be thrown and execution will halt.
8843 * Ext.Error.handle = function(err) {
8844 * if (err.someProperty == 'NotReallyAnError') {
8845 * // maybe log something to the application here if applicable
8848 * // any non-true return value (including none) will cause the error to be thrown
8851 * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes that were originally
8852 * raised with it, plus properties about the method and class from which the error originated (if raised from a
8853 * class that uses the Ext 4 class system).
8857 return Ext.Error.ignore;
8861 // This is the standard property that is the name of the constructor.
8865 * Creates new Error object.
8866 * @param {String/Object} config The error message string, or an object containing the
8867 * attribute "msg" that will be used as the error message. Any other data included in
8868 * the object will be applied to the error instance and logged to the browser console, if available.
8870 constructor: function(config){
8871 if (Ext.isString(config)) {
8872 config = { msg: config };
8877 Ext.apply(me, config);
8879 me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8880 // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8884 * Provides a custom string representation of the error object. This is an override of the base JavaScript
8885 * `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8886 * be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8888 * The default implementation will include the error message along with the raising class and method, if available,
8889 * but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8890 * a particular error instance, if you want to provide a custom description that will show up in the console.
8891 * @return {String} The error message. If raised from within the Ext 4 class system, the error message will also
8892 * include the raising class and method names, if available.
8894 toString: function(){
8896 className = me.className ? me.className : '',
8897 methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8898 msg = me.msg || '(No description provided)';
8900 return className + methodName + msg;
8905 * This mechanism is used to notify the user of the first error encountered on the page. This
8906 * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8907 * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8908 * where exceptions are handled in a try/catch.
8912 var prevOnError, timer, errors = 0,
8913 extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
8916 if (typeof window === 'undefined') {
8917 return; // build system or some such environment...
8920 // This method is called to notify the user of the current error status.
8921 function notify () {
8922 var counters = Ext.log.counters,
8923 supports = Ext.supports,
8924 hasOnError = supports && supports.WindowOnError; // TODO - timing
8926 // Put log counters to the status bar (for most browsers):
8927 if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
8928 var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
8929 'Info:',counters.info, 'Log:',counters.log].join(' ');
8931 msg = '*** Errors: ' + errors + ' - ' + msg;
8932 } else if (counters.error) {
8938 // Display an alert on the first error:
8939 if (!Ext.isDefined(Ext.Error.notify)) {
8940 Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
8942 if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
8943 Ext.Error.notify = false;
8946 win.clearInterval(timer); // ticks can queue up so stop...
8950 alert('Unhandled error on page: See console or log');
8955 // Sets up polling loop. This is the only way to know about errors in some browsers
8956 // (Opera/Safari) and is the only way to update the status bar for warnings and other
8959 timer = win.setInterval(notify, 1000);
8962 // window.onerror sounds ideal but it prevents the built-in error dialog from doing
8963 // its (better) thing.