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);
180 sourceMethod: 'extend',
181 msg: 'Attempting to extend from a class which has not been loaded on the page.'
185 // We create a new temporary class
186 var F = function() {},
187 subclassProto, superclassProto = superclass.prototype;
189 F.prototype = superclassProto;
190 subclassProto = subclass.prototype = new F();
191 subclassProto.constructor = subclass;
192 subclass.superclass = superclassProto;
194 if (superclassProto.constructor === objectConstructor) {
195 superclassProto.constructor = superclass;
198 subclass.override = function(overrides) {
199 Ext.override(subclass, overrides);
202 subclassProto.override = inlineOverrides;
203 subclassProto.proto = subclassProto;
205 subclass.override(overrides);
206 subclass.extend = function(o) {
207 return Ext.extend(subclass, o);
215 * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
217 Ext.define('My.cool.Class', {
223 Ext.override(My.cool.Class, {
225 alert('About to say...');
227 this.callOverridden();
231 var cool = new My.cool.Class();
232 cool.sayHi(); // alerts 'About to say...'
235 * Please note that `this.callOverridden()` only works if the class was previously
236 * created with {@link Ext#define)
238 * @param {Object} cls The class to override
239 * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
240 * containing one or more methods.
244 override: function(cls, overrides) {
245 if (cls.prototype.$className) {
246 return cls.override(overrides);
249 Ext.apply(cls.prototype, overrides);
254 // A full set of static methods to do type checking
258 * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
259 * value (second argument) otherwise.
261 * @param {Object} value The value to test
262 * @param {Object} defaultValue The value to return if the original value is empty
263 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
264 * @return {Object} value, if non-empty, else defaultValue
266 valueFrom: function(value, defaultValue, allowBlank){
267 return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
271 * Returns the type of the given variable in string format. List of possible values are:
273 * - `undefined`: If the given value is `undefined`
274 * - `null`: If the given value is `null`
275 * - `string`: If the given value is a string
276 * - `number`: If the given value is a number
277 * - `boolean`: If the given value is a boolean value
278 * - `date`: If the given value is a `Date` object
279 * - `function`: If the given value is a function reference
280 * - `object`: If the given value is an object
281 * - `array`: If the given value is an array
282 * - `regexp`: If the given value is a regular expression
283 * - `element`: If the given value is a DOM Element
284 * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
285 * - `whitespace`: If the given value is a DOM text node and contains only whitespace
287 * @param {Object} value
291 typeOf: function(value) {
292 if (value === null) {
296 var type = typeof value;
298 if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
302 var typeToString = toString.call(value);
304 switch(typeToString) {
305 case '[object Array]':
307 case '[object Date]':
309 case '[object Boolean]':
311 case '[object Number]':
313 case '[object RegExp]':
317 if (type === 'function') {
321 if (type === 'object') {
322 if (value.nodeType !== undefined) {
323 if (value.nodeType === 3) {
324 return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
336 sourceMethod: 'typeOf',
337 msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
342 * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
346 * - a zero-length array
347 * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
349 * @param {Object} value The value to test
350 * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
354 isEmpty: function(value, allowEmptyString) {
355 return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
359 * Returns true if the passed value is a JavaScript Array, false otherwise.
361 * @param {Object} target The target to test
365 isArray: ('isArray' in Array) ? Array.isArray : function(value) {
366 return toString.call(value) === '[object Array]';
370 * Returns true if the passed value is a JavaScript Date object, false otherwise.
371 * @param {Object} object The object to test
374 isDate: function(value) {
375 return toString.call(value) === '[object Date]';
379 * Returns true if the passed value is a JavaScript Object, false otherwise.
380 * @param {Object} value The value to test
384 isObject: (toString.call(null) === '[object Object]') ?
386 // check ownerDocument here as well to exclude DOM nodes
387 return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
390 return toString.call(value) === '[object Object]';
394 * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
395 * @param {Object} value The value to test
398 isPrimitive: function(value) {
399 var type = typeof value;
401 return type === 'string' || type === 'number' || type === 'boolean';
405 * Returns true if the passed value is a JavaScript Function, false otherwise.
406 * @param {Object} value The value to test
411 // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
412 // Object.prorotype.toString (slower)
413 (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
414 return toString.call(value) === '[object Function]';
415 } : function(value) {
416 return typeof value === 'function';
420 * Returns true if the passed value is a number. Returns false for non-finite numbers.
421 * @param {Object} value The value to test
424 isNumber: function(value) {
425 return typeof value === 'number' && isFinite(value);
429 * Validates that a value is numeric.
430 * @param {Object} value Examples: 1, '1', '2.34'
431 * @return {Boolean} True if numeric, false otherwise
433 isNumeric: function(value) {
434 return !isNaN(parseFloat(value)) && isFinite(value);
438 * Returns true if the passed value is a string.
439 * @param {Object} value The value to test
442 isString: function(value) {
443 return typeof value === 'string';
447 * Returns true if the passed value is a boolean.
449 * @param {Object} value The value to test
452 isBoolean: function(value) {
453 return typeof value === 'boolean';
457 * Returns true if the passed value is an HTMLElement
458 * @param {Object} value The value to test
461 isElement: function(value) {
462 return value ? value.nodeType === 1 : false;
466 * Returns true if the passed value is a TextNode
467 * @param {Object} value The value to test
470 isTextNode: function(value) {
471 return value ? value.nodeName === "#text" : false;
475 * Returns true if the passed value is defined.
476 * @param {Object} value The value to test
479 isDefined: function(value) {
480 return typeof value !== 'undefined';
484 * Returns true if the passed value is iterable, false otherwise
485 * @param {Object} value The value to test
488 isIterable: function(value) {
489 return (value && typeof value !== 'string') ? value.length !== undefined : false;
496 * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
497 * @param {Object} item The variable to clone
498 * @return {Object} clone
500 clone: function(item) {
501 if (item === null || item === undefined) {
506 // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
508 if (item.nodeType && item.cloneNode) {
509 return item.cloneNode(true);
512 var type = toString.call(item);
515 if (type === '[object Date]') {
516 return new Date(item.getTime());
519 var i, j, k, clone, key;
522 if (type === '[object Array]') {
528 clone[i] = Ext.clone(item[i]);
532 else if (type === '[object Object]' && item.constructor === Object) {
536 clone[key] = Ext.clone(item[key]);
540 for (j = enumerables.length; j--;) {
547 return clone || item;
552 * Generate a unique reference of Ext in the global scope, useful for sandboxing
554 getUniqueGlobalNamespace: function() {
555 var uniqueGlobalNamespace = this.uniqueGlobalNamespace;
557 if (uniqueGlobalNamespace === undefined) {
561 uniqueGlobalNamespace = 'ExtBox' + (++i);
562 } while (Ext.global[uniqueGlobalNamespace] !== undefined);
564 Ext.global[uniqueGlobalNamespace] = Ext;
565 this.uniqueGlobalNamespace = uniqueGlobalNamespace;
568 return uniqueGlobalNamespace;
574 functionFactory: function() {
575 var args = Array.prototype.slice.call(arguments);
577 if (args.length > 0) {
578 args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
579 args[args.length - 1];
582 return Function.prototype.constructor.apply(Function.prototype, args);
587 * Old alias to {@link Ext#typeOf}
588 * @deprecated 4.0.0 Use {@link Ext#typeOf} instead
592 Ext.type = Ext.typeOf;
597 * @author Jacky Nguyen <jacky@sencha.com>
598 * @docauthor Jacky Nguyen <jacky@sencha.com>
601 * A utility class that wrap around a string version number and provide convenient
602 * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:
604 var version = new Ext.Version('1.0.2beta');
605 console.log("Version is " + version); // Version is 1.0.2beta
607 console.log(version.getMajor()); // 1
608 console.log(version.getMinor()); // 0
609 console.log(version.getPatch()); // 2
610 console.log(version.getBuild()); // 0
611 console.log(version.getRelease()); // beta
613 console.log(version.isGreaterThan('1.0.1')); // True
614 console.log(version.isGreaterThan('1.0.2alpha')); // True
615 console.log(version.isGreaterThan('1.0.2RC')); // False
616 console.log(version.isGreaterThan('1.0.2')); // False
617 console.log(version.isLessThan('1.0.2')); // True
619 console.log(version.match(1.0)); // True
620 console.log(version.match('1.0.2')); // True
626 // Current core version
627 var version = '4.0.7', Version;
628 Ext.Version = Version = Ext.extend(Object, {
631 * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
632 * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
633 * @return {Ext.Version} this
635 constructor: function(version) {
636 var parts, releaseStartIndex;
638 if (version instanceof Version) {
642 this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');
644 releaseStartIndex = this.version.search(/([^\d\.])/);
646 if (releaseStartIndex !== -1) {
647 this.release = this.version.substr(releaseStartIndex, version.length);
648 this.shortVersion = this.version.substr(0, releaseStartIndex);
651 this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');
653 parts = this.version.split('.');
655 this.major = parseInt(parts.shift() || 0, 10);
656 this.minor = parseInt(parts.shift() || 0, 10);
657 this.patch = parseInt(parts.shift() || 0, 10);
658 this.build = parseInt(parts.shift() || 0, 10);
664 * Override the native toString method
666 * @return {String} version
668 toString: function() {
673 * Override the native valueOf method
675 * @return {String} version
677 valueOf: function() {
682 * Returns the major component value
683 * @return {Number} major
685 getMajor: function() {
686 return this.major || 0;
690 * Returns the minor component value
691 * @return {Number} minor
693 getMinor: function() {
694 return this.minor || 0;
698 * Returns the patch component value
699 * @return {Number} patch
701 getPatch: function() {
702 return this.patch || 0;
706 * Returns the build component value
707 * @return {Number} build
709 getBuild: function() {
710 return this.build || 0;
714 * Returns the release component value
715 * @return {Number} release
717 getRelease: function() {
718 return this.release || '';
722 * Returns whether this version if greater than the supplied argument
723 * @param {String/Number} target The version to compare with
724 * @return {Boolean} True if this version if greater than the target, false otherwise
726 isGreaterThan: function(target) {
727 return Version.compare(this.version, target) === 1;
731 * Returns whether this version if smaller than the supplied argument
732 * @param {String/Number} target The version to compare with
733 * @return {Boolean} True if this version if smaller than the target, false otherwise
735 isLessThan: function(target) {
736 return Version.compare(this.version, target) === -1;
740 * Returns whether this version equals to the supplied argument
741 * @param {String/Number} target The version to compare with
742 * @return {Boolean} True if this version equals to the target, false otherwise
744 equals: function(target) {
745 return Version.compare(this.version, target) === 0;
749 * Returns whether this version matches the supplied argument. Example:
751 * var version = new Ext.Version('1.0.2beta');
752 * console.log(version.match(1)); // True
753 * console.log(version.match(1.0)); // True
754 * console.log(version.match('1.0.2')); // True
755 * console.log(version.match('1.0.2RC')); // False
757 * @param {String/Number} target The version to compare with
758 * @return {Boolean} True if this version matches the target, false otherwise
760 match: function(target) {
761 target = String(target);
762 return this.version.substr(0, target.length) === target;
766 * Returns this format: [major, minor, patch, build, release]. Useful for comparison
769 toArray: function() {
770 return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
774 * Returns shortVersion version without dots and release
777 getShortVersion: function() {
778 return this.shortVersion;
797 * Converts a version component to a comparable value
800 * @param {Object} value The value to convert
803 getComponentValue: function(value) {
804 return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
808 * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
809 * they are handled in the following order:
810 * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
813 * @param {String} current The current version to compare to
814 * @param {String} target The target version to compare to
815 * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
817 compare: function(current, target) {
818 var currentValue, targetValue, i;
820 current = new Version(current).toArray();
821 target = new Version(target).toArray();
823 for (i = 0; i < Math.max(current.length, target.length); i++) {
824 currentValue = this.getComponentValue(current[i]);
825 targetValue = this.getComponentValue(target[i]);
827 if (currentValue < targetValue) {
829 } else if (currentValue > targetValue) {
847 lastRegisteredVersion: null,
850 * Set version number for the given package name.
852 * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
853 * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
856 setVersion: function(packageName, version) {
857 Ext.versions[packageName] = new Version(version);
858 Ext.lastRegisteredVersion = Ext.versions[packageName];
864 * Get the version number of the supplied package name; will return the last registered version
865 * (last Ext.setVersion call) if there's no package name given.
867 * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
868 * @return {Ext.Version} The version
870 getVersion: function(packageName) {
871 if (packageName === undefined) {
872 return Ext.lastRegisteredVersion;
875 return Ext.versions[packageName];
879 * Create a closure for deprecated code.
881 // This means Ext.oldMethod is only supported in 4.0.0beta and older.
882 // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
883 // the closure will not be invoked
884 Ext.deprecate('extjs', '4.0.0beta', function() {
885 Ext.oldMethod = Ext.newMethod;
890 * @param {String} packageName The package name
891 * @param {String} since The last version before it's deprecated
892 * @param {Function} closure The callback function to be executed with the specified version is less than the current version
893 * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
896 deprecate: function(packageName, since, closure, scope) {
897 if (Version.compare(Ext.getVersion(packageName), since) < 1) {
901 }); // End Versioning
903 Ext.setVersion('core', version);
910 * A collection of useful static methods to deal with strings
915 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,
917 formatRe: /\{(\d+)\}/g,
918 escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
921 * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
922 * @param {String} value The string to encode
923 * @return {String} The encoded text
926 htmlEncode: (function() {
932 }, keys = [], p, regex;
934 for (p in entities) {
938 regex = new RegExp('(' + keys.join('|') + ')', 'g');
940 return function(value) {
941 return (!value) ? value : String(value).replace(regex, function(match, capture) {
942 return entities[capture];
948 * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
949 * @param {String} value The string to decode
950 * @return {String} The decoded text
953 htmlDecode: (function() {
959 }, keys = [], p, regex;
961 for (p in entities) {
965 regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
967 return function(value) {
968 return (!value) ? value : String(value).replace(regex, function(match, capture) {
969 if (capture in entities) {
970 return entities[capture];
972 return String.fromCharCode(parseInt(capture.substr(2), 10));
979 * Appends content to the query string of a URL, handling logic for whether to place
980 * a question mark or ampersand.
981 * @param {String} url The URL to append to.
982 * @param {String} string The content to append to the URL.
983 * @return (String) The resulting URL
985 urlAppend : function(url, string) {
986 if (!Ext.isEmpty(string)) {
987 return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
994 * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
997 alert('-' + s + '-'); //alerts "- foo bar -"
998 alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
1000 * @param {String} string The string to escape
1001 * @return {String} The trimmed string
1003 trim: function(string) {
1004 return string.replace(Ext.String.trimRegex, "");
1008 * Capitalize the given string
1009 * @param {String} string
1012 capitalize: function(string) {
1013 return string.charAt(0).toUpperCase() + string.substr(1);
1017 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
1018 * @param {String} value The string to truncate
1019 * @param {Number} length The maximum length to allow before truncating
1020 * @param {Boolean} word True to try to find a common word break
1021 * @return {String} The converted text
1023 ellipsis: function(value, len, word) {
1024 if (value && value.length > len) {
1026 var vs = value.substr(0, len - 2),
1027 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
1028 if (index !== -1 && index >= (len - 15)) {
1029 return vs.substr(0, index) + "...";
1032 return value.substr(0, len - 3) + "...";
1038 * Escapes the passed string for use in a regular expression
1039 * @param {String} string
1042 escapeRegex: function(string) {
1043 return string.replace(Ext.String.escapeRegexRe, "\\$1");
1047 * Escapes the passed string for ' and \
1048 * @param {String} string The string to escape
1049 * @return {String} The escaped string
1051 escape: function(string) {
1052 return string.replace(Ext.String.escapeRe, "\\$1");
1056 * Utility function that allows you to easily switch a string between two alternating values. The passed value
1057 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
1058 * they are already different, the first value passed in is returned. Note that this method returns the new value
1059 * but does not change the current string.
1061 // alternate sort directions
1062 sort = Ext.String.toggle(sort, 'ASC', 'DESC');
1064 // instead of conditional logic:
1065 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
1067 * @param {String} string The current string
1068 * @param {String} value The value to compare to the current string
1069 * @param {String} other The new value to use if the string already equals the first value passed in
1070 * @return {String} The new value
1072 toggle: function(string, value, other) {
1073 return string === value ? other : value;
1077 * Pads the left side of a string with a specified character. This is especially useful
1078 * for normalizing number and date strings. Example usage:
1081 var s = Ext.String.leftPad('123', 5, '0');
1082 // s now contains the string: '00123'
1084 * @param {String} string The original string
1085 * @param {Number} size The total length of the output string
1086 * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
1087 * @return {String} The padded string
1089 leftPad: function(string, size, character) {
1090 var result = String(string);
1091 character = character || " ";
1092 while (result.length < size) {
1093 result = character + result;
1099 * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
1100 * token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
1102 var cls = 'my-class', text = 'Some text';
1103 var s = Ext.String.format('<div class="{0}">{1}</div>', cls, text);
1104 // s now contains the string: '<div class="my-class">Some text</div>'
1106 * @param {String} string The tokenized string to be formatted
1107 * @param {String} value1 The value to replace token {0}
1108 * @param {String} value2 Etc...
1109 * @return {String} The formatted string
1111 format: function(format) {
1112 var args = Ext.Array.toArray(arguments, 1);
1113 return format.replace(Ext.String.formatRe, function(m, i) {
1119 * Returns a string with a specified number of repititions a given string pattern.
1120 * The pattern be separated by a different string.
1122 * var s = Ext.String.repeat('---', 4); // = '------------'
1123 * var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
1125 * @param {String} pattern The pattern to repeat.
1126 * @param {Number} count The number of times to repeat the pattern (may be 0).
1127 * @param {String} sep An option string to separate each pattern.
1129 repeat: function(pattern, count, sep) {
1130 for (var buf = [], i = count; i--; ) {
1133 return buf.join(sep || '');
1140 * A collection of useful static methods to deal with numbers
1146 var isToFixedBroken = (0.9).toFixed() !== '1';
1150 * Checks whether or not the passed number is within a desired range. If the number is already within the
1151 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1152 * exceeded. Note that this method returns the constrained value but does not change the current number.
1153 * @param {Number} number The number to check
1154 * @param {Number} min The minimum number in the range
1155 * @param {Number} max The maximum number in the range
1156 * @return {Number} The constrained value if outside the range, otherwise the current value
1158 constrain: function(number, min, max) {
1159 number = parseFloat(number);
1162 number = Math.max(number, min);
1165 number = Math.min(number, max);
1171 * Snaps the passed number between stopping points based upon a passed increment value.
1172 * @param {Number} value The unsnapped value.
1173 * @param {Number} increment The increment by which the value must move.
1174 * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1175 * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1176 * @return {Number} The value of the nearest snap target.
1178 snap : function(value, increment, minValue, maxValue) {
1179 var newValue = value,
1182 if (!(increment && value)) {
1185 m = value % increment;
1188 if (m * 2 >= increment) {
1189 newValue += increment;
1190 } else if (m * 2 < -increment) {
1191 newValue -= increment;
1194 return Ext.Number.constrain(newValue, minValue, maxValue);
1198 * Formats a number using fixed-point notation
1199 * @param {Number} value The number to format
1200 * @param {Number} precision The number of digits to show after the decimal point
1202 toFixed: function(value, precision) {
1203 if (isToFixedBroken) {
1204 precision = precision || 0;
1205 var pow = Math.pow(10, precision);
1206 return (Math.round(value * pow) / pow).toFixed(precision);
1209 return value.toFixed(precision);
1213 * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1216 Ext.Number.from('1.23', 1); // returns 1.23
1217 Ext.Number.from('abc', 1); // returns 1
1219 * @param {Object} value
1220 * @param {Number} defaultValue The value to return if the original value is non-numeric
1221 * @return {Number} value, if numeric, defaultValue otherwise
1223 from: function(value, defaultValue) {
1224 if (isFinite(value)) {
1225 value = parseFloat(value);
1228 return !isNaN(value) ? value : defaultValue;
1235 * @deprecated 4.0.0 Please use {@link Ext.Number#from} instead.
1238 * @alias Ext.Number#from
1240 Ext.num = function() {
1241 return Ext.Number.from.apply(this, arguments);
1246 * @author Jacky Nguyen <jacky@sencha.com>
1247 * @docauthor Jacky Nguyen <jacky@sencha.com>
1249 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1253 var arrayPrototype = Array.prototype,
1254 slice = arrayPrototype.slice,
1255 supportsSplice = function () {
1260 if (!array.splice) {
1264 // This detects a bug in IE8 splice method:
1265 // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1271 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");
1273 lengthBefore = array.length; //41
1274 array.splice(13, 0, "XXX"); // add one element
1276 if (lengthBefore+1 != array.length) {
1283 supportsForEach = 'forEach' in arrayPrototype,
1284 supportsMap = 'map' in arrayPrototype,
1285 supportsIndexOf = 'indexOf' in arrayPrototype,
1286 supportsEvery = 'every' in arrayPrototype,
1287 supportsSome = 'some' in arrayPrototype,
1288 supportsFilter = 'filter' in arrayPrototype,
1289 supportsSort = function() {
1290 var a = [1,2,3,4,5].sort(function(){ return 0; });
1291 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1293 supportsSliceOnNodeList = true,
1297 // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1298 if (typeof document !== 'undefined') {
1299 slice.call(document.getElementsByTagName('body'));
1302 supportsSliceOnNodeList = false;
1305 function fixArrayIndex (array, index) {
1306 return (index < 0) ? Math.max(0, array.length + index)
1307 : Math.min(array.length, index);
1311 Does the same work as splice, but with a slightly more convenient signature. The splice
1312 method has bugs in IE8, so this is the implementation we use on that platform.
1314 The rippling of items in the array can be tricky. Consider two use cases:
1319 +---+---+---+---+---+---+---+---+
1320 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1321 +---+---+---+---+---+---+---+---+
1324 / / \/ \/ \ +--------------------------+
1325 / / /\ /\ +--------------------------+ \
1326 / / / \/ +--------------------------+ \ \
1327 / / / /+--------------------------+ \ \ \
1330 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1331 | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1332 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1336 In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1337 that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1338 must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1340 function replaceSim (array, index, removeCount, insert) {
1341 var add = insert ? insert.length : 0,
1342 length = array.length,
1343 pos = fixArrayIndex(array, index);
1345 // we try to use Array.push when we can for efficiency...
1346 if (pos === length) {
1348 array.push.apply(array, insert);
1351 var remove = Math.min(removeCount, length - pos),
1352 tailOldPos = pos + remove,
1353 tailNewPos = tailOldPos + add - remove,
1354 tailCount = length - tailOldPos,
1355 lengthAfterRemove = length - remove,
1358 if (tailNewPos < tailOldPos) { // case A
1359 for (i = 0; i < tailCount; ++i) {
1360 array[tailNewPos+i] = array[tailOldPos+i];
1362 } else if (tailNewPos > tailOldPos) { // case B
1363 for (i = tailCount; i--; ) {
1364 array[tailNewPos+i] = array[tailOldPos+i];
1366 } // else, add == remove (nothing to do)
1368 if (add && pos === lengthAfterRemove) {
1369 array.length = lengthAfterRemove; // truncate array
1370 array.push.apply(array, insert);
1372 array.length = lengthAfterRemove + add; // reserves space
1373 for (i = 0; i < add; ++i) {
1374 array[pos+i] = insert[i];
1382 function replaceNative (array, index, removeCount, insert) {
1383 if (insert && insert.length) {
1384 if (index < array.length) {
1385 array.splice.apply(array, [index, removeCount].concat(insert));
1387 array.push.apply(array, insert);
1390 array.splice(index, removeCount);
1395 function eraseSim (array, index, removeCount) {
1396 return replaceSim(array, index, removeCount);
1399 function eraseNative (array, index, removeCount) {
1400 array.splice(index, removeCount);
1404 function spliceSim (array, index, removeCount) {
1405 var pos = fixArrayIndex(array, index),
1406 removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1408 if (arguments.length < 4) {
1409 replaceSim(array, pos, removeCount);
1411 replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1417 function spliceNative (array) {
1418 return array.splice.apply(array, slice.call(arguments, 1));
1421 var erase = supportsSplice ? eraseNative : eraseSim,
1422 replace = supportsSplice ? replaceNative : replaceSim,
1423 splice = supportsSplice ? spliceNative : spliceSim;
1425 // NOTE: from here on, use erase, replace or splice (not native methods)...
1427 ExtArray = Ext.Array = {
1429 * Iterates an array or an iterable value and invoke the given callback function for each item.
1431 * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1433 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1434 * console.log(name);
1437 * var sum = function() {
1440 * Ext.Array.each(arguments, function(value) {
1447 * sum(1, 2, 3); // returns 6
1449 * The iteration can be stopped by returning false in the function callback.
1451 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1452 * if (name === 'Singapore') {
1453 * return false; // break here
1457 * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1459 * @param {Array/NodeList/Object} iterable The value to be iterated. If this
1460 * argument is not iterable, the callback function is called once.
1461 * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1462 * the current `index`.
1463 * @param {Object} fn.item The item at the current `index` in the passed `array`
1464 * @param {Number} fn.index The current `index` within the `array`
1465 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1466 * @param {Boolean} fn.return Return false to stop iteration.
1467 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1468 * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1470 * @return {Boolean} See description for the `fn` parameter.
1472 each: function(array, fn, scope, reverse) {
1473 array = ExtArray.from(array);
1478 if (reverse !== true) {
1479 for (i = 0; i < ln; i++) {
1480 if (fn.call(scope || array[i], array[i], i, array) === false) {
1486 for (i = ln - 1; i > -1; i--) {
1487 if (fn.call(scope || array[i], array[i], i, array) === false) {
1497 * Iterates an array and invoke the given callback function for each item. Note that this will simply
1498 * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
1499 * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
1500 * could be much better in modern browsers comparing with {@link Ext.Array#each}
1502 * @param {Array} array The array to iterate
1503 * @param {Function} fn The callback function.
1504 * @param {Object} fn.item The item at the current `index` in the passed `array`
1505 * @param {Number} fn.index The current `index` within the `array`
1506 * @param {Array} fn.allItems The `array` itself which was passed as the first argument
1507 * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1509 forEach: function(array, fn, scope) {
1510 if (supportsForEach) {
1511 return array.forEach(fn, scope);
1517 for (; i < ln; i++) {
1518 fn.call(scope, array[i], i, array);
1523 * Get the index of the provided `item` in the given `array`, a supplement for the
1524 * missing arrayPrototype.indexOf in Internet Explorer.
1526 * @param {Array} array The array to check
1527 * @param {Object} item The item to look for
1528 * @param {Number} from (Optional) The index at which to begin the search
1529 * @return {Number} The index of item in the array (or -1 if it is not found)
1531 indexOf: function(array, item, from) {
1532 if (supportsIndexOf) {
1533 return array.indexOf(item, from);
1536 var i, length = array.length;
1538 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1539 if (array[i] === item) {
1548 * Checks whether or not the given `array` contains the specified `item`
1550 * @param {Array} array The array to check
1551 * @param {Object} item The item to look for
1552 * @return {Boolean} True if the array contains the item, false otherwise
1554 contains: function(array, item) {
1555 if (supportsIndexOf) {
1556 return array.indexOf(item) !== -1;
1561 for (i = 0, ln = array.length; i < ln; i++) {
1562 if (array[i] === item) {
1571 * Converts any iterable (numeric indices and a length property) into a true array.
1574 * var args = Ext.Array.toArray(arguments),
1575 * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1577 * alert(args.join(' '));
1578 * alert(fromSecondToLastArgs.join(' '));
1581 * test('just', 'testing', 'here'); // alerts 'just testing here';
1582 * // alerts 'testing here';
1584 * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1585 * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1586 * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1588 * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1590 * @param {Object} iterable the iterable object to be turned into a true Array.
1591 * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1592 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1593 * index of the iterable value
1594 * @return {Array} array
1596 toArray: function(iterable, start, end){
1597 if (!iterable || !iterable.length) {
1601 if (typeof iterable === 'string') {
1602 iterable = iterable.split('');
1605 if (supportsSliceOnNodeList) {
1606 return slice.call(iterable, start || 0, end || iterable.length);
1613 end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1615 for (i = start; i < end; i++) {
1616 array.push(iterable[i]);
1623 * Plucks the value of a property from each item in the Array. Example:
1625 * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1627 * @param {Array/NodeList} array The Array of items to pluck the value from.
1628 * @param {String} propertyName The property name to pluck from each element.
1629 * @return {Array} The value from each item in the Array.
1631 pluck: function(array, propertyName) {
1635 for (i = 0, ln = array.length; i < ln; i++) {
1638 ret.push(item[propertyName]);
1645 * Creates a new array with the results of calling a provided function on every element in this array.
1647 * @param {Array} array
1648 * @param {Function} fn Callback function for each item
1649 * @param {Object} scope Callback function scope
1650 * @return {Array} results
1652 map: function(array, fn, scope) {
1654 return array.map(fn, scope);
1661 for (; i < len; i++) {
1662 results[i] = fn.call(scope, array[i], i, array);
1669 * Executes the specified function for each array element until the function returns a falsy value.
1670 * If such an item is found, the function will return false immediately.
1671 * Otherwise, it will return true.
1673 * @param {Array} array
1674 * @param {Function} fn Callback function for each item
1675 * @param {Object} scope Callback function scope
1676 * @return {Boolean} True if no false value is returned by the callback function.
1678 every: function(array, fn, scope) {
1680 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1682 if (supportsEvery) {
1683 return array.every(fn, scope);
1689 for (; i < ln; ++i) {
1690 if (!fn.call(scope, array[i], i, array)) {
1699 * Executes the specified function for each array element until the function returns a truthy value.
1700 * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1702 * @param {Array} array
1703 * @param {Function} fn Callback function for each item
1704 * @param {Object} scope Callback function scope
1705 * @return {Boolean} True if the callback function returns a truthy value.
1707 some: function(array, fn, scope) {
1709 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1712 return array.some(fn, scope);
1718 for (; i < ln; ++i) {
1719 if (fn.call(scope, array[i], i, array)) {
1728 * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1730 * See {@link Ext.Array#filter}
1732 * @param {Array} array
1733 * @return {Array} results
1735 clean: function(array) {
1741 for (; i < ln; i++) {
1744 if (!Ext.isEmpty(item)) {
1753 * Returns a new array with unique items
1755 * @param {Array} array
1756 * @return {Array} results
1758 unique: function(array) {
1764 for (; i < ln; i++) {
1767 if (ExtArray.indexOf(clone, item) === -1) {
1776 * Creates a new array with all of the elements of this array for which
1777 * the provided filtering function returns true.
1779 * @param {Array} array
1780 * @param {Function} fn Callback function for each item
1781 * @param {Object} scope Callback function scope
1782 * @return {Array} results
1784 filter: function(array, fn, scope) {
1785 if (supportsFilter) {
1786 return array.filter(fn, scope);
1793 for (; i < ln; i++) {
1794 if (fn.call(scope, array[i], i, array)) {
1795 results.push(array[i]);
1803 * Converts a value to an array if it's not already an array; returns:
1805 * - An empty array if given value is `undefined` or `null`
1806 * - Itself if given value is already an array
1807 * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1808 * - An array with one item which is the given value, otherwise
1810 * @param {Object} value The value to convert to an array if it's not already is an array
1811 * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
1813 * @return {Array} array
1815 from: function(value, newReference) {
1816 if (value === undefined || value === null) {
1820 if (Ext.isArray(value)) {
1821 return (newReference) ? slice.call(value) : value;
1824 if (value && value.length !== undefined && typeof value !== 'string') {
1825 return Ext.toArray(value);
1832 * Removes the specified item from the array if it exists
1834 * @param {Array} array The array
1835 * @param {Object} item The item to remove
1836 * @return {Array} The passed array itself
1838 remove: function(array, item) {
1839 var index = ExtArray.indexOf(array, item);
1842 erase(array, index, 1);
1849 * Push an item into the array only if the array doesn't contain it yet
1851 * @param {Array} array The array
1852 * @param {Object} item The item to include
1854 include: function(array, item) {
1855 if (!ExtArray.contains(array, item)) {
1861 * Clone a flat array without referencing the previous one. Note that this is different
1862 * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1863 * for Array.prototype.slice.call(array)
1865 * @param {Array} array The array
1866 * @return {Array} The clone array
1868 clone: function(array) {
1869 return slice.call(array);
1873 * Merge multiple arrays into one with unique items.
1875 * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1877 * @param {Array} array1
1878 * @param {Array} array2
1879 * @param {Array} etc
1880 * @return {Array} merged
1883 var args = slice.call(arguments),
1887 for (i = 0, ln = args.length; i < ln; i++) {
1888 array = array.concat(args[i]);
1891 return ExtArray.unique(array);
1895 * Merge multiple arrays into one with unique items that exist in all of the arrays.
1897 * @param {Array} array1
1898 * @param {Array} array2
1899 * @param {Array} etc
1900 * @return {Array} intersect
1902 intersect: function() {
1904 arrays = slice.call(arguments),
1905 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1907 if (!arrays.length) {
1911 // Find the smallest array
1912 for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1913 if (!minArray || array.length < minArray.length) {
1919 minArray = ExtArray.unique(minArray);
1920 erase(arrays, x, 1);
1922 // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1923 // an item in the small array, we're likely to find it before reaching the end
1924 // of the inner loop and can terminate the search early.
1925 for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1928 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1929 for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1937 if (count === arraysLn) {
1946 * Perform a set difference A-B by subtracting all items in array B from array A.
1948 * @param {Array} arrayA
1949 * @param {Array} arrayB
1950 * @return {Array} difference
1952 difference: function(arrayA, arrayB) {
1953 var clone = slice.call(arrayA),
1957 for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1958 for (j = 0; j < ln; j++) {
1959 if (clone[j] === arrayB[i]) {
1971 * Returns a shallow copy of a part of an array. This is equivalent to the native
1972 * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1973 * is "arguments" since the arguments object does not supply a slice method but can
1974 * be the context object to Array.prototype.slice.
1976 * @param {Array} array The array (or arguments object).
1977 * @param {Number} begin The index at which to begin. Negative values are offsets from
1978 * the end of the array.
1979 * @param {Number} end The index at which to end. The copied items do not include
1980 * end. Negative values are offsets from the end of the array. If end is omitted,
1981 * all items up to the end of the array are copied.
1982 * @return {Array} The copied piece of the array.
1984 // Note: IE6 will return [] on slice.call(x, undefined).
1985 slice: ([1,2].slice(1, undefined).length ?
1986 function (array, begin, end) {
1987 return slice.call(array, begin, end);
1989 // at least IE6 uses arguments.length for variadic signature
1990 function (array, begin, end) {
1991 // After tested for IE 6, the one below is of the best performance
1992 // see http://jsperf.com/slice-fix
1993 if (typeof begin === 'undefined') {
1994 return slice.call(array);
1996 if (typeof end === 'undefined') {
1997 return slice.call(array, begin);
1999 return slice.call(array, begin, end);
2004 * Sorts the elements of an Array.
2005 * By default, this method sorts the elements alphabetically and ascending.
2007 * @param {Array} array The array to sort.
2008 * @param {Function} sortFn (optional) The comparison function.
2009 * @return {Array} The sorted array.
2011 sort: function(array, sortFn) {
2014 return array.sort(sortFn);
2016 return array.sort();
2020 var length = array.length,
2025 for (; i < length; i++) {
2027 for (j = i + 1; j < length; j++) {
2029 comparison = sortFn(array[j], array[min]);
2030 if (comparison < 0) {
2033 } else if (array[j] < array[min]) {
2039 array[i] = array[min];
2048 * Recursively flattens into 1-d Array. Injects Arrays inline.
2050 * @param {Array} array The array to flatten
2051 * @return {Array} The 1-d array.
2053 flatten: function(array) {
2056 function rFlatten(a) {
2059 for (i = 0, ln = a.length; i < ln; i++) {
2062 if (Ext.isArray(v)) {
2072 return rFlatten(array);
2076 * Returns the minimum value in the Array.
2078 * @param {Array/NodeList} array The Array from which to select the minimum value.
2079 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2080 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2081 * @return {Object} minValue The minimum value
2083 min: function(array, comparisonFn) {
2087 for (i = 0, ln = array.length; i < ln; i++) {
2091 if (comparisonFn(min, item) === 1) {
2106 * Returns the maximum value in the Array.
2108 * @param {Array/NodeList} array The Array from which to select the maximum value.
2109 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2110 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2111 * @return {Object} maxValue The maximum value
2113 max: function(array, comparisonFn) {
2117 for (i = 0, ln = array.length; i < ln; i++) {
2121 if (comparisonFn(max, item) === -1) {
2136 * Calculates the mean of all items in the array.
2138 * @param {Array} array The Array to calculate the mean value of.
2139 * @return {Number} The mean.
2141 mean: function(array) {
2142 return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2146 * Calculates the sum of all items in the given array.
2148 * @param {Array} array The Array to calculate the sum value of.
2149 * @return {Number} The sum.
2151 sum: function(array) {
2155 for (i = 0,ln = array.length; i < ln; i++) {
2164 _replaceSim: replaceSim, // for unit testing
2165 _spliceSim: spliceSim,
2168 * Removes items from an array. This is functionally equivalent to the splice method
2169 * of Array, but works around bugs in IE8's splice method and does not copy the
2170 * removed elements in order to return them (because very often they are ignored).
2172 * @param {Array} array The Array on which to replace.
2173 * @param {Number} index The index in the array at which to operate.
2174 * @param {Number} removeCount The number of items to remove at index.
2175 * @return {Array} The array passed.
2181 * Inserts items in to an array.
2183 * @param {Array} array The Array on which to replace.
2184 * @param {Number} index The index in the array at which to operate.
2185 * @param {Array} items The array of items to insert at index.
2186 * @return {Array} The array passed.
2188 insert: function (array, index, items) {
2189 return replace(array, index, 0, items);
2193 * Replaces items in an array. This is functionally equivalent to the splice method
2194 * of Array, but works around bugs in IE8's splice method and is often more convenient
2195 * to call because it accepts an array of items to insert rather than use a variadic
2198 * @param {Array} array The Array on which to replace.
2199 * @param {Number} index The index in the array at which to operate.
2200 * @param {Number} removeCount The number of items to remove at index (can be 0).
2201 * @param {Array} insert (optional) An array of items to insert at index.
2202 * @return {Array} The array passed.
2208 * Replaces items in an array. This is equivalent to the splice method of Array, but
2209 * works around bugs in IE8's splice method. The signature is exactly the same as the
2210 * splice method except that the array is the first argument. All arguments following
2211 * removeCount are inserted in the array at index.
2213 * @param {Array} array The Array on which to replace.
2214 * @param {Number} index The index in the array at which to operate.
2215 * @param {Number} removeCount The number of items to remove at index (can be 0).
2216 * @return {Array} An array containing the removed items.
2225 * @alias Ext.Array#each
2227 Ext.each = ExtArray.each;
2232 * @alias Ext.Array#merge
2234 ExtArray.union = ExtArray.merge;
2237 * Old alias to {@link Ext.Array#min}
2238 * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2241 * @alias Ext.Array#min
2243 Ext.min = ExtArray.min;
2246 * Old alias to {@link Ext.Array#max}
2247 * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2250 * @alias Ext.Array#max
2252 Ext.max = ExtArray.max;
2255 * Old alias to {@link Ext.Array#sum}
2256 * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2259 * @alias Ext.Array#sum
2261 Ext.sum = ExtArray.sum;
2264 * Old alias to {@link Ext.Array#mean}
2265 * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2268 * @alias Ext.Array#mean
2270 Ext.mean = ExtArray.mean;
2273 * Old alias to {@link Ext.Array#flatten}
2274 * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2277 * @alias Ext.Array#flatten
2279 Ext.flatten = ExtArray.flatten;
2282 * Old alias to {@link Ext.Array#clean}
2283 * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2286 * @alias Ext.Array#clean
2288 Ext.clean = ExtArray.clean;
2291 * Old alias to {@link Ext.Array#unique}
2292 * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2295 * @alias Ext.Array#unique
2297 Ext.unique = ExtArray.unique;
2300 * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2301 * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2304 * @alias Ext.Array#pluck
2306 Ext.pluck = ExtArray.pluck;
2311 * @alias Ext.Array#toArray
2313 Ext.toArray = function() {
2314 return ExtArray.toArray.apply(ExtArray, arguments);
2319 * @class Ext.Function
2321 * A collection of useful static methods to deal with function callbacks
2327 * A very commonly used method throughout the framework. It acts as a wrapper around another method
2328 * which originally accepts 2 arguments for `name` and `value`.
2329 * The wrapped function then allows "flexible" value setting of either:
2331 * - `name` and `value` as 2 arguments
2332 * - one single object argument with multiple key - value pairs
2336 * var setValue = Ext.Function.flexSetter(function(name, value) {
2337 * this[name] = value;
2341 * // Setting a single name - value
2342 * setValue('name1', 'value1');
2344 * // Settings multiple name - value pairs
2351 * @param {Function} setter
2352 * @returns {Function} flexSetter
2354 flexSetter: function(fn) {
2355 return function(a, b) {
2362 if (typeof a !== 'string') {
2364 if (a.hasOwnProperty(k)) {
2365 fn.call(this, k, a[k]);
2369 if (Ext.enumerables) {
2370 for (i = Ext.enumerables.length; i--;) {
2371 k = Ext.enumerables[i];
2372 if (a.hasOwnProperty(k)) {
2373 fn.call(this, k, a[k]);
2378 fn.call(this, a, b);
2386 * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2387 * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2389 * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2391 * @param {Function} fn The function to delegate.
2392 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2393 * **If omitted, defaults to the browser window.**
2394 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2395 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2396 * if a number the args are inserted at the specified position
2397 * @return {Function} The new function
2399 bind: function(fn, scope, args, appendArgs) {
2400 if (arguments.length === 2) {
2402 return fn.apply(scope, arguments);
2407 slice = Array.prototype.slice;
2410 var callArgs = args || arguments;
2412 if (appendArgs === true) {
2413 callArgs = slice.call(arguments, 0);
2414 callArgs = callArgs.concat(args);
2416 else if (typeof appendArgs == 'number') {
2417 callArgs = slice.call(arguments, 0); // copy arguments first
2418 Ext.Array.insert(callArgs, appendArgs, args);
2421 return method.apply(scope || window, callArgs);
2426 * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2427 * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2428 * This is especially useful when creating callbacks.
2432 * var originalFunction = function(){
2433 * alert(Ext.Array.from(arguments).join(' '));
2436 * var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2438 * callback(); // alerts 'Hello World'
2439 * callback('by Me'); // alerts 'Hello World by Me'
2441 * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2443 * @param {Function} fn The original function
2444 * @param {Array} args The arguments to pass to new callback
2445 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2446 * @return {Function} The new callback function
2448 pass: function(fn, args, scope) {
2450 args = Ext.Array.from(args);
2454 return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2459 * Create an alias to the provided method property with name `methodName` of `object`.
2460 * Note that the execution scope will still be bound to the provided `object` itself.
2462 * @param {Object/Function} object
2463 * @param {String} methodName
2464 * @return {Function} aliasFn
2466 alias: function(object, methodName) {
2468 return object[methodName].apply(object, arguments);
2473 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2474 * the original one is not called. The resulting function returns the results of the original function.
2475 * The passed function is called with the parameters of the original function. Example usage:
2477 * var sayHi = function(name){
2478 * alert('Hi, ' + name);
2481 * sayHi('Fred'); // alerts "Hi, Fred"
2483 * // create a new function that validates input without
2484 * // directly modifying the original function:
2485 * var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2486 * return name == 'Brian';
2489 * sayHiToFriend('Fred'); // no alert
2490 * sayHiToFriend('Brian'); // alerts "Hi, Brian"
2492 * @param {Function} origFn The original function.
2493 * @param {Function} newFn The function to call before the original
2494 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2495 * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2496 * @param {Object} returnValue (optional) The value to return if the passed function return false (defaults to null).
2497 * @return {Function} The new function
2499 createInterceptor: function(origFn, newFn, scope, returnValue) {
2500 var method = origFn;
2501 if (!Ext.isFunction(newFn)) {
2509 newFn.method = origFn;
2510 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2516 * Creates a delegate (callback) which, when called, executes after a specific delay.
2518 * @param {Function} fn The function which will be called on a delay when the returned function is called.
2519 * Optionally, a replacement (or additional) argument list may be specified.
2520 * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2521 * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2522 * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2523 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2524 * if a number the args are inserted at the specified position.
2525 * @return {Function} A function which, when called, executes the original function after the specified delay.
2527 createDelayed: function(fn, delay, scope, args, appendArgs) {
2528 if (scope || args) {
2529 fn = Ext.Function.bind(fn, scope, args, appendArgs);
2533 setTimeout(function() {
2534 fn.apply(me, arguments);
2540 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2542 * var sayHi = function(name){
2543 * alert('Hi, ' + name);
2546 * // executes immediately:
2549 * // executes after 2 seconds:
2550 * Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2552 * // this syntax is sometimes useful for deferring
2553 * // execution of an anonymous function:
2554 * Ext.Function.defer(function(){
2555 * alert('Anonymous');
2558 * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2560 * @param {Function} fn The function to defer.
2561 * @param {Number} millis The number of milliseconds for the setTimeout call
2562 * (if less than or equal to 0 the function is executed immediately)
2563 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2564 * **If omitted, defaults to the browser window.**
2565 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2566 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2567 * if a number the args are inserted at the specified position
2568 * @return {Number} The timeout id that can be used with clearTimeout
2570 defer: function(fn, millis, obj, args, appendArgs) {
2571 fn = Ext.Function.bind(fn, obj, args, appendArgs);
2573 return setTimeout(fn, millis);
2580 * Create a combined function call sequence of the original function + the passed function.
2581 * The resulting function returns the results of the original function.
2582 * The passed function is called with the parameters of the original function. Example usage:
2584 * var sayHi = function(name){
2585 * alert('Hi, ' + name);
2588 * sayHi('Fred'); // alerts "Hi, Fred"
2590 * var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2591 * alert('Bye, ' + name);
2594 * sayGoodbye('Fred'); // both alerts show
2596 * @param {Function} origFn The original function.
2597 * @param {Function} newFn The function to sequence
2598 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2599 * If omitted, defaults to the scope in which the original function is called or the browser window.
2600 * @return {Function} The new function
2602 createSequence: function(origFn, newFn, scope) {
2603 if (!Ext.isFunction(newFn)) {
2608 var retval = origFn.apply(this || window, arguments);
2609 newFn.apply(scope || this || window, arguments);
2616 * Creates a delegate function, optionally with a bound scope which, when called, buffers
2617 * the execution of the passed function for the configured number of milliseconds.
2618 * If called again within that period, the impending invocation will be canceled, and the
2619 * timeout period will begin again.
2621 * @param {Function} fn The function to invoke on a buffered timer.
2622 * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2624 * @param {Object} scope (optional) The scope (`this` reference) in which
2625 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2626 * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2627 * passed by the caller.
2628 * @return {Function} A function which invokes the passed function after buffering for the specified time.
2630 createBuffered: function(fn, buffer, scope, args) {
2636 clearTimeout(timerId);
2639 timerId = setTimeout(function(){
2640 fn.apply(scope || me, args || arguments);
2647 * Creates a throttled version of the passed function which, when called repeatedly and
2648 * rapidly, invokes the passed function only after a certain interval has elapsed since the
2649 * previous invocation.
2651 * This is useful for wrapping functions which may be called repeatedly, such as
2652 * a handler of a mouse move event when the processing is expensive.
2654 * @param {Function} fn The function to execute at a regular time interval.
2655 * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2656 * @param {Object} scope (optional) The scope (`this` reference) in which
2657 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2658 * @returns {Function} A function which invokes the passed function at the specified interval.
2660 createThrottled: function(fn, interval, scope) {
2661 var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2662 fn.apply(scope || this, lastArgs);
2663 lastCallTime = new Date().getTime();
2667 elapsed = new Date().getTime() - lastCallTime;
2668 lastArgs = arguments;
2670 clearTimeout(timer);
2671 if (!lastCallTime || (elapsed >= interval)) {
2674 timer = setTimeout(execute, interval - elapsed);
2680 * Adds behavior to an existing method that is executed before the
2681 * original behavior of the function. For example:
2685 * add: function(ingredient) {
2686 * this.contents.push(ingredient);
2689 * Ext.Function.interceptBefore(soup, "add", function(ingredient){
2690 * if (!this.contents.length && ingredient !== "water") {
2691 * // Always add water to start with
2692 * this.contents.push("water");
2695 * soup.add("onions");
2697 * soup.contents; // will contain: water, onions, salt
2699 * @param {Object} object The target object
2700 * @param {String} methodName Name of the method to override
2701 * @param {Function} fn Function with the new behavior. It will
2702 * be called with the same arguments as the original method. The
2703 * return value of this function will be the return value of the
2705 * @return {Function} The new function just created.
2707 interceptBefore: function(object, methodName, fn) {
2708 var method = object[methodName] || Ext.emptyFn;
2710 return object[methodName] = function() {
2711 var ret = fn.apply(this, arguments);
2712 method.apply(this, arguments);
2719 * Adds behavior to an existing method that is executed after the
2720 * original behavior of the function. For example:
2724 * add: function(ingredient) {
2725 * this.contents.push(ingredient);
2728 * Ext.Function.interceptAfter(soup, "add", function(ingredient){
2729 * // Always add a bit of extra salt
2730 * this.contents.push("salt");
2732 * soup.add("water");
2733 * soup.add("onions");
2734 * soup.contents; // will contain: water, salt, onions, salt
2736 * @param {Object} object The target object
2737 * @param {String} methodName Name of the method to override
2738 * @param {Function} fn Function with the new behavior. It will
2739 * be called with the same arguments as the original method. The
2740 * return value of this function will be the return value of the
2742 * @return {Function} The new function just created.
2744 interceptAfter: function(object, methodName, fn) {
2745 var method = object[methodName] || Ext.emptyFn;
2747 return object[methodName] = function() {
2748 method.apply(this, arguments);
2749 return fn.apply(this, arguments);
2757 * @alias Ext.Function#defer
2759 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2764 * @alias Ext.Function#pass
2766 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2771 * @alias Ext.Function#bind
2773 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2776 * @author Jacky Nguyen <jacky@sencha.com>
2777 * @docauthor Jacky Nguyen <jacky@sencha.com>
2780 * A collection of useful static methods to deal with objects.
2787 var ExtObject = Ext.Object = {
2790 * Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
2791 * query strings. For example:
2793 * var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2795 * // objects then equals:
2797 * { name: 'hobbies', value: 'reading' },
2798 * { name: 'hobbies', value: 'cooking' },
2799 * { name: 'hobbies', value: 'swimming' },
2802 * var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2810 * }, true); // Recursive
2812 * // objects then equals:
2814 * { name: 'dateOfBirth[day]', value: 3 },
2815 * { name: 'dateOfBirth[month]', value: 8 },
2816 * { name: 'dateOfBirth[year]', value: 1987 },
2817 * { name: 'dateOfBirth[extra][hour]', value: 4 },
2818 * { name: 'dateOfBirth[extra][minute]', value: 30 },
2821 * @param {String} name
2822 * @param {Object/Array} value
2823 * @param {Boolean} [recursive=false] True to traverse object recursively
2826 toQueryObjects: function(name, value, recursive) {
2827 var self = ExtObject.toQueryObjects,
2831 if (Ext.isArray(value)) {
2832 for (i = 0, ln = value.length; i < ln; i++) {
2834 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2844 else if (Ext.isObject(value)) {
2846 if (value.hasOwnProperty(i)) {
2848 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2870 * Takes an object and converts it to an encoded query string.
2874 * Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2875 * Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2876 * Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2877 * Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2878 * Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2882 * Ext.Object.toQueryString({
2883 * username: 'Jacky',
2889 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2890 * }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2892 * // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2893 * // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2895 * @param {Object} object The object to encode
2896 * @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
2897 * (PHP / Ruby on Rails servers and similar).
2898 * @return {String} queryString
2900 toQueryString: function(object, recursive) {
2901 var paramObjects = [],
2903 i, j, ln, paramObject, value;
2906 if (object.hasOwnProperty(i)) {
2907 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2911 for (j = 0, ln = paramObjects.length; j < ln; j++) {
2912 paramObject = paramObjects[j];
2913 value = paramObject.value;
2915 if (Ext.isEmpty(value)) {
2918 else if (Ext.isDate(value)) {
2919 value = Ext.Date.toString(value);
2922 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2925 return params.join('&');
2929 * Converts a query string back into an object.
2933 * Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2934 * Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2935 * Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2936 * Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2940 * 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);
2943 * username: 'Jacky',
2949 * hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2952 * @param {String} queryString The query string to decode
2953 * @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
2954 * PHP / Ruby on Rails servers and similar.
2957 fromQueryString: function(queryString, recursive) {
2958 var parts = queryString.replace(/^\?/, '').split('&'),
2960 temp, components, name, value, i, ln,
2961 part, j, subLn, matchedKeys, matchedName,
2964 for (i = 0, ln = parts.length; i < ln; i++) {
2967 if (part.length > 0) {
2968 components = part.split('=');
2969 name = decodeURIComponent(components[0]);
2970 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2973 if (object.hasOwnProperty(name)) {
2974 if (!Ext.isArray(object[name])) {
2975 object[name] = [object[name]];
2978 object[name].push(value);
2981 object[name] = value;
2985 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2986 matchedName = name.match(/^([^\[]+)/);
2990 sourceClass: "Ext.Object",
2991 sourceMethod: "fromQueryString",
2992 queryString: queryString,
2993 recursive: recursive,
2994 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2998 name = matchedName[0];
3001 if (matchedKeys === null) {
3002 object[name] = value;
3006 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
3007 key = matchedKeys[j];
3008 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
3016 for (j = 0, subLn = keys.length; j < subLn; j++) {
3019 if (j === subLn - 1) {
3020 if (Ext.isArray(temp) && key === '') {
3028 if (temp[key] === undefined || typeof temp[key] === 'string') {
3029 nextKey = keys[j+1];
3031 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
3045 * Iterates through an object and invokes the given callback function for each iteration.
3046 * The iteration can be stopped by returning `false` in the callback function. For example:
3050 * hairColor: 'black'
3051 * loves: ['food', 'sleeping', 'wife']
3054 * Ext.Object.each(person, function(key, value, myself) {
3055 * console.log(key + ":" + value);
3057 * if (key === 'hairColor') {
3058 * return false; // stop the iteration
3062 * @param {Object} object The object to iterate
3063 * @param {Function} fn The callback function.
3064 * @param {String} fn.key
3065 * @param {Object} fn.value
3066 * @param {Object} fn.object The object itself
3067 * @param {Object} [scope] The execution scope (`this`) of the callback function
3069 each: function(object, fn, scope) {
3070 for (var property in object) {
3071 if (object.hasOwnProperty(property)) {
3072 if (fn.call(scope || object, property, object[property], object) === false) {
3080 * Merges any number of objects recursively without referencing them or their children.
3083 * companyName: 'Ext JS',
3084 * products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
3088 * location: 'Palo Alto',
3094 * companyName: 'Sencha Inc.',
3095 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3098 * location: 'Redwood City'
3102 * var sencha = Ext.Object.merge(extjs, newStuff);
3104 * // extjs and sencha then equals to
3106 * companyName: 'Sencha Inc.',
3107 * products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3111 * location: 'Redwood City'
3116 * @param {Object...} object Any number of objects to merge.
3117 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3119 merge: function(source, key, value) {
3120 if (typeof key === 'string') {
3121 if (value && value.constructor === Object) {
3122 if (source[key] && source[key].constructor === Object) {
3123 ExtObject.merge(source[key], value);
3126 source[key] = Ext.clone(value);
3130 source[key] = value;
3137 ln = arguments.length,
3140 for (; i < ln; i++) {
3141 object = arguments[i];
3143 for (property in object) {
3144 if (object.hasOwnProperty(property)) {
3145 ExtObject.merge(source, property, object[property]);
3154 * Returns the first matching key corresponding to the given value.
3155 * If no matching value is found, null is returned.
3162 * alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
3164 * @param {Object} object
3165 * @param {Object} value The value to find
3167 getKey: function(object, value) {
3168 for (var property in object) {
3169 if (object.hasOwnProperty(property) && object[property] === value) {
3178 * Gets all values of the given object as an array.
3180 * var values = Ext.Object.getValues({
3183 * }); // ['Jacky', 'food']
3185 * @param {Object} object
3186 * @return {Array} An array of values from the object
3188 getValues: function(object) {
3192 for (property in object) {
3193 if (object.hasOwnProperty(property)) {
3194 values.push(object[property]);
3202 * Gets all keys of the given object as an array.
3204 * var values = Ext.Object.getKeys({
3207 * }); // ['name', 'loves']
3209 * @param {Object} object
3210 * @return {String[]} An array of keys from the object
3213 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3217 for (property in object) {
3218 if (object.hasOwnProperty(property)) {
3219 keys.push(property);
3227 * Gets the total number of this object's own properties
3229 * var size = Ext.Object.getSize({
3232 * }); // size equals 2
3234 * @param {Object} object
3235 * @return {Number} size
3237 getSize: function(object) {
3241 for (property in object) {
3242 if (object.hasOwnProperty(property)) {
3253 * A convenient alias method for {@link Ext.Object#merge}.
3257 * @alias Ext.Object#merge
3259 Ext.merge = Ext.Object.merge;
3262 * Alias for {@link Ext.Object#toQueryString}.
3266 * @alias Ext.Object#toQueryString
3267 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
3269 Ext.urlEncode = function() {
3270 var args = Ext.Array.from(arguments),
3273 // Support for the old `pre` argument
3274 if ((typeof args[1] === 'string')) {
3275 prefix = args[1] + '&';
3279 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3283 * Alias for {@link Ext.Object#fromQueryString}.
3287 * @alias Ext.Object#fromQueryString
3288 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
3290 Ext.urlDecode = function() {
3291 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3298 * A set of useful static methods to deal with date
3299 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3300 * this object for convenience
3302 * The date parsing and formatting syntax contains a subset of
3303 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3304 * supported will provide results equivalent to their PHP versions.
3306 * The following is a list of all currently supported formats:
3308 Format Description Example returned values
3309 ------ ----------------------------------------------------------------------- -----------------------
3310 d Day of the month, 2 digits with leading zeros 01 to 31
3311 D A short textual representation of the day of the week Mon to Sun
3312 j Day of the month without leading zeros 1 to 31
3313 l A full textual representation of the day of the week Sunday to Saturday
3314 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
3315 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
3316 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
3317 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
3318 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
3319 F A full textual representation of a month, such as January or March January to December
3320 m Numeric representation of a month, with leading zeros 01 to 12
3321 M A short textual representation of a month Jan to Dec
3322 n Numeric representation of a month, without leading zeros 1 to 12
3323 t Number of days in the given month 28 to 31
3324 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
3325 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
3326 belongs to the previous or next year, that year is used instead)
3327 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
3328 y A two digit representation of a year Examples: 99 or 03
3329 a Lowercase Ante meridiem and Post meridiem am or pm
3330 A Uppercase Ante meridiem and Post meridiem AM or PM
3331 g 12-hour format of an hour without leading zeros 1 to 12
3332 G 24-hour format of an hour without leading zeros 0 to 23
3333 h 12-hour format of an hour with leading zeros 01 to 12
3334 H 24-hour format of an hour with leading zeros 00 to 23
3335 i Minutes, with leading zeros 00 to 59
3336 s Seconds, with leading zeros 00 to 59
3337 u Decimal fraction of a second Examples:
3338 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
3339 100 (i.e. 0.100s) or
3340 999 (i.e. 0.999s) or
3341 999876543210 (i.e. 0.999876543210s)
3342 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
3343 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
3344 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
3345 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
3348 1) If unspecified, the month / day defaults to the current month / day, 1991 or
3349 the time defaults to midnight, while the timezone defaults to the 1992-10 or
3350 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3351 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
3352 are optional. 1995-07-18T17:21:28-02:00 or
3353 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
3354 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
3355 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
3356 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
3357 date-time granularity which are supported, or see 2000-02-13T21:25:33
3358 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
3359 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
3360 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3361 \/Date(1238606590509+0800)\/
3364 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3367 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3369 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3370 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
3371 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
3372 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
3375 * Here are some standard date/time patterns that you might find helpful. They
3376 * are not part of the source of Ext.Date, but to use them you can simply copy this
3377 * block of code into any script that is included after Ext.Date and they will also become
3378 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
3380 Ext.Date.patterns = {
3381 ISO8601Long:"Y-m-d H:i:s",
3382 ISO8601Short:"Y-m-d",
3384 LongDate: "l, F d, Y",
3385 FullDateTime: "l, F d, Y g:i:s A",
3388 LongTime: "g:i:s A",
3389 SortableDateTime: "Y-m-d\\TH:i:s",
3390 UniversalSortableDateTime: "Y-m-d H:i:sO",
3397 var dt = new Date();
3398 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3400 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3401 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3406 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3407 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3408 * They generate precompiled functions from format patterns instead of parsing and
3409 * processing each pattern every time a date is formatted. These functions are available
3410 * on every Date object.
3415 // create private copy of Ext's Ext.util.Format.format() method
3416 // - to remove unnecessary dependency
3417 // - to resolve namespace conflict with MS-Ajax's implementation
3418 function xf(format) {
3419 var args = Array.prototype.slice.call(arguments, 1);
3420 return format.replace(/\{(\d+)\}/g, function(m, i) {
3427 * Returns the current timestamp
3428 * @return {Date} The current timestamp
3431 now: Date.now || function() {
3439 toString: function(date) {
3440 var pad = Ext.String.leftPad;
3442 return date.getFullYear() + "-"
3443 + pad(date.getMonth() + 1, 2, '0') + "-"
3444 + pad(date.getDate(), 2, '0') + "T"
3445 + pad(date.getHours(), 2, '0') + ":"
3446 + pad(date.getMinutes(), 2, '0') + ":"
3447 + pad(date.getSeconds(), 2, '0');
3451 * Returns the number of milliseconds between two dates
3452 * @param {Date} dateA The first date
3453 * @param {Date} dateB (optional) The second date, defaults to now
3454 * @return {Number} The difference in milliseconds
3456 getElapsed: function(dateA, dateB) {
3457 return Math.abs(dateA - (dateB || new Date()));
3461 * Global flag which determines if strict date parsing should be used.
3462 * Strict date parsing will not roll-over invalid dates, which is the
3463 * default behaviour of javascript Date objects.
3464 * (see {@link #parse} for more information)
3465 * Defaults to <tt>false</tt>.
3471 formatCodeToRegex: function(character, currentGroup) {
3472 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3473 var p = utilDate.parseCodes[character];
3476 p = typeof p == 'function'? p() : p;
3477 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3480 return p ? Ext.applyIf({
3481 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3485 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3490 * <p>An object hash in which each property is a date parsing function. The property name is the
3491 * format string which that function parses.</p>
3492 * <p>This object is automatically populated with date parsing functions as
3493 * date formats are requested for Ext standard formatting strings.</p>
3494 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3495 * may be used as a format string to {@link #parse}.<p>
3496 * <p>Example:</p><pre><code>
3497 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3499 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3500 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3501 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3502 * (i.e. prevent javascript Date "rollover") (The default must be false).
3503 * Invalid date strings should return null when parsed.</div></li>
3505 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3506 * formatting function must be placed into the {@link #formatFunctions} property.
3507 * @property parseFunctions
3511 "MS": function(input, strict) {
3512 // note: the timezone offset is ignored since the MS Ajax server sends
3513 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3514 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3515 var r = (input || '').match(re);
3516 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3522 * <p>An object hash in which each property is a date formatting function. The property name is the
3523 * format string which corresponds to the produced formatted date string.</p>
3524 * <p>This object is automatically populated with date formatting functions as
3525 * date formats are requested for Ext standard formatting strings.</p>
3526 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3527 * may be used as a format string to {@link #format}. Example:</p><pre><code>
3528 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3530 * <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>
3531 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3533 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3534 * parsing function must be placed into the {@link #parseFunctions} property.
3535 * @property formatFunctions
3540 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3541 return '\\/Date(' + this.getTime() + ')\\/';
3548 * Date interval constant
3554 * Date interval constant
3560 * Date interval constant
3565 /** Date interval constant
3571 * Date interval constant
3577 * Date interval constant
3583 * Date interval constant
3589 * <p>An object hash containing default date values used during date parsing.</p>
3590 * <p>The following properties are available:<div class="mdetail-params"><ul>
3591 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3592 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3593 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3594 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3595 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3596 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3597 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3599 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3600 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3601 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3602 * It is the responsiblity of the developer to account for this.</b></p>
3605 // set default day value to the first day of the month
3606 Ext.Date.defaults.d = 1;
3608 // parse a February date string containing only year and month values.
3609 // setting the default day value to 1 prevents weird date rollover issues
3610 // when attempting to parse the following date string on, for example, March 31st 2009.
3611 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3613 * @property defaults
3619 * @property {String[]} dayNames
3620 * An array of textual day names.
3621 * Override these values for international dates.
3624 Ext.Date.dayNames = [
3642 * @property {String[]} monthNames
3643 * An array of textual month names.
3644 * Override these values for international dates.
3647 Ext.Date.monthNames = [
3670 * @property {Object} monthNumbers
3671 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3672 * Override these values for international dates.
3675 Ext.Date.monthNumbers = {
3676 'ShortJanNameInYourLang':0,
3677 'ShortFebNameInYourLang':1,
3697 * @property {String} defaultFormat
3698 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3699 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
3700 * <p>This may be overridden in a locale file.</p>
3702 defaultFormat : "m/d/Y",
3704 * Get the short month name for the given month number.
3705 * Override this function for international dates.
3706 * @param {Number} month A zero-based javascript month number.
3707 * @return {String} The short month name.
3709 getShortMonthName : function(month) {
3710 return utilDate.monthNames[month].substring(0, 3);
3714 * Get the short day name for the given day number.
3715 * Override this function for international dates.
3716 * @param {Number} day A zero-based javascript day number.
3717 * @return {String} The short day name.
3719 getShortDayName : function(day) {
3720 return utilDate.dayNames[day].substring(0, 3);
3724 * Get the zero-based javascript month number for the given short/full month name.
3725 * Override this function for international dates.
3726 * @param {String} name The short/full month name.
3727 * @return {Number} The zero-based javascript month number.
3729 getMonthNumber : function(name) {
3730 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3731 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3735 * Checks if the specified format contains hour information
3736 * @param {String} format The format to check
3737 * @return {Boolean} True if the format contains hour information
3740 formatContainsHourInfo : (function(){
3741 var stripEscapeRe = /(\\.)/g,
3742 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3743 return function(format){
3744 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3749 * Checks if the specified format contains information about
3750 * anything other than the time.
3751 * @param {String} format The format to check
3752 * @return {Boolean} True if the format contains information about
3753 * date/day information.
3756 formatContainsDateInfo : (function(){
3757 var stripEscapeRe = /(\\.)/g,
3758 dateInfoRe = /([djzmnYycU]|MS)/;
3760 return function(format){
3761 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3766 * The base format-code to formatting-function hashmap used by the {@link #format} method.
3767 * Formatting functions are strings (or functions which return strings) which
3768 * will return the appropriate value when evaluated in the context of the Date object
3769 * from which the {@link #format} method is called.
3770 * Add to / override these mappings for custom date formatting.
3771 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3774 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3775 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3780 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3781 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3782 j: "this.getDate()",
3783 l: "Ext.Date.dayNames[this.getDay()]",
3784 N: "(this.getDay() ? this.getDay() : 7)",
3785 S: "Ext.Date.getSuffix(this)",
3787 z: "Ext.Date.getDayOfYear(this)",
3788 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3789 F: "Ext.Date.monthNames[this.getMonth()]",
3790 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3791 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3792 n: "(this.getMonth() + 1)",
3793 t: "Ext.Date.getDaysInMonth(this)",
3794 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3795 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3796 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3797 y: "('' + this.getFullYear()).substring(2, 4)",
3798 a: "(this.getHours() < 12 ? 'am' : 'pm')",
3799 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3800 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3801 G: "this.getHours()",
3802 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3803 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3804 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3805 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3806 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3807 O: "Ext.Date.getGMTOffset(this)",
3808 P: "Ext.Date.getGMTOffset(this, true)",
3809 T: "Ext.Date.getTimezone(this)",
3810 Z: "(this.getTimezoneOffset() * -60)",
3812 c: function() { // ISO-8601 -- GMT format
3813 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3814 var e = c.charAt(i);
3815 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3817 return code.join(" + ");
3820 c: function() { // ISO-8601 -- UTC format
3822 "this.getUTCFullYear()", "'-'",
3823 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3824 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3826 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3827 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3828 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3834 U: "Math.round(this.getTime() / 1000)"
3838 * Checks if the passed Date parameters will cause a javascript Date "rollover".
3839 * @param {Number} year 4-digit year
3840 * @param {Number} month 1-based month-of-year
3841 * @param {Number} day Day of month
3842 * @param {Number} hour (optional) Hour
3843 * @param {Number} minute (optional) Minute
3844 * @param {Number} second (optional) Second
3845 * @param {Number} millisecond (optional) Millisecond
3846 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3848 isValid : function(y, m, d, h, i, s, ms) {
3855 // Special handling for year < 100
3856 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3858 return y == dt.getFullYear() &&
3859 m == dt.getMonth() + 1 &&
3860 d == dt.getDate() &&
3861 h == dt.getHours() &&
3862 i == dt.getMinutes() &&
3863 s == dt.getSeconds() &&
3864 ms == dt.getMilliseconds();
3868 * Parses the passed string using the specified date format.
3869 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3870 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3871 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3872 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3873 * Keep in mind that the input date string must precisely match the specified format string
3874 * in order for the parse operation to be successful (failed parse operations return a null value).
3875 * <p>Example:</p><pre><code>
3876 //dt = Fri May 25 2007 (current date)
3877 var dt = new Date();
3879 //dt = Thu May 25 2006 (today's month/day in 2006)
3880 dt = Ext.Date.parse("2006", "Y");
3882 //dt = Sun Jan 15 2006 (all date parts specified)
3883 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3885 //dt = Sun Jan 15 2006 15:20:01
3886 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3888 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3889 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3891 * @param {String} input The raw date string.
3892 * @param {String} format The expected date string format.
3893 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3894 (defaults to false). Invalid date strings will return null when parsed.
3895 * @return {Date} The parsed Date.
3897 parse : function(input, format, strict) {
3898 var p = utilDate.parseFunctions;
3899 if (p[format] == null) {
3900 utilDate.createParser(format);
3902 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3906 parseDate: function(input, format, strict){
3907 return utilDate.parse(input, format, strict);
3912 getFormatCode : function(character) {
3913 var f = utilDate.formatCodes[character];
3916 f = typeof f == 'function'? f() : f;
3917 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3920 // note: unknown characters are treated as literals
3921 return f || ("'" + Ext.String.escape(character) + "'");
3925 createFormat : function(format) {
3930 for (var i = 0; i < format.length; ++i) {
3931 ch = format.charAt(i);
3932 if (!special && ch == "\\") {
3934 } else if (special) {
3936 code.push("'" + Ext.String.escape(ch) + "'");
3938 code.push(utilDate.getFormatCode(ch));
3941 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3945 createParser : (function() {
3947 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3948 "def = Ext.Date.defaults,",
3949 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3954 "if(u != null){", // i.e. unix time is defined
3955 "v = new Date(u * 1000);", // give top priority to UNIX time
3957 // create Date object representing midnight of the current day;
3958 // this will provide us with our date defaults
3959 // (note: clearTime() handles Daylight Saving Time automatically)
3960 "dt = Ext.Date.clearTime(new Date);",
3962 // date calculations (note: these calculations create a dependency on Ext.Number.from())
3963 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3964 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3965 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3967 // time calculations (note: these calculations create a dependency on Ext.Number.from())
3968 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3969 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3970 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3971 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3973 "if(z >= 0 && y >= 0){",
3974 // both the year and zero-based day of year are defined and >= 0.
3975 // these 2 values alone provide sufficient info to create a full date object
3977 // create Date object representing January 1st for the given year
3978 // handle years < 100 appropriately
3979 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3981 // then add day of year, checking for Date "rollover" if necessary
3982 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3983 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3984 "v = null;", // invalid date, so return null
3986 // plain old Date object
3987 // handle years < 100 properly
3988 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3994 // favour UTC offset over GMT offset
3996 // reset to UTC, then add offset
3997 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3999 // reset to GMT, then add offset
4000 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
4007 return function(format) {
4008 var regexNum = utilDate.parseRegexes.length,
4015 for (var i = 0; i < format.length; ++i) {
4016 ch = format.charAt(i);
4017 if (!special && ch == "\\") {
4019 } else if (special) {
4021 regex.push(Ext.String.escape(ch));
4023 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
4024 currentGroup += obj.g;
4026 if (obj.g && obj.c) {
4032 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4033 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
4041 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4042 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4043 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4047 c:"d = parseInt(results[{0}], 10);\n",
4048 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4052 c:"d = parseInt(results[{0}], 10);\n",
4053 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4056 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
4060 s:"(?:" + a.join("|") +")"
4067 s:"(?:" + utilDate.dayNames.join("|") + ")"
4073 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4083 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4087 c:"z = parseInt(results[{0}], 10);\n",
4088 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4093 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4098 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4099 s:"(" + utilDate.monthNames.join("|") + ")"
4103 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4104 return Ext.applyIf({
4105 s:"(" + a.join("|") + ")"
4106 }, utilDate.formatCodeToRegex("F"));
4110 c:"m = parseInt(results[{0}], 10) - 1;\n",
4111 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4115 c:"m = parseInt(results[{0}], 10) - 1;\n",
4116 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4121 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4129 return utilDate.formatCodeToRegex("Y");
4133 c:"y = parseInt(results[{0}], 10);\n",
4134 s:"(\\d{4})" // 4-digit year
4138 c:"var ty = parseInt(results[{0}], 10);\n"
4139 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4143 * In the am/pm parsing routines, we allow both upper and lower case
4144 * even though it doesn't exactly match the spec. It gives much more flexibility
4145 * in being able to specify case insensitive regexes.
4149 c:"if (/(am)/i.test(results[{0}])) {\n"
4150 + "if (!h || h == 12) { h = 0; }\n"
4151 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4156 c:"if (/(am)/i.test(results[{0}])) {\n"
4157 + "if (!h || h == 12) { h = 0; }\n"
4158 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4162 return utilDate.formatCodeToRegex("G");
4166 c:"h = parseInt(results[{0}], 10);\n",
4167 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4170 return utilDate.formatCodeToRegex("H");
4174 c:"h = parseInt(results[{0}], 10);\n",
4175 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4179 c:"i = parseInt(results[{0}], 10);\n",
4180 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4184 c:"s = parseInt(results[{0}], 10);\n",
4185 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4189 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4190 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4195 "o = results[{0}];",
4196 "var sn = o.substring(0,1),", // get + / - sign
4197 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4198 "mn = o.substring(3,5) % 60;", // get minutes
4199 "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
4201 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4206 "o = results[{0}];",
4207 "var sn = o.substring(0,1),", // get + / - sign
4208 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4209 "mn = o.substring(4,6) % 60;", // get minutes
4210 "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
4212 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4217 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4221 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4222 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4223 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4228 utilDate.formatCodeToRegex("Y", 1), // year
4229 utilDate.formatCodeToRegex("m", 2), // month
4230 utilDate.formatCodeToRegex("d", 3), // day
4231 utilDate.formatCodeToRegex("h", 4), // hour
4232 utilDate.formatCodeToRegex("i", 5), // minute
4233 utilDate.formatCodeToRegex("s", 6), // second
4234 {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)
4235 {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
4236 "if(results[8]) {", // timezone specified
4237 "if(results[8] == 'Z'){",
4239 "}else if (results[8].indexOf(':') > -1){",
4240 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4242 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4248 for (var i = 0, l = arr.length; i < l; ++i) {
4249 calc.push(arr[i].c);
4256 arr[0].s, // year (required)
4257 "(?:", "-", arr[1].s, // month (optional)
4258 "(?:", "-", arr[2].s, // day (optional)
4260 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4261 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
4262 "(?::", arr[5].s, ")?", // seconds (optional)
4263 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4264 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4273 c:"u = parseInt(results[{0}], 10);\n",
4274 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4278 //Old Ext.Date prototype methods.
4280 dateFormat: function(date, format) {
4281 return utilDate.format(date, format);
4285 * Formats a date given the supplied format string.
4286 * @param {Date} date The date to format
4287 * @param {String} format The format string
4288 * @return {String} The formatted date
4290 format: function(date, format) {
4291 if (utilDate.formatFunctions[format] == null) {
4292 utilDate.createFormat(format);
4294 var result = utilDate.formatFunctions[format].call(date);
4299 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4301 * Note: The date string returned by the javascript Date object's toString() method varies
4302 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4303 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4304 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4305 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4306 * from the GMT offset portion of the date string.
4307 * @param {Date} date The date
4308 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4310 getTimezone : function(date) {
4311 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4313 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4314 // 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)
4315 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4316 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4317 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4319 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4320 // step 1: (?:\((.*)\) -- find timezone in parentheses
4321 // 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
4322 // step 3: remove all non uppercase characters found in step 1 and 2
4323 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4327 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4328 * @param {Date} date The date
4329 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4330 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4332 getGMTOffset : function(date, colon) {
4333 var offset = date.getTimezoneOffset();
4334 return (offset > 0 ? "-" : "+")
4335 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4336 + (colon ? ":" : "")
4337 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4341 * Get the numeric day number of the year, adjusted for leap year.
4342 * @param {Date} date The date
4343 * @return {Number} 0 to 364 (365 in leap years).
4345 getDayOfYear: function(date) {
4347 d = Ext.Date.clone(date),
4348 m = date.getMonth(),
4351 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4352 num += utilDate.getDaysInMonth(d);
4354 return num + date.getDate() - 1;
4358 * Get the numeric ISO-8601 week number of the year.
4359 * (equivalent to the format specifier 'W', but without a leading zero).
4360 * @param {Date} date The date
4361 * @return {Number} 1 to 53
4364 getWeekOfYear : (function() {
4365 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4366 var ms1d = 864e5, // milliseconds in a day
4367 ms7d = 7 * ms1d; // milliseconds in a week
4369 return function(date) { // return a closure so constants get calculated only once
4370 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4371 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4372 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4374 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4379 * Checks if the current date falls within a leap year.
4380 * @param {Date} date The date
4381 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4383 isLeapYear : function(date) {
4384 var year = date.getFullYear();
4385 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4389 * Get the first day of the current month, adjusted for leap year. The returned value
4390 * is the numeric day index within the week (0-6) which can be used in conjunction with
4391 * the {@link #monthNames} array to retrieve the textual day name.
4394 var dt = new Date('1/10/2007'),
4395 firstDay = Ext.Date.getFirstDayOfMonth(dt);
4396 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4398 * @param {Date} date The date
4399 * @return {Number} The day number (0-6).
4401 getFirstDayOfMonth : function(date) {
4402 var day = (date.getDay() - (date.getDate() - 1)) % 7;
4403 return (day < 0) ? (day + 7) : day;
4407 * Get the last day of the current month, adjusted for leap year. The returned value
4408 * is the numeric day index within the week (0-6) which can be used in conjunction with
4409 * the {@link #monthNames} array to retrieve the textual day name.
4412 var dt = new Date('1/10/2007'),
4413 lastDay = Ext.Date.getLastDayOfMonth(dt);
4414 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4416 * @param {Date} date The date
4417 * @return {Number} The day number (0-6).
4419 getLastDayOfMonth : function(date) {
4420 return utilDate.getLastDateOfMonth(date).getDay();
4425 * Get the date of the first day of the month in which this date resides.
4426 * @param {Date} date The date
4429 getFirstDateOfMonth : function(date) {
4430 return new Date(date.getFullYear(), date.getMonth(), 1);
4434 * Get the date of the last day of the month in which this date resides.
4435 * @param {Date} date The date
4438 getLastDateOfMonth : function(date) {
4439 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4443 * Get the number of days in the current month, adjusted for leap year.
4444 * @param {Date} date The date
4445 * @return {Number} The number of days in the month.
4448 getDaysInMonth: (function() {
4449 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4451 return function(date) { // return a closure for efficiency
4452 var m = date.getMonth();
4454 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4459 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4460 * @param {Date} date The date
4461 * @return {String} 'st, 'nd', 'rd' or 'th'.
4463 getSuffix : function(date) {
4464 switch (date.getDate()) {
4481 * Creates and returns a new Date instance with the exact same date value as the called instance.
4482 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4483 * variable will also be changed. When the intention is to create a new variable that will not
4484 * modify the original instance, you should create a clone.
4486 * Example of correctly cloning a date:
4489 var orig = new Date('10/1/2006');
4492 console.log(orig); //returns 'Thu Oct 05 2006'!
4495 var orig = new Date('10/1/2006'),
4496 copy = Ext.Date.clone(orig);
4498 console.log(orig); //returns 'Thu Oct 01 2006'
4500 * @param {Date} date The date
4501 * @return {Date} The new Date instance.
4503 clone : function(date) {
4504 return new Date(date.getTime());
4508 * Checks if the current date is affected by Daylight Saving Time (DST).
4509 * @param {Date} date The date
4510 * @return {Boolean} True if the current date is affected by DST.
4512 isDST : function(date) {
4513 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4514 // courtesy of @geoffrey.mcgill
4515 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4519 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4520 * automatically adjusting for Daylight Saving Time (DST) where applicable.
4521 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4522 * @param {Date} date The date
4523 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4524 * @return {Date} this or the clone.
4526 clearTime : function(date, clone) {
4528 return Ext.Date.clearTime(Ext.Date.clone(date));
4531 // get current date before clearing time
4532 var d = date.getDate();
4538 date.setMilliseconds(0);
4540 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4541 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4542 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4544 // increment hour until cloned date == current date
4545 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4548 date.setHours(c.getHours());
4555 * Provides a convenient method for performing basic date arithmetic. This method
4556 * does not modify the Date instance being called - it creates and returns
4557 * a new Date instance containing the resulting date value.
4562 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4563 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4565 // Negative values will be subtracted:
4566 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4567 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4571 * @param {Date} date The date to modify
4572 * @param {String} interval A valid date interval enum value.
4573 * @param {Number} value The amount to add to the current date.
4574 * @return {Date} The new Date instance.
4576 add : function(date, interval, value) {
4577 var d = Ext.Date.clone(date),
4579 if (!interval || value === 0) return d;
4581 switch(interval.toLowerCase()) {
4582 case Ext.Date.MILLI:
4583 d.setMilliseconds(d.getMilliseconds() + value);
4585 case Ext.Date.SECOND:
4586 d.setSeconds(d.getSeconds() + value);
4588 case Ext.Date.MINUTE:
4589 d.setMinutes(d.getMinutes() + value);
4592 d.setHours(d.getHours() + value);
4595 d.setDate(d.getDate() + value);
4597 case Ext.Date.MONTH:
4598 var day = date.getDate();
4600 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4603 d.setMonth(date.getMonth() + value);
4606 d.setFullYear(date.getFullYear() + value);
4613 * Checks if a date falls on or between the given start and end dates.
4614 * @param {Date} date The date to check
4615 * @param {Date} start Start date
4616 * @param {Date} end End date
4617 * @return {Boolean} true if this date falls on or between the given start and end dates.
4619 between : function(date, start, end) {
4620 var t = date.getTime();
4621 return start.getTime() <= t && t <= end.getTime();
4624 //Maintains compatibility with old static and prototype window.Date methods.
4625 compat: function() {
4626 var nativeDate = window.Date,
4628 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'],
4629 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4632 Ext.Array.forEach(statics, function(s) {
4633 nativeDate[s] = utilDate[s];
4636 //Append to prototype
4637 Ext.Array.forEach(proto, function(s) {
4638 nativeDate.prototype[s] = function() {
4639 var args = Array.prototype.slice.call(arguments);
4641 return utilDate[s].apply(utilDate, args);
4647 var utilDate = Ext.Date;
4652 * @author Jacky Nguyen <jacky@sencha.com>
4653 * @docauthor Jacky Nguyen <jacky@sencha.com>
4656 * The root of all classes created with {@link Ext#define}.
4658 * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
4659 * All prototype and static members of this class are inherited by all other classes.
4661 (function(flexSetter) {
4663 var Base = Ext.Base = function() {};
4665 $className: 'Ext.Base',
4670 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4671 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4672 * for a detailed comparison
4674 * Ext.define('My.Cat', {
4676 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4679 * constructor: function() {
4680 * alert(this.self.speciesName); / dependent on 'this'
4685 * clone: function() {
4686 * return new this.self();
4691 * Ext.define('My.SnowLeopard', {
4694 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4698 * var cat = new My.Cat(); // alerts 'Cat'
4699 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
4701 * var clone = snowLeopard.clone();
4702 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4709 // Default constructor, simply returns `this`
4710 constructor: function() {
4714 //<feature classSystem.config>
4716 * Initialize configuration for this class. a typical example:
4718 * Ext.define('My.awesome.Class', {
4719 * // The default config
4725 * constructor: function(config) {
4726 * this.initConfig(config);
4732 * var awesome = new My.awesome.Class({
4733 * name: 'Super Awesome'
4736 * alert(awesome.getName()); // 'Super Awesome'
4739 * @param {Object} config
4740 * @return {Object} mixins The mixin prototypes as key - value pairs
4742 initConfig: function(config) {
4743 if (!this.$configInited) {
4744 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4746 this.applyConfig(this.config);
4748 this.$configInited = true;
4757 setConfig: function(config) {
4758 this.applyConfig(config || {});
4766 applyConfig: flexSetter(function(name, value) {
4767 var setter = 'set' + Ext.String.capitalize(name);
4769 if (typeof this[setter] === 'function') {
4770 this[setter].call(this, value);
4778 * Call the parent's overridden method. For example:
4780 * Ext.define('My.own.A', {
4781 * constructor: function(test) {
4786 * Ext.define('My.own.B', {
4787 * extend: 'My.own.A',
4789 * constructor: function(test) {
4792 * this.callParent([test + 1]);
4796 * Ext.define('My.own.C', {
4797 * extend: 'My.own.B',
4799 * constructor: function() {
4800 * alert("Going to call parent's overriden constructor...");
4802 * this.callParent(arguments);
4806 * var a = new My.own.A(1); // alerts '1'
4807 * var b = new My.own.B(1); // alerts '1', then alerts '2'
4808 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4809 * // alerts '2', then alerts '3'
4812 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4813 * from the current method, for example: `this.callParent(arguments)`
4814 * @return {Object} Returns the result from the superclass' method
4816 callParent: function(args) {
4817 var method = this.callParent.caller,
4818 parentClass, methodName;
4820 if (!method.$owner) {
4821 if (!method.caller) {
4823 sourceClass: Ext.getClassName(this),
4824 sourceMethod: "callParent",
4825 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4829 method = method.caller;
4832 parentClass = method.$owner.superclass;
4833 methodName = method.$name;
4835 if (!(methodName in parentClass)) {
4837 sourceClass: Ext.getClassName(this),
4838 sourceMethod: methodName,
4839 msg: "this.callParent() was called but there's no such method (" + methodName +
4840 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4844 return parentClass[methodName].apply(this, args || []);
4849 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4850 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4851 * `this` points to during run-time
4853 * Ext.define('My.Cat', {
4856 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4859 * constructor: function() {
4860 * var statics = this.statics();
4862 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
4863 * // equivalent to: My.Cat.speciesName
4865 * alert(this.self.speciesName); // dependent on 'this'
4867 * statics.totalCreated++;
4872 * clone: function() {
4873 * var cloned = new this.self; // dependent on 'this'
4875 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
4882 * Ext.define('My.SnowLeopard', {
4886 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4889 * constructor: function() {
4890 * this.callParent();
4894 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
4896 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4898 * var clone = snowLeopard.clone();
4899 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4900 * alert(clone.groupName); // alerts 'Cat'
4902 * alert(My.Cat.totalCreated); // alerts 3
4905 * @return {Ext.Class}
4907 statics: function() {
4908 var method = this.statics.caller,
4915 return method.$owner;
4919 * Call the original method that was previously overridden with {@link Ext.Base#override}
4921 * Ext.define('My.Cat', {
4922 * constructor: function() {
4923 * alert("I'm a cat!");
4930 * constructor: function() {
4931 * alert("I'm going to be a cat!");
4933 * var instance = this.callOverridden();
4935 * alert("Meeeeoooowwww");
4941 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4942 * // alerts "I'm a cat!"
4943 * // alerts "Meeeeoooowwww"
4945 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4946 * @return {Object} Returns the result after calling the overridden method
4949 callOverridden: function(args) {
4950 var method = this.callOverridden.caller;
4952 if (!method.$owner) {
4954 sourceClass: Ext.getClassName(this),
4955 sourceMethod: "callOverridden",
4956 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4960 if (!method.$previous) {
4962 sourceClass: Ext.getClassName(this),
4963 sourceMethod: "callOverridden",
4964 msg: "this.callOverridden was called in '" + method.$name +
4965 "' but this method has never been overridden"
4969 return method.$previous.apply(this, args || []);
4972 destroy: function() {}
4975 // These static properties will be copied to every newly created class with {@link Ext#define}
4976 Ext.apply(Ext.Base, {
4978 * Create a new instance of this Class.
4980 * Ext.define('My.cool.Class', {
4984 * My.cool.Class.create({
4988 * All parameters are passed to the constructor of the class.
4990 * @return {Object} the created instance.
4994 create: function() {
4995 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
5002 own: function(name, value) {
5003 if (typeof value == 'function') {
5004 this.ownMethod(name, value);
5007 this.prototype[name] = value;
5015 ownMethod: function(name, fn) {
5018 if (typeof fn.$owner !== 'undefined' && fn !== Ext.emptyFn) {
5022 return originalFn.apply(this, arguments);
5027 className = Ext.getClassName(this);
5029 fn.displayName = className + '#' + name;
5034 this.prototype[name] = fn;
5038 * Add / override static properties of this class.
5040 * Ext.define('My.cool.Class', {
5044 * My.cool.Class.addStatics({
5045 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
5046 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
5047 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
5050 * @param {Object} members
5051 * @return {Ext.Base} this
5055 addStatics: function(members) {
5056 for (var name in members) {
5057 if (members.hasOwnProperty(name)) {
5058 this[name] = members[name];
5067 * @param {Object} members
5069 addInheritableStatics: function(members) {
5070 var inheritableStatics,
5071 hasInheritableStatics,
5072 prototype = this.prototype,
5075 inheritableStatics = prototype.$inheritableStatics;
5076 hasInheritableStatics = prototype.$hasInheritableStatics;
5078 if (!inheritableStatics) {
5079 inheritableStatics = prototype.$inheritableStatics = [];
5080 hasInheritableStatics = prototype.$hasInheritableStatics = {};
5083 var className = Ext.getClassName(this);
5085 for (name in members) {
5086 if (members.hasOwnProperty(name)) {
5087 member = members[name];
5088 if (typeof member == 'function') {
5089 member.displayName = className + '.' + name;
5091 this[name] = member;
5093 if (!hasInheritableStatics[name]) {
5094 hasInheritableStatics[name] = true;
5095 inheritableStatics.push(name);
5104 * Add methods / properties to the prototype of this class.
5106 * Ext.define('My.awesome.Cat', {
5107 * constructor: function() {
5112 * My.awesome.Cat.implement({
5113 * meow: function() {
5114 * alert('Meowww...');
5118 * var kitty = new My.awesome.Cat;
5121 * @param {Object} members
5125 implement: function(members) {
5126 var prototype = this.prototype,
5127 enumerables = Ext.enumerables,
5129 var className = Ext.getClassName(this);
5130 for (name in members) {
5131 if (members.hasOwnProperty(name)) {
5132 member = members[name];
5134 if (typeof member === 'function') {
5135 member.$owner = this;
5136 member.$name = name;
5138 member.displayName = className + '#' + name;
5142 prototype[name] = member;
5147 for (i = enumerables.length; i--;) {
5148 name = enumerables[i];
5150 if (members.hasOwnProperty(name)) {
5151 member = members[name];
5152 member.$owner = this;
5153 member.$name = name;
5154 prototype[name] = member;
5161 * Borrow another class' members to the prototype of this class.
5163 * Ext.define('Bank', {
5165 * printMoney: function() {
5170 * Ext.define('Thief', {
5174 * Thief.borrow(Bank, ['money', 'printMoney']);
5176 * var steve = new Thief();
5178 * alert(steve.money); // alerts '$$$'
5179 * steve.printMoney(); // alerts '$$$$$$$'
5181 * @param {Ext.Base} fromClass The class to borrow members from
5182 * @param {String/String[]} members The names of the members to borrow
5183 * @return {Ext.Base} this
5187 borrow: function(fromClass, members) {
5188 var fromPrototype = fromClass.prototype,
5191 members = Ext.Array.from(members);
5193 for (i = 0, ln = members.length; i < ln; i++) {
5194 member = members[i];
5196 this.own(member, fromPrototype[member]);
5203 * Override prototype members of this class. Overridden methods can be invoked via
5204 * {@link Ext.Base#callOverridden}
5206 * Ext.define('My.Cat', {
5207 * constructor: function() {
5208 * alert("I'm a cat!");
5215 * constructor: function() {
5216 * alert("I'm going to be a cat!");
5218 * var instance = this.callOverridden();
5220 * alert("Meeeeoooowwww");
5226 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5227 * // alerts "I'm a cat!"
5228 * // alerts "Meeeeoooowwww"
5230 * @param {Object} members
5231 * @return {Ext.Base} this
5235 override: function(members) {
5236 var prototype = this.prototype,
5237 enumerables = Ext.enumerables,
5238 name, i, member, previous;
5240 if (arguments.length === 2) {
5242 member = arguments[1];
5244 if (typeof member == 'function') {
5245 if (typeof prototype[name] == 'function') {
5246 previous = prototype[name];
5247 member.$previous = previous;
5250 this.ownMethod(name, member);
5253 prototype[name] = member;
5259 for (name in members) {
5260 if (members.hasOwnProperty(name)) {
5261 member = members[name];
5263 if (typeof member === 'function') {
5264 if (typeof prototype[name] === 'function') {
5265 previous = prototype[name];
5266 member.$previous = previous;
5269 this.ownMethod(name, member);
5272 prototype[name] = member;
5278 for (i = enumerables.length; i--;) {
5279 name = enumerables[i];
5281 if (members.hasOwnProperty(name)) {
5282 if (typeof prototype[name] !== 'undefined') {
5283 previous = prototype[name];
5284 members[name].$previous = previous;
5287 this.ownMethod(name, members[name]);
5295 //<feature classSystem.mixins>
5297 * Used internally by the mixins pre-processor
5301 mixin: function(name, cls) {
5302 var mixin = cls.prototype,
5303 my = this.prototype,
5306 for (key in mixin) {
5307 if (mixin.hasOwnProperty(key)) {
5308 if (typeof my[key] === 'undefined' && key !== 'mixins' && key !== 'mixinId') {
5309 if (typeof mixin[key] === 'function') {
5312 if (typeof fn.$owner === 'undefined') {
5313 this.ownMethod(key, fn);
5320 my[key] = mixin[key];
5323 //<feature classSystem.config>
5324 else if (key === 'config' && my.config && mixin.config) {
5325 Ext.Object.merge(my.config, mixin.config);
5331 if (typeof mixin.onClassMixedIn !== 'undefined') {
5332 mixin.onClassMixedIn.call(cls, this);
5335 if (!my.hasOwnProperty('mixins')) {
5336 if ('mixins' in my) {
5337 my.mixins = Ext.Object.merge({}, my.mixins);
5344 my.mixins[name] = mixin;
5349 * Get the current class' name in string format.
5351 * Ext.define('My.cool.Class', {
5352 * constructor: function() {
5353 * alert(this.self.getName()); // alerts 'My.cool.Class'
5357 * My.cool.Class.getName(); // 'My.cool.Class'
5359 * @return {String} className
5363 getName: function() {
5364 return Ext.getClassName(this);
5368 * Create aliases for existing prototype methods. Example:
5370 * Ext.define('My.cool.Class', {
5371 * method1: function() { ... },
5372 * method2: function() { ... }
5375 * var test = new My.cool.Class();
5377 * My.cool.Class.createAlias({
5378 * method3: 'method1',
5379 * method4: 'method2'
5382 * test.method3(); // test.method1()
5384 * My.cool.Class.createAlias('method5', 'method3');
5386 * test.method5(); // test.method3() -> test.method1()
5388 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5389 * {@link Ext.Function#flexSetter flexSetter}
5390 * @param {String/Object} origin The original method name
5395 createAlias: flexSetter(function(alias, origin) {
5396 this.prototype[alias] = function() {
5397 return this[origin].apply(this, arguments);
5402 })(Ext.Function.flexSetter);
5405 * @author Jacky Nguyen <jacky@sencha.com>
5406 * @docauthor Jacky Nguyen <jacky@sencha.com>
5409 * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
5410 * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
5411 * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
5413 * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
5414 * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
5416 * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
5417 * from, see {@link Ext.Base}.
5423 baseStaticProperties = [],
5426 for (baseStaticProperty in Base) {
5427 if (Base.hasOwnProperty(baseStaticProperty)) {
5428 baseStaticProperties.push(baseStaticProperty);
5433 * @method constructor
5434 * Creates new class.
5435 * @param {Object} classData An object represent the properties of this class
5436 * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
5437 * Note that the creation process can be asynchronous depending on the pre-processors used.
5438 * @return {Ext.Base} The newly created class
5440 Ext.Class = Class = function(newClass, classData, onClassCreated) {
5441 if (typeof newClass != 'function') {
5442 onClassCreated = classData;
5443 classData = newClass;
5444 newClass = function() {
5445 return this.constructor.apply(this, arguments);
5453 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5454 registeredPreprocessors = Class.getPreprocessors(),
5457 preprocessor, staticPropertyName, process, i, j, ln;
5459 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5460 staticPropertyName = baseStaticProperties[i];
5461 newClass[staticPropertyName] = Base[staticPropertyName];
5464 delete classData.preprocessors;
5466 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5467 preprocessor = preprocessorStack[j];
5469 if (typeof preprocessor == 'string') {
5470 preprocessor = registeredPreprocessors[preprocessor];
5472 if (!preprocessor.always) {
5473 if (classData.hasOwnProperty(preprocessor.name)) {
5474 preprocessors.push(preprocessor.fn);
5478 preprocessors.push(preprocessor.fn);
5482 preprocessors.push(preprocessor);
5486 classData.onClassCreated = onClassCreated || Ext.emptyFn;
5488 classData.onBeforeClassCreated = function(cls, data) {
5489 onClassCreated = data.onClassCreated;
5491 delete data.onBeforeClassCreated;
5492 delete data.onClassCreated;
5494 cls.implement(data);
5496 onClassCreated.call(cls, cls);
5499 process = function(cls, data) {
5500 preprocessor = preprocessors[index++];
5502 if (!preprocessor) {
5503 data.onBeforeClassCreated.apply(this, arguments);
5507 if (preprocessor.call(this, cls, data, process) !== false) {
5508 process.apply(this, arguments);
5512 process.call(Class, newClass, classData);
5523 * Register a new pre-processor to be used during the class creation process
5526 * @param {String} name The pre-processor's name
5527 * @param {Function} fn The callback function to be executed. Typical format:
5529 * function(cls, data, fn) {
5532 * // Execute this when the processing is finished.
5533 * // Asynchronous processing is perfectly ok
5535 * fn.call(this, cls, data);
5539 * @param {Function} fn.cls The created class
5540 * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
5541 * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
5542 * regardless of whether the processing is synchronous or aynchronous
5544 * @return {Ext.Class} this
5547 registerPreprocessor: function(name, fn, always) {
5548 this.preprocessors[name] = {
5550 always: always || false,
5558 * Retrieve a pre-processor callback function by its name, which has been registered before
5560 * @param {String} name
5561 * @return {Function} preprocessor
5564 getPreprocessor: function(name) {
5565 return this.preprocessors[name];
5568 getPreprocessors: function() {
5569 return this.preprocessors;
5573 * Retrieve the array stack of default pre-processors
5575 * @return {Function[]} defaultPreprocessors
5578 getDefaultPreprocessors: function() {
5579 return this.defaultPreprocessors || [];
5583 * Set the default array stack of default pre-processors
5585 * @param {Function/Function[]} preprocessors
5586 * @return {Ext.Class} this
5589 setDefaultPreprocessors: function(preprocessors) {
5590 this.defaultPreprocessors = Ext.Array.from(preprocessors);
5596 * Inserts this pre-processor at a specific position in the stack, optionally relative to
5597 * any existing pre-processor. For example:
5599 * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5603 * fn.call(this, cls, data);
5605 * }).setDefaultPreprocessorPosition('debug', 'last');
5607 * @param {String} name The pre-processor name. Note that it needs to be registered with
5608 * {@link #registerPreprocessor registerPreprocessor} before this
5609 * @param {String} offset The insertion position. Four possible values are:
5610 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5611 * @param {String} relativeName
5612 * @return {Ext.Class} this
5615 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5616 var defaultPreprocessors = this.defaultPreprocessors,
5619 if (typeof offset == 'string') {
5620 if (offset === 'first') {
5621 defaultPreprocessors.unshift(name);
5625 else if (offset === 'last') {
5626 defaultPreprocessors.push(name);
5631 offset = (offset === 'after') ? 1 : -1;
5634 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5637 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5645 * @cfg {String} extend
5646 * The parent class that this class extends. For example:
5648 * Ext.define('Person', {
5649 * say: function(text) { alert(text); }
5652 * Ext.define('Developer', {
5654 * say: function(text) { this.callParent(["print "+text]); }
5657 Class.registerPreprocessor('extend', function(cls, data) {
5658 var extend = data.extend,
5660 basePrototype = base.prototype,
5661 prototype = function() {},
5662 parent, i, k, ln, staticName, parentStatics,
5663 parentPrototype, clsPrototype;
5665 if (extend && extend !== Object) {
5672 parentPrototype = parent.prototype;
5674 prototype.prototype = parentPrototype;
5675 clsPrototype = cls.prototype = new prototype();
5677 if (!('$class' in parent)) {
5678 for (i in basePrototype) {
5679 if (!parentPrototype[i]) {
5680 parentPrototype[i] = basePrototype[i];
5685 clsPrototype.self = cls;
5687 cls.superclass = clsPrototype.superclass = parentPrototype;
5691 //<feature classSystem.inheritableStatics>
5692 // Statics inheritance
5693 parentStatics = parentPrototype.$inheritableStatics;
5695 if (parentStatics) {
5696 for (k = 0, ln = parentStatics.length; k < ln; k++) {
5697 staticName = parentStatics[k];
5699 if (!cls.hasOwnProperty(staticName)) {
5700 cls[staticName] = parent[staticName];
5706 //<feature classSystem.config>
5707 // Merge the parent class' config object without referencing it
5708 if (parentPrototype.config) {
5709 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5712 clsPrototype.config = {};
5716 //<feature classSystem.onClassExtended>
5717 if (clsPrototype.$onExtended) {
5718 clsPrototype.$onExtended.call(cls, cls, data);
5721 if (data.onClassExtended) {
5722 clsPrototype.$onExtended = data.onClassExtended;
5723 delete data.onClassExtended;
5729 //<feature classSystem.statics>
5731 * @cfg {Object} statics
5732 * List of static methods for this class. For example:
5734 * Ext.define('Computer', {
5736 * factory: function(brand) {
5737 * // 'this' in static methods refer to the class itself
5738 * return new this(brand);
5742 * constructor: function() { ... }
5745 * var dellComputer = Computer.factory('Dell');
5747 Class.registerPreprocessor('statics', function(cls, data) {
5748 cls.addStatics(data.statics);
5750 delete data.statics;
5754 //<feature classSystem.inheritableStatics>
5756 * @cfg {Object} inheritableStatics
5757 * List of inheritable static methods for this class.
5758 * Otherwise just like {@link #statics} but subclasses inherit these methods.
5760 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5761 cls.addInheritableStatics(data.inheritableStatics);
5763 delete data.inheritableStatics;
5767 //<feature classSystem.config>
5769 * @cfg {Object} config
5770 * List of configuration options with their default values, for which automatically
5771 * accessor methods are generated. For example:
5773 * Ext.define('SmartPhone', {
5775 * hasTouchScreen: false,
5776 * operatingSystem: 'Other',
5779 * constructor: function(cfg) {
5780 * this.initConfig(cfg);
5784 * var iPhone = new SmartPhone({
5785 * hasTouchScreen: true,
5786 * operatingSystem: 'iOS'
5789 * iPhone.getPrice(); // 500;
5790 * iPhone.getOperatingSystem(); // 'iOS'
5791 * iPhone.getHasTouchScreen(); // true;
5792 * iPhone.hasTouchScreen(); // true
5794 Class.registerPreprocessor('config', function(cls, data) {
5795 var prototype = cls.prototype;
5797 Ext.Object.each(data.config, function(name) {
5798 var cName = name.charAt(0).toUpperCase() + name.substr(1),
5800 apply = 'apply' + cName,
5801 setter = 'set' + cName,
5802 getter = 'get' + cName;
5804 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5805 data[apply] = function(val) {
5810 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5811 data[setter] = function(val) {
5812 var ret = this[apply].call(this, val, this[pName]);
5814 if (typeof ret != 'undefined') {
5822 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5823 data[getter] = function() {
5829 Ext.Object.merge(prototype.config, data.config);
5834 //<feature classSystem.mixins>
5836 * @cfg {Object} mixins
5837 * List of classes to mix into this class. For example:
5839 * Ext.define('CanSing', {
5840 * sing: function() {
5841 * alert("I'm on the highway to hell...")
5845 * Ext.define('Musician', {
5849 * canSing: 'CanSing'
5853 Class.registerPreprocessor('mixins', function(cls, data) {
5854 var mixins = data.mixins,
5859 Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
5860 if (mixins instanceof Array) {
5861 for (i = 0,ln = mixins.length; i < ln; i++) {
5863 name = mixin.prototype.mixinId || mixin.$className;
5865 cls.mixin(name, mixin);
5869 for (name in mixins) {
5870 if (mixins.hasOwnProperty(name)) {
5871 cls.mixin(name, mixins[name]);
5880 Class.setDefaultPreprocessors([
5882 //<feature classSystem.statics>
5885 //<feature classSystem.inheritableStatics>
5886 ,'inheritableStatics'
5888 //<feature classSystem.config>
5891 //<feature classSystem.mixins>
5896 //<feature classSystem.backwardsCompatible>
5897 // Backwards compatible
5898 Ext.extend = function(subclass, superclass, members) {
5899 if (arguments.length === 2 && Ext.isObject(superclass)) {
5900 members = superclass;
5901 superclass = subclass;
5908 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5911 members.extend = superclass;
5912 members.preprocessors = [
5914 //<feature classSystem.statics>
5917 //<feature classSystem.inheritableStatics>
5918 ,'inheritableStatics'
5920 //<feature classSystem.mixins>
5923 //<feature classSystem.config>
5929 cls = new Class(subclass, members);
5932 cls = new Class(members);
5935 cls.prototype.override = function(o) {
5937 if (o.hasOwnProperty(m)) {
5950 * @author Jacky Nguyen <jacky@sencha.com>
5951 * @docauthor Jacky Nguyen <jacky@sencha.com>
5952 * @class Ext.ClassManager
5954 * Ext.ClassManager manages all classes and handles mapping from string class name to
5955 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5956 * these convenient shorthands:
5958 * - {@link Ext#define Ext.define}
5959 * - {@link Ext#create Ext.create}
5960 * - {@link Ext#widget Ext.widget}
5961 * - {@link Ext#getClass Ext.getClass}
5962 * - {@link Ext#getClassName Ext.getClassName}
5966 * Ext.define(className, properties);
5968 * in which `properties` is an object represent a collection of properties that apply to the class. See
5969 * {@link Ext.ClassManager#create} for more detailed instructions.
5971 * Ext.define('Person', {
5974 * constructor: function(name) {
5982 * eat: function(foodType) {
5983 * alert("I'm eating: " + foodType);
5989 * var aaron = new Person("Aaron");
5990 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5992 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5993 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5997 * Ext.define('Developer', {
6000 * constructor: function(name, isGeek) {
6001 * this.isGeek = isGeek;
6003 * // Apply a method from the parent class' prototype
6004 * this.callParent([name]);
6010 * code: function(language) {
6011 * alert("I'm coding in: " + language);
6019 * var jacky = new Developer("Jacky", true);
6020 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
6021 * // alert("I'm eating: Bugs");
6023 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
6027 * Ext.define('CanPlayGuitar', {
6028 * playGuitar: function() {
6029 * alert("F#...G...D...A");
6033 * Ext.define('CanComposeSongs', {
6034 * composeSongs: function() { ... }
6037 * Ext.define('CanSing', {
6038 * sing: function() {
6039 * alert("I'm on the highway to hell...")
6043 * Ext.define('Musician', {
6047 * canPlayGuitar: 'CanPlayGuitar',
6048 * canComposeSongs: 'CanComposeSongs',
6049 * canSing: 'CanSing'
6053 * Ext.define('CoolPerson', {
6057 * canPlayGuitar: 'CanPlayGuitar',
6058 * canSing: 'CanSing'
6061 * sing: function() {
6062 * alert("Ahem....");
6064 * this.mixins.canSing.sing.call(this);
6066 * alert("[Playing guitar at the same time...]");
6068 * this.playGuitar();
6072 * var me = new CoolPerson("Jacky");
6074 * me.sing(); // alert("Ahem...");
6075 * // alert("I'm on the highway to hell...");
6076 * // alert("[Playing guitar at the same time...]");
6077 * // alert("F#...G...D...A");
6081 * Ext.define('SmartPhone', {
6083 * hasTouchScreen: false,
6084 * operatingSystem: 'Other',
6088 * isExpensive: false,
6090 * constructor: function(config) {
6091 * this.initConfig(config);
6096 * applyPrice: function(price) {
6097 * this.isExpensive = (price > 500);
6102 * applyOperatingSystem: function(operatingSystem) {
6103 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
6107 * return operatingSystem;
6111 * var iPhone = new SmartPhone({
6112 * hasTouchScreen: true,
6113 * operatingSystem: 'iOS'
6116 * iPhone.getPrice(); // 500;
6117 * iPhone.getOperatingSystem(); // 'iOS'
6118 * iPhone.getHasTouchScreen(); // true;
6119 * iPhone.hasTouchScreen(); // true
6121 * iPhone.isExpensive; // false;
6122 * iPhone.setPrice(600);
6123 * iPhone.getPrice(); // 600
6124 * iPhone.isExpensive; // true;
6126 * iPhone.setOperatingSystem('AlienOS');
6127 * iPhone.getOperatingSystem(); // 'Other'
6131 * Ext.define('Computer', {
6133 * factory: function(brand) {
6134 * // 'this' in static methods refer to the class itself
6135 * return new this(brand);
6139 * constructor: function() { ... }
6142 * var dellComputer = Computer.factory('Dell');
6144 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
6145 * static properties within class methods
6149 (function(Class, alias) {
6151 var slice = Array.prototype.slice;
6153 var Manager = Ext.ClassManager = {
6156 * @property {Object} classes
6157 * All classes which were defined through the ClassManager. Keys are the
6158 * name of the classes and the values are references to the classes.
6171 namespaceRewrites: [{
6180 alternateToName: {},
6186 enableNamespaceParseCache: true,
6189 namespaceParseCache: {},
6195 instantiationCounts: {},
6198 * Checks if a class has already been created.
6200 * @param {String} className
6201 * @return {Boolean} exist
6203 isCreated: function(className) {
6204 var i, ln, part, root, parts;
6206 if (typeof className !== 'string' || className.length < 1) {
6208 sourceClass: "Ext.ClassManager",
6209 sourceMethod: "exist",
6210 msg: "Invalid classname, must be a string and must not be empty"
6214 if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6219 parts = this.parseNamespace(className);
6221 for (i = 0, ln = parts.length; i < ln; i++) {
6224 if (typeof part !== 'string') {
6227 if (!root || !root[part]) {
6235 Ext.Loader.historyPush(className);
6237 this.existCache[className] = true;
6243 * Supports namespace rewriting
6246 parseNamespace: function(namespace) {
6247 if (typeof namespace !== 'string') {
6249 sourceClass: "Ext.ClassManager",
6250 sourceMethod: "parseNamespace",
6251 msg: "Invalid namespace, must be a string"
6255 var cache = this.namespaceParseCache;
6257 if (this.enableNamespaceParseCache) {
6258 if (cache.hasOwnProperty(namespace)) {
6259 return cache[namespace];
6264 rewrites = this.namespaceRewrites,
6265 rewrite, from, to, i, ln, root = Ext.global;
6267 for (i = 0, ln = rewrites.length; i < ln; i++) {
6268 rewrite = rewrites[i];
6269 from = rewrite.from;
6272 if (namespace === from || namespace.substring(0, from.length) === from) {
6273 namespace = namespace.substring(from.length);
6275 if (typeof to !== 'string') {
6278 parts = parts.concat(to.split('.'));
6287 parts = parts.concat(namespace.split('.'));
6289 if (this.enableNamespaceParseCache) {
6290 cache[namespace] = parts;
6297 * Creates a namespace and assign the `value` to the created object
6299 * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6301 * alert(MyCompany.pkg.Example === someObject); // alerts true
6303 * @param {String} name
6304 * @param {Object} value
6306 setNamespace: function(name, value) {
6307 var root = Ext.global,
6308 parts = this.parseNamespace(name),
6309 ln = parts.length - 1,
6313 for (i = 0; i < ln; i++) {
6316 if (typeof part !== 'string') {
6333 * The new Ext.ns, supports namespace rewriting
6336 createNamespaces: function() {
6337 var root = Ext.global,
6338 parts, part, i, j, ln, subLn;
6340 for (i = 0, ln = arguments.length; i < ln; i++) {
6341 parts = this.parseNamespace(arguments[i]);
6343 for (j = 0, subLn = parts.length; j < subLn; j++) {
6346 if (typeof part !== 'string') {
6362 * Sets a name reference to a class.
6364 * @param {String} name
6365 * @param {Object} value
6366 * @return {Ext.ClassManager} this
6368 set: function(name, value) {
6369 var targetName = this.getName(value);
6371 this.classes[name] = this.setNamespace(name, value);
6373 if (targetName && targetName !== name) {
6374 this.maps.alternateToName[name] = targetName;
6381 * Retrieve a class by its name.
6383 * @param {String} name
6384 * @return {Ext.Class} class
6386 get: function(name) {
6387 if (this.classes.hasOwnProperty(name)) {
6388 return this.classes[name];
6391 var root = Ext.global,
6392 parts = this.parseNamespace(name),
6395 for (i = 0, ln = parts.length; i < ln; i++) {
6398 if (typeof part !== 'string') {
6401 if (!root || !root[part]) {
6413 * Register the alias for a class.
6415 * @param {Ext.Class/String} cls a reference to a class or a className
6416 * @param {String} alias Alias to use when referring to this class
6418 setAlias: function(cls, alias) {
6419 var aliasToNameMap = this.maps.aliasToName,
6420 nameToAliasesMap = this.maps.nameToAliases,
6423 if (typeof cls === 'string') {
6426 className = this.getName(cls);
6429 if (alias && aliasToNameMap[alias] !== className) {
6430 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
6431 Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
6432 "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
6435 aliasToNameMap[alias] = className;
6438 if (!nameToAliasesMap[className]) {
6439 nameToAliasesMap[className] = [];
6443 Ext.Array.include(nameToAliasesMap[className], alias);
6450 * Get a reference to the class by its alias.
6452 * @param {String} alias
6453 * @return {Ext.Class} class
6455 getByAlias: function(alias) {
6456 return this.get(this.getNameByAlias(alias));
6460 * Get the name of a class by its alias.
6462 * @param {String} alias
6463 * @return {String} className
6465 getNameByAlias: function(alias) {
6466 return this.maps.aliasToName[alias] || '';
6470 * Get the name of a class by its alternate name.
6472 * @param {String} alternate
6473 * @return {String} className
6475 getNameByAlternate: function(alternate) {
6476 return this.maps.alternateToName[alternate] || '';
6480 * Get the aliases of a class by the class name
6482 * @param {String} name
6483 * @return {String[]} aliases
6485 getAliasesByName: function(name) {
6486 return this.maps.nameToAliases[name] || [];
6490 * Get the name of the class by its reference or its instance.
6492 * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6494 * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6496 * @param {Ext.Class/Object} object
6497 * @return {String} className
6499 getName: function(object) {
6500 return object && object.$className || '';
6504 * Get the class of the provided object; returns null if it's not an instance
6505 * of any class created with Ext.define.
6507 * var component = new Ext.Component();
6509 * Ext.ClassManager.getClass(component); // returns Ext.Component
6511 * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6513 * @param {Object} object
6514 * @return {Ext.Class} class
6516 getClass: function(object) {
6517 return object && object.self || null;
6523 * {@link Ext#define Ext.define} and {@link Ext.ClassManager#create Ext.ClassManager.create} are almost aliases
6524 * of each other, with the only exception that Ext.define allows definition of {@link Ext.Class#override overrides}.
6525 * To avoid trouble, always use Ext.define.
6527 * Ext.define('My.awesome.Class', {
6528 * someProperty: 'something',
6529 * someMethod: function() { ... }
6533 * alert('Created!');
6534 * alert(this === My.awesome.Class); // alerts true
6536 * var myInstance = new this();
6539 * @param {String} className The class name to create in string dot-namespaced format, for example:
6540 * `My.very.awesome.Class`, `FeedViewer.plugin.CoolPager`. It is highly recommended to follow this simple convention:
6542 * - The root and the class name are 'CamelCased'
6543 * - Everything else is lower-cased
6545 * @param {Object} data The key-value pairs of properties to apply to this class. Property names can be of any valid
6546 * strings, except those in the reserved list below:
6548 * - {@link Ext.Base#self self}
6549 * - {@link Ext.Class#alias alias}
6550 * - {@link Ext.Class#alternateClassName alternateClassName}
6551 * - {@link Ext.Class#config config}
6552 * - {@link Ext.Class#extend extend}
6553 * - {@link Ext.Class#inheritableStatics inheritableStatics}
6554 * - {@link Ext.Class#mixins mixins}
6555 * - {@link Ext.Class#override override} (only when using {@link Ext#define Ext.define})
6556 * - {@link Ext.Class#requires requires}
6557 * - {@link Ext.Class#singleton singleton}
6558 * - {@link Ext.Class#statics statics}
6559 * - {@link Ext.Class#uses uses}
6561 * @param {Function} [createdFn] callback to execute after the class is created, the execution scope of which
6562 * (`this`) will be the newly created class itself.
6564 * @return {Ext.Base}
6566 create: function(className, data, createdFn) {
6569 if (typeof className !== 'string') {
6572 sourceMethod: "define",
6573 msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6577 data.$className = className;
6579 return new Class(data, function() {
6580 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6581 registeredPostprocessors = manager.postprocessors,
6583 postprocessors = [],
6584 postprocessor, process, i, ln;
6586 delete data.postprocessors;
6588 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6589 postprocessor = postprocessorStack[i];
6591 if (typeof postprocessor === 'string') {
6592 postprocessor = registeredPostprocessors[postprocessor];
6594 if (!postprocessor.always) {
6595 if (data[postprocessor.name] !== undefined) {
6596 postprocessors.push(postprocessor.fn);
6600 postprocessors.push(postprocessor.fn);
6604 postprocessors.push(postprocessor);
6608 process = function(clsName, cls, clsData) {
6609 postprocessor = postprocessors[index++];
6611 if (!postprocessor) {
6612 manager.set(className, cls);
6614 Ext.Loader.historyPush(className);
6617 createdFn.call(cls, cls);
6623 if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6624 process.apply(this, arguments);
6628 process.call(manager, className, this, data);
6633 * Instantiate a class by its alias.
6635 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6636 * attempt to load the class via synchronous loading.
6638 * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6640 * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6642 * @param {String} alias
6643 * @param {Object...} args Additional arguments after the alias will be passed to the
6644 * class constructor.
6645 * @return {Object} instance
6647 instantiateByAlias: function() {
6648 var alias = arguments[0],
6649 args = slice.call(arguments),
6650 className = this.getNameByAlias(alias);
6653 className = this.maps.aliasToName[alias];
6658 sourceMethod: "createByAlias",
6659 msg: "Cannot create an instance of unrecognized alias: " + alias
6663 if (Ext.global.console) {
6664 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6665 "Ext.require('" + alias + "') above Ext.onReady");
6668 Ext.syncRequire(className);
6671 args[0] = className;
6673 return this.instantiate.apply(this, args);
6677 * Instantiate a class by either full name, alias or alternate name.
6679 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6680 * attempt to load the class via synchronous loading.
6682 * For example, all these three lines return the same result:
6685 * var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6688 * var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6690 * // full class name
6691 * var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6693 * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6695 * @param {String} name
6696 * @param {Object...} args Additional arguments after the name will be passed to the class' constructor.
6697 * @return {Object} instance
6699 instantiate: function() {
6700 var name = arguments[0],
6701 args = slice.call(arguments, 1),
6705 if (typeof name !== 'function') {
6706 if ((typeof name !== 'string' || name.length < 1)) {
6709 sourceMethod: "create",
6710 msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6714 cls = this.get(name);
6720 // No record of this class name, it's possibly an alias, so look it up
6722 possibleName = this.getNameByAlias(name);
6725 name = possibleName;
6727 cls = this.get(name);
6731 // Still no record of this class name, it's possibly an alternate name, so look it up
6733 possibleName = this.getNameByAlternate(name);
6736 name = possibleName;
6738 cls = this.get(name);
6742 // Still not existing at this point, try to load it via synchronous mode as the last resort
6744 if (Ext.global.console) {
6745 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6746 "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6749 Ext.syncRequire(name);
6751 cls = this.get(name);
6757 sourceMethod: "create",
6758 msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6762 if (typeof cls !== 'function') {
6765 sourceMethod: "create",
6766 msg: "'" + name + "' is a singleton and cannot be instantiated"
6770 if (!this.instantiationCounts[name]) {
6771 this.instantiationCounts[name] = 0;
6774 this.instantiationCounts[name]++;
6776 return this.getInstantiator(args.length)(cls, args);
6784 dynInstantiate: function(name, args) {
6785 args = Ext.Array.from(args, true);
6788 return this.instantiate.apply(this, args);
6795 getInstantiator: function(length) {
6796 if (!this.instantiators[length]) {
6800 for (i = 0; i < length; i++) {
6801 args.push('a['+i+']');
6804 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6807 return this.instantiators[length];
6818 defaultPostprocessors: [],
6821 * Register a post-processor function.
6823 * @param {String} name
6824 * @param {Function} postprocessor
6826 registerPostprocessor: function(name, fn, always) {
6827 this.postprocessors[name] = {
6829 always: always || false,
6837 * Set the default post processors array stack which are applied to every class.
6839 * @param {String/String[]} The name of a registered post processor or an array of registered names.
6840 * @return {Ext.ClassManager} this
6842 setDefaultPostprocessors: function(postprocessors) {
6843 this.defaultPostprocessors = Ext.Array.from(postprocessors);
6849 * Insert this post-processor at a specific position in the stack, optionally relative to
6850 * any existing post-processor
6852 * @param {String} name The post-processor name. Note that it needs to be registered with
6853 * {@link Ext.ClassManager#registerPostprocessor} before this
6854 * @param {String} offset The insertion position. Four possible values are:
6855 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6856 * @param {String} relativeName
6857 * @return {Ext.ClassManager} this
6859 setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6860 var defaultPostprocessors = this.defaultPostprocessors,
6863 if (typeof offset === 'string') {
6864 if (offset === 'first') {
6865 defaultPostprocessors.unshift(name);
6869 else if (offset === 'last') {
6870 defaultPostprocessors.push(name);
6875 offset = (offset === 'after') ? 1 : -1;
6878 index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6881 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6888 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6889 * or class names. Expressions support wildcards:
6891 * // returns ['Ext.window.Window']
6892 * var window = Ext.ClassManager.getNamesByExpression('widget.window');
6894 * // returns ['widget.panel', 'widget.window', ...]
6895 * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6897 * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6898 * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6900 * @param {String} expression
6901 * @return {String[]} classNames
6903 getNamesByExpression: function(expression) {
6904 var nameToAliasesMap = this.maps.nameToAliases,
6906 name, alias, aliases, possibleName, regex, i, ln;
6908 if (typeof expression !== 'string' || expression.length < 1) {
6910 sourceClass: "Ext.ClassManager",
6911 sourceMethod: "getNamesByExpression",
6912 msg: "Expression " + expression + " is invalid, must be a non-empty string"
6916 if (expression.indexOf('*') !== -1) {
6917 expression = expression.replace(/\*/g, '(.*?)');
6918 regex = new RegExp('^' + expression + '$');
6920 for (name in nameToAliasesMap) {
6921 if (nameToAliasesMap.hasOwnProperty(name)) {
6922 aliases = nameToAliasesMap[name];
6924 if (name.search(regex) !== -1) {
6928 for (i = 0, ln = aliases.length; i < ln; i++) {
6931 if (alias.search(regex) !== -1) {
6941 possibleName = this.getNameByAlias(expression);
6944 names.push(possibleName);
6946 possibleName = this.getNameByAlternate(expression);
6949 names.push(possibleName);
6951 names.push(expression);
6960 var defaultPostprocessors = Manager.defaultPostprocessors;
6961 //<feature classSystem.alias>
6964 * @cfg {String[]} alias
6966 * List of short aliases for class names. Most useful for defining xtypes for widgets:
6968 * Ext.define('MyApp.CoolPanel', {
6969 * extend: 'Ext.panel.Panel',
6970 * alias: ['widget.coolpanel'],
6974 * // Using Ext.create
6975 * Ext.widget('widget.coolpanel');
6976 * // Using the shorthand for widgets and in xtypes
6977 * Ext.widget('panel', {
6979 * {xtype: 'coolpanel', html: 'Foo'},
6980 * {xtype: 'coolpanel', html: 'Bar'}
6984 Manager.registerPostprocessor('alias', function(name, cls, data) {
6985 var aliases = data.alias,
6990 for (i = 0, ln = aliases.length; i < ln; i++) {
6993 this.setAlias(cls, alias);
6998 * @cfg {Boolean} singleton
7000 * When set to true, the class will be instantiated as singleton. For example:
7002 * Ext.define('Logger', {
7004 * log: function(msg) {
7009 * Logger.log('Hello');
7011 Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
7012 fn.call(this, name, new cls(), data);
7017 * @cfg {String/String[]} alternateClassName
7019 * Defines alternate names for this class. For example:
7021 * Ext.define('Developer', {
7022 * alternateClassName: ['Coder', 'Hacker'],
7023 * code: function(msg) {
7024 * alert('Typing... ' + msg);
7028 * var joe = Ext.create('Developer');
7029 * joe.code('stackoverflow');
7031 * var rms = Ext.create('Hacker');
7032 * rms.code('hack hack');
7034 Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
7035 var alternates = data.alternateClassName,
7038 if (!(alternates instanceof Array)) {
7039 alternates = [alternates];
7042 for (i = 0, ln = alternates.length; i < ln; i++) {
7043 alternate = alternates[i];
7045 if (typeof alternate !== 'string') {
7048 sourceMethod: "define",
7049 msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
7053 this.set(alternate, cls);
7057 Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
7063 * @alias Ext.ClassManager#instantiate
7065 create: alias(Manager, 'instantiate'),
7069 * API to be stablized
7071 * @param {Object} item
7072 * @param {String} namespace
7074 factory: function(item, namespace) {
7075 if (item instanceof Array) {
7078 for (i = 0, ln = item.length; i < ln; i++) {
7079 item[i] = Ext.factory(item[i], namespace);
7085 var isString = (typeof item === 'string');
7087 if (isString || (item instanceof Object && item.constructor === Object)) {
7088 var name, config = {};
7094 name = item.className;
7096 delete config.className;
7099 if (namespace !== undefined && name.indexOf(namespace) === -1) {
7100 name = namespace + '.' + Ext.String.capitalize(name);
7103 return Ext.create(name, config);
7106 if (typeof item === 'function') {
7107 return Ext.create(item);
7114 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
7116 * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
7117 * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
7121 * @param {String} name xtype of the widget to create.
7122 * @param {Object...} args arguments for the widget constructor.
7123 * @return {Object} widget instance
7125 widget: function(name) {
7126 var args = slice.call(arguments);
7127 args[0] = 'widget.' + name;
7129 return Manager.instantiateByAlias.apply(Manager, args);
7135 * @alias Ext.ClassManager#instantiateByAlias
7137 createByAlias: alias(Manager, 'instantiateByAlias'),
7140 * @cfg {String} override
7143 * Defines an override applied to a class. Note that **overrides can only be created using
7144 * {@link Ext#define}.** {@link Ext.ClassManager#create} only creates classes.
7146 * To define an override, include the override property. The content of an override is
7147 * aggregated with the specified class in order to extend or modify that class. This can be
7148 * as simple as setting default property values or it can extend and/or replace methods.
7149 * This can also extend the statics of the class.
7151 * One use for an override is to break a large class into manageable pieces.
7153 * // File: /src/app/Panel.js
7155 * Ext.define('My.app.Panel', {
7156 * extend: 'Ext.panel.Panel',
7158 * 'My.app.PanelPart2',
7159 * 'My.app.PanelPart3'
7162 * constructor: function (config) {
7163 * this.callSuper(arguments); // calls Ext.panel.Panel's constructor
7168 * method: function () {
7174 * // File: /src/app/PanelPart2.js
7175 * Ext.define('My.app.PanelPart2', {
7176 * override: 'My.app.Panel',
7178 * constructor: function (config) {
7179 * this.callSuper(arguments); // calls My.app.Panel's constructor
7184 * Another use of overrides is to provide optional parts of classes that can be
7185 * independently required. In this case, the class may even be unaware of the
7186 * override altogether.
7188 * Ext.define('My.ux.CoolTip', {
7189 * override: 'Ext.tip.ToolTip',
7191 * constructor: function (config) {
7192 * this.callSuper(arguments); // calls Ext.tip.ToolTip's constructor
7197 * The above override can now be required as normal.
7199 * Ext.define('My.app.App', {
7205 * Overrides can also contain statics:
7207 * Ext.define('My.app.BarMod', {
7208 * override: 'Ext.foo.Bar',
7211 * method: function (x) {
7212 * return this.callSuper([x * 2]); // call Ext.foo.Bar.method
7217 * IMPORTANT: An override is only included in a build if the class it overrides is
7218 * required. Otherwise, the override, like the target class, is not included.
7225 * @alias Ext.ClassManager#create
7227 define: function (className, data, createdFn) {
7228 if (!data.override) {
7229 return Manager.create.apply(Manager, arguments);
7232 var requires = data.requires,
7234 overrideName = className;
7236 className = data.override;
7238 // hoist any 'requires' or 'uses' from the body onto the faux class:
7239 data = Ext.apply({}, data);
7240 delete data.requires;
7242 delete data.override;
7244 // make sure className is in the requires list:
7245 if (typeof requires == 'string') {
7246 requires = [ className, requires ];
7247 } else if (requires) {
7248 requires = requires.slice(0);
7249 requires.unshift(className);
7251 requires = [ className ];
7254 // TODO - we need to rework this to allow the override to not require the target class
7255 // and rather 'wait' for it in such a way that if the target class is not in the build,
7256 // neither are any of its overrides.
7258 // Also, this should process the overrides for a class ASAP (ideally before any derived
7259 // classes) if the target class 'requires' the overrides. Without some special handling, the
7260 // overrides so required will be processed before the class and have to be bufferred even
7263 // TODO - we should probably support the "config" processor on an override (to config new
7264 // functionaliy like Aria) and maybe inheritableStatics (although static is now supported
7265 // by callSuper). If inheritableStatics causes those statics to be included on derived class
7266 // constructors, that probably means "no" to this since an override can come after other
7267 // classes extend the target.
7268 return Manager.create(overrideName, {
7272 constructor: function () {
7273 throw new Error("Cannot create override '" + overrideName + "'");
7276 var cls = Manager.get(className);
7277 if (cls.override) { // if (normal class)
7279 } else { // else (singleton)
7280 cls.self.override(data);
7284 // called once the override is applied and with the context of the
7285 // overridden class (the override itself is a meaningless, name-only
7287 createdFn.call(cls);
7295 * @alias Ext.ClassManager#getName
7297 getClassName: alias(Manager, 'getName'),
7300 * Returns the displayName property or className or object.
7301 * When all else fails, returns "Anonymous".
7302 * @param {Object} object
7305 getDisplayName: function(object) {
7306 if (object.displayName) {
7307 return object.displayName;
7310 if (object.$name && object.$class) {
7311 return Ext.getClassName(object.$class) + '#' + object.$name;
7314 if (object.$className) {
7315 return object.$className;
7324 * @alias Ext.ClassManager#getClass
7326 getClass: alias(Manager, 'getClass'),
7329 * Creates namespaces to be used for scoping variables and classes so that they are not global.
7330 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
7332 * Ext.namespace('Company', 'Company.data');
7334 * // equivalent and preferable to the above syntax
7335 * Ext.namespace('Company.data');
7337 * Company.Widget = function() { ... };
7339 * Company.data.CustomStore = function(config) { ... };
7343 * @param {String} namespace1
7344 * @param {String} namespace2
7345 * @param {String} etc
7346 * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7348 namespace: alias(Manager, 'createNamespaces')
7352 * Old name for {@link Ext#widget}.
7353 * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7358 Ext.createWidget = Ext.widget;
7361 * Convenient alias for {@link Ext#namespace Ext.namespace}
7364 * @alias Ext#namespace
7366 Ext.ns = Ext.namespace;
7368 Class.registerPreprocessor('className', function(cls, data) {
7369 if (data.$className) {
7370 cls.$className = data.$className;
7371 cls.displayName = cls.$className;
7375 Class.setDefaultPreprocessorPosition('className', 'first');
7377 Class.registerPreprocessor('xtype', function(cls, data) {
7378 var xtypes = Ext.Array.from(data.xtype),
7379 widgetPrefix = 'widget.',
7380 aliases = Ext.Array.from(data.alias),
7383 data.xtype = xtypes[0];
7384 data.xtypes = xtypes;
7386 aliases = data.alias = Ext.Array.from(data.alias);
7388 for (i = 0,ln = xtypes.length; i < ln; i++) {
7391 if (typeof xtype != 'string' || xtype.length < 1) {
7392 throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
7395 aliases.push(widgetPrefix + xtype);
7398 data.alias = aliases;
7401 Class.setDefaultPreprocessorPosition('xtype', 'last');
7403 Class.registerPreprocessor('alias', function(cls, data) {
7404 var aliases = Ext.Array.from(data.alias),
7405 xtypes = Ext.Array.from(data.xtypes),
7406 widgetPrefix = 'widget.',
7407 widgetPrefixLength = widgetPrefix.length,
7408 i, ln, alias, xtype;
7410 for (i = 0, ln = aliases.length; i < ln; i++) {
7413 if (typeof alias != 'string') {
7414 throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
7417 if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
7418 xtype = alias.substring(widgetPrefixLength);
7419 Ext.Array.include(xtypes, xtype);
7422 cls.xtype = data.xtype = xtype;
7427 data.alias = aliases;
7428 data.xtypes = xtypes;
7431 Class.setDefaultPreprocessorPosition('alias', 'last');
7433 })(Ext.Class, Ext.Function.alias);
7438 * @author Jacky Nguyen <jacky@sencha.com>
7439 * @docauthor Jacky Nguyen <jacky@sencha.com>
7441 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7442 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7443 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7446 * # Asynchronous Loading
7450 * + No web server needed: you can run the application via the file system protocol
7451 * (i.e: `file://path/to/your/index.html`)
7452 * + Best possible debugging experience: error messages come with the exact file name and line number
7455 * + Dependencies need to be specified before-hand
7457 * ### Method 1: Explicitly include what you need:
7460 * Ext.require({String/Array} expressions);
7462 * // Example: Single alias
7463 * Ext.require('widget.window');
7465 * // Example: Single class name
7466 * Ext.require('Ext.window.Window');
7468 * // Example: Multiple aliases / class names mix
7469 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7472 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7474 * ### Method 2: Explicitly exclude what you don't need:
7476 * // Syntax: Note that it must be in this chaining format.
7477 * Ext.exclude({String/Array} expressions)
7478 * .require({String/Array} expressions);
7480 * // Include everything except Ext.data.*
7481 * Ext.exclude('Ext.data.*').require('*');Â
7483 * // Include all widgets except widget.checkbox*,
7484 * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7485 * Ext.exclude('widget.checkbox*').require('widget.*');
7487 * # Synchronous Loading on Demand
7490 * + There's no need to specify dependencies before-hand, which is always the convenience of including
7494 * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7495 * + Must be from the same domain due to XHR restriction
7496 * + Need a web server, same reason as above
7498 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7500 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7502 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7504 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7506 * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7507 * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7508 * the given class and all its dependencies.
7510 * # Hybrid Loading - The Best of Both Worlds
7512 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7514 * ### Step 1: Start writing your application using synchronous approach.
7516 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7518 * Ext.onReady(function(){
7519 * var window = Ext.createWidget('window', {
7526 * title: 'Hello Dialog',
7528 * title: 'Navigation',
7529 * collapsible: true,
7535 * title: 'TabPanel',
7543 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7545 * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7546 * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7548 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7550 * Ext.require('Ext.window.Window');
7551 * Ext.require('Ext.layout.container.Border');
7555 * Everything should now load via asynchronous mode.
7559 * It's important to note that dynamic loading should only be used during development on your local machines.
7560 * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7561 * the whole process of transitioning from / to between development / maintenance and production as easy as
7562 * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7563 * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7564 * array into one, then include it on top of your application.
7566 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7568 (function(Manager, Class, flexSetter, alias) {
7571 dependencyProperties = ['extend', 'mixins', 'requires'],
7574 Loader = Ext.Loader = {
7578 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7581 * Flag indicating whether there are still files being loaded
7587 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7589 * requires: [...], // The required classes for this queue item
7590 * callback: function() { ... } // The function to execute when all classes specified in requires exist
7597 * Maintain the list of files that have already been handled so that they never get double-loaded
7603 * Maintain the list of listeners to execute when all required scripts are fully loaded
7609 * Contains optional dependencies to be loaded last
7612 optionalRequires: [],
7615 * Map of fully qualified class names to an array of dependent classes.
7631 hasFileLoadError: false,
7636 classNameToFilePathMap: {},
7639 * @property {String[]} history
7640 * An array of class names to keep track of the dependency loading order.
7641 * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7651 * @cfg {Boolean} enabled
7652 * Whether or not to enable the dynamic dependency loading feature.
7657 * @cfg {Boolean} disableCaching
7658 * Appends current timestamp to script files to prevent caching.
7660 disableCaching: true,
7663 * @cfg {String} disableCachingParam
7664 * The get parameter name for the cache buster's timestamp.
7666 disableCachingParam: '_dc',
7669 * @cfg {Object} paths
7670 * The mapping from namespaces to file paths
7673 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7674 * // loaded from ./layout/Container.js
7676 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
7677 * // ./src/my_own_folder/layout/Container.js
7680 * Note that all relative paths are relative to the current HTML document.
7681 * If not being specified, for example, `Other.awesome.Class`
7682 * will simply be loaded from `./Other/awesome/Class.js`
7690 * Set the configuration for the loader. This should be called right after ext-core.js
7691 * (or ext-core-debug.js) is included in the page, e.g.:
7693 * <script type="text/javascript" src="ext-core-debug.js"></script>
7694 * <script type="text/javascript">
7695 * Ext.Loader.setConfig({
7698 * 'My': 'my_own_path'
7702 * <script type="text/javascript">
7705 * Ext.onReady(function() {
7706 * // application code here
7710 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7712 * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
7713 * @param {Object} value (optional) The new value to set, needed if first parameter is String.
7714 * @return {Ext.Loader} this
7716 setConfig: function(name, value) {
7717 if (Ext.isObject(name) && arguments.length === 1) {
7718 Ext.Object.merge(this.config, name);
7721 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7728 * Get the config value corresponding to the specified name.
7729 * If no name is given, will return the config object.
7730 * @param {String} name The config property name
7733 getConfig: function(name) {
7735 return this.config[name];
7742 * Sets the path of a namespace. For Example:
7744 * Ext.Loader.setPath('Ext', '.');
7746 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7747 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7748 * @return {Ext.Loader} this
7751 setPath: flexSetter(function(name, path) {
7752 this.config.paths[name] = path;
7758 * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7761 * Ext.Loader.setPath('My', '/path/to/My');
7763 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7765 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7767 * Ext.Loader.setPath({
7768 * 'My': '/path/to/lib',
7769 * 'My.awesome': '/other/path/for/awesome/stuff',
7770 * 'My.awesome.more': '/more/awesome/path'
7773 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7775 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7777 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7779 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7781 * @param {String} className
7782 * @return {String} path
7784 getPath: function(className) {
7786 paths = this.config.paths,
7787 prefix = this.getPrefix(className);
7789 if (prefix.length > 0) {
7790 if (prefix === className) {
7791 return paths[prefix];
7794 path = paths[prefix];
7795 className = className.substring(prefix.length + 1);
7798 if (path.length > 0) {
7802 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7807 * @param {String} className
7809 getPrefix: function(className) {
7810 var paths = this.config.paths,
7811 prefix, deepestPrefix = '';
7813 if (paths.hasOwnProperty(className)) {
7817 for (prefix in paths) {
7818 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7819 if (prefix.length > deepestPrefix.length) {
7820 deepestPrefix = prefix;
7825 return deepestPrefix;
7829 * Refresh all items in the queue. If all dependencies for an item exist during looping,
7830 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7834 refreshQueue: function() {
7835 var ln = this.queue.length,
7836 i, item, j, requires;
7839 this.triggerReady();
7843 for (i = 0; i < ln; i++) {
7844 item = this.queue[i];
7847 requires = item.requires;
7849 // Don't bother checking when the number of files loaded
7850 // is still less than the array length
7851 if (requires.length > this.numLoadedFiles) {
7858 if (Manager.isCreated(requires[j])) {
7859 // Take out from the queue
7860 Ext.Array.erase(requires, j, 1);
7865 } while (j < requires.length);
7867 if (item.requires.length === 0) {
7868 Ext.Array.erase(this.queue, i, 1);
7869 item.callback.call(item.scope);
7870 this.refreshQueue();
7880 * Inject a script element to document's head, call onLoad and onError accordingly
7883 injectScriptElement: function(url, onLoad, onError, scope) {
7884 var script = document.createElement('script'),
7886 onLoadFn = function() {
7887 me.cleanupScriptElement(script);
7890 onErrorFn = function() {
7891 me.cleanupScriptElement(script);
7892 onError.call(scope);
7895 script.type = 'text/javascript';
7897 script.onload = onLoadFn;
7898 script.onerror = onErrorFn;
7899 script.onreadystatechange = function() {
7900 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7905 this.documentHead.appendChild(script);
7913 cleanupScriptElement: function(script) {
7914 script.onload = null;
7915 script.onreadystatechange = null;
7916 script.onerror = null;
7922 * Load a script file, supports both asynchronous and synchronous approaches
7924 * @param {String} url
7925 * @param {Function} onLoad
7926 * @param {Object} scope
7927 * @param {Boolean} synchronous
7930 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7932 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7933 fileName = url.split('/').pop(),
7934 isCrossOriginRestricted = false,
7935 xhr, status, onScriptError;
7937 scope = scope || this;
7939 this.isLoading = true;
7942 onScriptError = function() {
7943 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7946 if (!Ext.isReady && Ext.onDocumentReady) {
7947 Ext.onDocumentReady(function() {
7948 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7952 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7956 if (typeof XMLHttpRequest !== 'undefined') {
7957 xhr = new XMLHttpRequest();
7959 xhr = new ActiveXObject('Microsoft.XMLHTTP');
7963 xhr.open('GET', noCacheUrl, false);
7966 isCrossOriginRestricted = true;
7969 status = (xhr.status === 1223) ? 204 : xhr.status;
7971 if (!isCrossOriginRestricted) {
7972 isCrossOriginRestricted = (status === 0);
7975 if (isCrossOriginRestricted
7977 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7978 "being loaded from a different domain or from the local file system whereby cross origin " +
7979 "requests are not allowed due to security reasons. Use asynchronous loading with " +
7980 "Ext.require instead.", synchronous);
7982 else if (status >= 200 && status < 300
7984 // Firebug friendly, file names are still shown even though they're eval'ed code
7985 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7990 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7991 "verify that the file exists. " +
7992 "XHR status code: " + status, synchronous);
7995 // Prevent potential IE memory leak
8001 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
8002 * Can be chained with more `require` and `exclude` methods, e.g.:
8004 * Ext.exclude('Ext.data.*').require('*');
8006 * Ext.exclude('widget.button*').require('widget.*');
8008 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
8010 * @param {String/String[]} excludes
8011 * @return {Object} object contains `require` method for chaining
8013 exclude: function(excludes) {
8017 require: function(expressions, fn, scope) {
8018 return me.require(expressions, fn, scope, excludes);
8021 syncRequire: function(expressions, fn, scope) {
8022 return me.syncRequire(expressions, fn, scope, excludes);
8028 * Synchronously loads all classes by the given names and all their direct dependencies;
8029 * optionally executes the given callback function when finishes, within the optional scope.
8031 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
8033 * @param {String/String[]} expressions Can either be a string or an array of string
8034 * @param {Function} fn (Optional) The callback function
8035 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8036 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8038 syncRequire: function() {
8039 this.syncModeEnabled = true;
8040 this.require.apply(this, arguments);
8041 this.refreshQueue();
8042 this.syncModeEnabled = false;
8046 * Loads all classes by the given names and all their direct dependencies;
8047 * optionally executes the given callback function when finishes, within the optional scope.
8049 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
8051 * @param {String/String[]} expressions Can either be a string or an array of string
8052 * @param {Function} fn (Optional) The callback function
8053 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
8054 * @param {String/String[]} excludes (Optional) Classes to be excluded, useful when being used with expressions
8056 require: function(expressions, fn, scope, excludes) {
8057 var filePath, expression, exclude, className, excluded = {},
8058 excludedClassNames = [],
8059 possibleClassNames = [],
8060 possibleClassName, classNames = [],
8063 expressions = Ext.Array.from(expressions);
8064 excludes = Ext.Array.from(excludes);
8066 fn = fn || Ext.emptyFn;
8068 scope = scope || Ext.global;
8070 for (i = 0, ln = excludes.length; i < ln; i++) {
8071 exclude = excludes[i];
8073 if (typeof exclude === 'string' && exclude.length > 0) {
8074 excludedClassNames = Manager.getNamesByExpression(exclude);
8076 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
8077 excluded[excludedClassNames[j]] = true;
8082 for (i = 0, ln = expressions.length; i < ln; i++) {
8083 expression = expressions[i];
8085 if (typeof expression === 'string' && expression.length > 0) {
8086 possibleClassNames = Manager.getNamesByExpression(expression);
8088 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
8089 possibleClassName = possibleClassNames[j];
8091 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
8092 Ext.Array.include(classNames, possibleClassName);
8098 // If the dynamic dependency feature is not being used, throw an error
8099 // if the dependencies are not defined
8100 if (!this.config.enabled) {
8101 if (classNames.length > 0) {
8103 sourceClass: "Ext.Loader",
8104 sourceMethod: "require",
8105 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
8106 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
8111 if (classNames.length === 0) {
8117 requires: classNames,
8122 classNames = classNames.slice();
8124 for (i = 0, ln = classNames.length; i < ln; i++) {
8125 className = classNames[i];
8127 if (!this.isFileLoaded.hasOwnProperty(className)) {
8128 this.isFileLoaded[className] = false;
8130 filePath = this.getPath(className);
8132 this.classNameToFilePathMap[className] = filePath;
8134 this.numPendingFiles++;
8136 this.loadScriptFile(
8138 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
8139 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
8141 this.syncModeEnabled
8151 * @param {String} className
8152 * @param {String} filePath
8154 onFileLoaded: function(className, filePath) {
8155 this.numLoadedFiles++;
8157 this.isFileLoaded[className] = true;
8159 this.numPendingFiles--;
8161 if (this.numPendingFiles === 0) {
8162 this.refreshQueue();
8165 if (this.numPendingFiles <= 1) {
8166 window.status = "Finished loading all dependencies, onReady fired!";
8169 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
8172 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
8173 var queue = this.queue,
8175 i, ln, j, subLn, missingClasses = [], missingPaths = [];
8177 for (i = 0, ln = queue.length; i < ln; i++) {
8178 requires = queue[i].requires;
8180 for (j = 0, subLn = requires.length; j < ln; j++) {
8181 if (this.isFileLoaded[requires[j]]) {
8182 missingClasses.push(requires[j]);
8187 if (missingClasses.length < 1) {
8191 missingClasses = Ext.Array.filter(missingClasses, function(item) {
8192 return !this.requiresMap.hasOwnProperty(item);
8195 for (i = 0,ln = missingClasses.length; i < ln; i++) {
8196 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
8200 sourceClass: "Ext.Loader",
8201 sourceMethod: "onFileLoaded",
8202 msg: "The following classes are not declared even if their files have been " +
8203 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
8204 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
8212 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
8213 this.numPendingFiles--;
8214 this.hasFileLoadError = true;
8217 sourceClass: "Ext.Loader",
8218 classToLoad: className,
8220 loadingType: isSynchronous ? 'synchronous' : 'async',
8228 addOptionalRequires: function(requires) {
8229 var optionalRequires = this.optionalRequires,
8232 requires = Ext.Array.from(requires);
8234 for (i = 0, ln = requires.length; i < ln; i++) {
8235 require = requires[i];
8237 Ext.Array.include(optionalRequires, require);
8246 triggerReady: function(force) {
8247 var readyListeners = this.readyListeners,
8248 optionalRequires, listener;
8250 if (this.isLoading || force) {
8251 this.isLoading = false;
8253 if (this.optionalRequires.length) {
8254 // Clone then empty the array to eliminate potential recursive loop issue
8255 optionalRequires = Ext.Array.clone(this.optionalRequires);
8257 // Empty the original array
8258 this.optionalRequires.length = 0;
8260 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
8264 while (readyListeners.length) {
8265 listener = readyListeners.shift();
8266 listener.fn.call(listener.scope);
8268 if (this.isLoading) {
8278 * Adds new listener to be executed when all required scripts are fully loaded.
8280 * @param {Function} fn The function callback to be executed
8281 * @param {Object} scope The execution scope (`this`) of the callback function
8282 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
8284 onReady: function(fn, scope, withDomReady, options) {
8287 if (withDomReady !== false && Ext.onDocumentReady) {
8291 Ext.onDocumentReady(oldFn, scope, options);
8295 if (!this.isLoading) {
8299 this.readyListeners.push({
8308 * @param {String} className
8310 historyPush: function(className) {
8311 if (className && this.isFileLoaded.hasOwnProperty(className)) {
8312 Ext.Array.include(this.history, className);
8322 * @alias Ext.Loader#require
8324 Ext.require = alias(Loader, 'require');
8328 * @method syncRequire
8329 * @alias Ext.Loader#syncRequire
8331 Ext.syncRequire = alias(Loader, 'syncRequire');
8336 * @alias Ext.Loader#exclude
8338 Ext.exclude = alias(Loader, 'exclude');
8343 * @alias Ext.Loader#onReady
8345 Ext.onReady = function(fn, scope, options) {
8346 Loader.onReady(fn, scope, true, options);
8350 * @cfg {String[]} requires
8352 * List of classes that have to be loaded before instantiating this class.
8355 * Ext.define('Mother', {
8356 * requires: ['Child'],
8357 * giveBirth: function() {
8358 * // we can be sure that child class is available.
8359 * return new Child();
8363 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
8366 className = Manager.getName(cls),
8367 i, j, ln, subLn, value, propertyName, propertyValue;
8370 Basically loop through the dependencyProperties, look for string class names and push
8371 them into a stack, regardless of whether the property's value is a string, array or object. For example:
8373 extend: 'Ext.MyClass',
8374 requires: ['Ext.some.OtherClass'],
8376 observable: 'Ext.util.Observable';
8379 which will later be transformed into:
8381 extend: Ext.MyClass,
8382 requires: [Ext.some.OtherClass],
8384 observable: Ext.util.Observable;
8389 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8390 propertyName = dependencyProperties[i];
8392 if (data.hasOwnProperty(propertyName)) {
8393 propertyValue = data[propertyName];
8395 if (typeof propertyValue === 'string') {
8396 dependencies.push(propertyValue);
8398 else if (propertyValue instanceof Array) {
8399 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8400 value = propertyValue[j];
8402 if (typeof value === 'string') {
8403 dependencies.push(value);
8407 else if (typeof propertyValue != 'function') {
8408 for (j in propertyValue) {
8409 if (propertyValue.hasOwnProperty(j)) {
8410 value = propertyValue[j];
8412 if (typeof value === 'string') {
8413 dependencies.push(value);
8421 if (dependencies.length === 0) {
8422 // Loader.historyPush(className);
8426 var deadlockPath = [],
8427 requiresMap = Loader.requiresMap,
8431 Automatically detect deadlocks before-hand,
8432 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
8434 - A extends B, then B extends A
8435 - A requires B, B requires C, then C requires A
8437 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
8438 no matter how deep the path is.
8442 requiresMap[className] = dependencies;
8444 detectDeadlock = function(cls) {
8445 deadlockPath.push(cls);
8447 if (requiresMap[cls]) {
8448 if (Ext.Array.contains(requiresMap[cls], className)) {
8450 sourceClass: "Ext.Loader",
8451 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
8452 deadlockPath[1] + "' " + "mutually require each other. Path: " +
8453 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
8457 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
8458 detectDeadlock(requiresMap[cls][i]);
8463 detectDeadlock(className);
8467 Loader.require(dependencies, function() {
8468 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8469 propertyName = dependencyProperties[i];
8471 if (data.hasOwnProperty(propertyName)) {
8472 propertyValue = data[propertyName];
8474 if (typeof propertyValue === 'string') {
8475 data[propertyName] = Manager.get(propertyValue);
8477 else if (propertyValue instanceof Array) {
8478 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8479 value = propertyValue[j];
8481 if (typeof value === 'string') {
8482 data[propertyName][j] = Manager.get(value);
8486 else if (typeof propertyValue != 'function') {
8487 for (var k in propertyValue) {
8488 if (propertyValue.hasOwnProperty(k)) {
8489 value = propertyValue[k];
8491 if (typeof value === 'string') {
8492 data[propertyName][k] = Manager.get(value);
8500 continueFn.call(me, cls, data);
8506 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8509 * @cfg {String[]} uses
8511 * List of classes to load together with this class. These aren't neccessarily loaded before
8512 * this class is instantiated. For example:
8514 * Ext.define('Mother', {
8516 * giveBirth: function() {
8517 * // This code might, or might not work:
8518 * // return new Child();
8520 * // Instead use Ext.create() to load the class at the spot if not loaded already:
8521 * return Ext.create('Child');
8525 Manager.registerPostprocessor('uses', function(name, cls, data) {
8526 var uses = Ext.Array.from(data.uses),
8530 for (i = 0, ln = uses.length; i < ln; i++) {
8533 if (typeof item === 'string') {
8538 Loader.addOptionalRequires(items);
8541 Manager.setDefaultPostprocessorPosition('uses', 'last');
8543 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8546 * @author Brian Moeskau <brian@sencha.com>
8547 * @docauthor Brian Moeskau <brian@sencha.com>
8549 * A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8550 * errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8551 * uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8552 * the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8553 * with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8554 * execution will halt.
8556 * Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8557 * handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8558 * although in a real application it's usually a better idea to override the handling function and perform
8559 * logging or some other method of reporting the errors in a way that is meaningful to the application.
8561 * At its simplest you can simply raise an error as a simple string from within any code:
8565 * Ext.Error.raise('Something bad happened!');
8567 * If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8568 * displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8569 * additional metadata about the error being raised. The {@link #raise} method can also take a config object.
8570 * In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8571 * added to the error object and, if the console is available, logged to the console for inspection.
8575 * Ext.define('Ext.Foo', {
8576 * doSomething: function(option){
8577 * if (someCondition === false) {
8579 * msg: 'You cannot do that!',
8580 * option: option, // whatever was passed into the method
8581 * 'error code': 100 // other arbitrary info
8587 * If a console is available (that supports the `console.dir` function) you'll see console output like:
8589 * An error was raised with the following data:
8590 * option: Object { foo: "bar"}
8593 * msg: "You cannot do that!"
8594 * sourceClass: "Ext.Foo"
8595 * sourceMethod: "doSomething"
8597 * uncaught exception: You cannot do that!
8599 * As you can see, the error will report exactly where it was raised and will include as much information as the
8600 * raising code can usefully provide.
8602 * If you want to handle all application errors globally you can simply override the static {@link #handle} method
8603 * and provide whatever handling logic you need. If the method returns true then the error is considered handled
8604 * and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8608 * Ext.Error.handle = function(err) {
8609 * if (err.someProperty == 'NotReallyAnError') {
8610 * // maybe log something to the application here if applicable
8613 * // any non-true return value (including none) will cause the error to be thrown
8617 Ext.Error = Ext.extend(Error, {
8620 * @property {Boolean} ignore
8621 * Static flag that can be used to globally disable error reporting to the browser if set to true
8622 * (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8623 * and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8624 * be preferable to supply a custom error {@link #handle handling} function instead.
8628 * Ext.Error.ignore = true;
8635 * @property {Boolean} notify
8636 * Static flag that can be used to globally control error notification to the user. Unlike
8637 * Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8638 * set to false to disable the alert notification (default is true for IE6 and IE7).
8640 * Only the first error will generate an alert. Internally this flag is set to false when the
8641 * first error occurs prior to displaying the alert.
8643 * This flag is not used in a release build.
8647 * Ext.Error.notify = false;
8651 //notify: Ext.isIE6 || Ext.isIE7,
8654 * Raise an error that can include additional data and supports automatic console logging if available.
8655 * You can pass a string error message or an object with the `msg` attribute which will be used as the
8656 * error message. The object can contain any other name-value attributes (or objects) to be logged
8657 * along with the error.
8659 * Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8660 * execution will halt.
8664 * Ext.Error.raise('A simple string error message');
8668 * Ext.define('Ext.Foo', {
8669 * doSomething: function(option){
8670 * if (someCondition === false) {
8672 * msg: 'You cannot do that!',
8673 * option: option, // whatever was passed into the method
8674 * 'error code': 100 // other arbitrary info
8680 * @param {String/Object} err The error message string, or an object containing the attribute "msg" that will be
8681 * used as the error message. Any other data included in the object will also be logged to the browser console,
8685 raise: function(err){
8687 if (Ext.isString(err)) {
8691 var method = this.raise.caller;
8695 err.sourceMethod = method.$name;
8697 if (method.$owner) {
8698 err.sourceClass = method.$owner.$className;
8702 if (Ext.Error.handle(err) !== true) {
8703 var msg = Ext.Error.prototype.toString.call(err);
8712 throw new Ext.Error(err);
8717 * Globally handle any Ext errors that may be raised, optionally providing custom logic to
8718 * handle different errors individually. Return true from the function to bypass throwing the
8719 * error to the browser, otherwise the error will be thrown and execution will halt.
8723 * Ext.Error.handle = function(err) {
8724 * if (err.someProperty == 'NotReallyAnError') {
8725 * // maybe log something to the application here if applicable
8728 * // any non-true return value (including none) will cause the error to be thrown
8731 * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes that were originally
8732 * raised with it, plus properties about the method and class from which the error originated (if raised from a
8733 * class that uses the Ext 4 class system).
8737 return Ext.Error.ignore;
8741 // This is the standard property that is the name of the constructor.
8745 * Creates new Error object.
8746 * @param {String/Object} config The error message string, or an object containing the
8747 * attribute "msg" that will be used as the error message. Any other data included in
8748 * the object will be applied to the error instance and logged to the browser console, if available.
8750 constructor: function(config){
8751 if (Ext.isString(config)) {
8752 config = { msg: config };
8757 Ext.apply(me, config);
8759 me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8760 // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8764 * Provides a custom string representation of the error object. This is an override of the base JavaScript
8765 * `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8766 * be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8768 * The default implementation will include the error message along with the raising class and method, if available,
8769 * but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8770 * a particular error instance, if you want to provide a custom description that will show up in the console.
8771 * @return {String} The error message. If raised from within the Ext 4 class system, the error message will also
8772 * include the raising class and method names, if available.
8774 toString: function(){
8776 className = me.className ? me.className : '',
8777 methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8778 msg = me.msg || '(No description provided)';
8780 return className + methodName + msg;
8785 * This mechanism is used to notify the user of the first error encountered on the page. This
8786 * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8787 * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8788 * where exceptions are handled in a try/catch.
8791 var prevOnError, timer, errors = 0,
8792 extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
8795 if (typeof window === 'undefined') {
8796 return; // build system or some such environment...
8799 // This method is called to notify the user of the current error status.
8800 function notify () {
8801 var counters = Ext.log.counters,
8802 supports = Ext.supports,
8803 hasOnError = supports && supports.WindowOnError; // TODO - timing
8805 // Put log counters to the status bar (for most browsers):
8806 if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
8807 var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
8808 'Info:',counters.info, 'Log:',counters.log].join(' ');
8810 msg = '*** Errors: ' + errors + ' - ' + msg;
8811 } else if (counters.error) {
8817 // Display an alert on the first error:
8818 if (!Ext.isDefined(Ext.Error.notify)) {
8819 Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
8821 if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
8822 Ext.Error.notify = false;
8825 win.clearInterval(timer); // ticks can queue up so stop...
8829 alert('Unhandled error on page: See console or log');
8834 // Sets up polling loop. This is the only way to know about errors in some browsers
8835 // (Opera/Safari) and is the only way to update the status bar for warnings and other
8838 timer = win.setInterval(notify, 1000);
8841 // window.onerror sounds ideal but it prevents the built-in error dialog from doing
8842 // its (better) thing.