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
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 {Mixed} value The value to test
262 * @param {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} 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 {Mixed} item The variable to clone
498 * @return {Mixed} 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.2', 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 {Mixed} 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) {
1122 * A collection of useful static methods to deal with numbers
1128 var isToFixedBroken = (0.9).toFixed() !== '1';
1132 * Checks whether or not the passed number is within a desired range. If the number is already within the
1133 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
1134 * exceeded. Note that this method returns the constrained value but does not change the current number.
1135 * @param {Number} number The number to check
1136 * @param {Number} min The minimum number in the range
1137 * @param {Number} max The maximum number in the range
1138 * @return {Number} The constrained value if outside the range, otherwise the current value
1140 constrain: function(number, min, max) {
1141 number = parseFloat(number);
1144 number = Math.max(number, min);
1147 number = Math.min(number, max);
1153 * Snaps the passed number between stopping points based upon a passed increment value.
1154 * @param {Number} value The unsnapped value.
1155 * @param {Number} increment The increment by which the value must move.
1156 * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
1157 * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
1158 * @return {Number} The value of the nearest snap target.
1160 snap : function(value, increment, minValue, maxValue) {
1161 var newValue = value,
1164 if (!(increment && value)) {
1167 m = value % increment;
1170 if (m * 2 >= increment) {
1171 newValue += increment;
1172 } else if (m * 2 < -increment) {
1173 newValue -= increment;
1176 return Ext.Number.constrain(newValue, minValue, maxValue);
1180 * Formats a number using fixed-point notation
1181 * @param {Number} value The number to format
1182 * @param {Number} precision The number of digits to show after the decimal point
1184 toFixed: function(value, precision) {
1185 if (isToFixedBroken) {
1186 precision = precision || 0;
1187 var pow = Math.pow(10, precision);
1188 return (Math.round(value * pow) / pow).toFixed(precision);
1191 return value.toFixed(precision);
1195 * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
1198 Ext.Number.from('1.23', 1); // returns 1.23
1199 Ext.Number.from('abc', 1); // returns 1
1201 * @param {Mixed} value
1202 * @param {Number} defaultValue The value to return if the original value is non-numeric
1203 * @return {Number} value, if numeric, defaultValue otherwise
1205 from: function(value, defaultValue) {
1206 if (isFinite(value)) {
1207 value = parseFloat(value);
1210 return !isNaN(value) ? value : defaultValue;
1217 * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
1219 * @deprecated 4.0.0 Replaced by Ext.Number.from
1223 Ext.num = function() {
1224 return Ext.Number.from.apply(this, arguments);
1227 * @author Jacky Nguyen <jacky@sencha.com>
1228 * @docauthor Jacky Nguyen <jacky@sencha.com>
1231 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
1238 var arrayPrototype = Array.prototype,
1239 slice = arrayPrototype.slice,
1240 supportsSplice = function () {
1245 if (!array.splice) {
1249 // This detects a bug in IE8 splice method:
1250 // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
1256 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");
1258 lengthBefore = array.length; //41
1259 array.splice(13, 0, "XXX"); // add one element
1261 if (lengthBefore+1 != array.length) {
1268 supportsForEach = 'forEach' in arrayPrototype,
1269 supportsMap = 'map' in arrayPrototype,
1270 supportsIndexOf = 'indexOf' in arrayPrototype,
1271 supportsEvery = 'every' in arrayPrototype,
1272 supportsSome = 'some' in arrayPrototype,
1273 supportsFilter = 'filter' in arrayPrototype,
1274 supportsSort = function() {
1275 var a = [1,2,3,4,5].sort(function(){ return 0; });
1276 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
1278 supportsSliceOnNodeList = true,
1282 // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
1283 if (typeof document !== 'undefined') {
1284 slice.call(document.getElementsByTagName('body'));
1287 supportsSliceOnNodeList = false;
1290 function fixArrayIndex (array, index) {
1291 return (index < 0) ? Math.max(0, array.length + index)
1292 : Math.min(array.length, index);
1296 Does the same work as splice, but with a slightly more convenient signature. The splice
1297 method has bugs in IE8, so this is the implementation we use on that platform.
1299 The rippling of items in the array can be tricky. Consider two use cases:
1304 +---+---+---+---+---+---+---+---+
1305 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1306 +---+---+---+---+---+---+---+---+
1309 / / \/ \/ \ +--------------------------+
1310 / / /\ /\ +--------------------------+ \
1311 / / / \/ +--------------------------+ \ \
1312 / / / /+--------------------------+ \ \ \
1315 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1316 | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
1317 +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
1321 In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
1322 that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
1323 must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
1325 function replaceSim (array, index, removeCount, insert) {
1326 var add = insert ? insert.length : 0,
1327 length = array.length,
1328 pos = fixArrayIndex(array, index);
1330 // we try to use Array.push when we can for efficiency...
1331 if (pos === length) {
1333 array.push.apply(array, insert);
1336 var remove = Math.min(removeCount, length - pos),
1337 tailOldPos = pos + remove,
1338 tailNewPos = tailOldPos + add - remove,
1339 tailCount = length - tailOldPos,
1340 lengthAfterRemove = length - remove,
1343 if (tailNewPos < tailOldPos) { // case A
1344 for (i = 0; i < tailCount; ++i) {
1345 array[tailNewPos+i] = array[tailOldPos+i];
1347 } else if (tailNewPos > tailOldPos) { // case B
1348 for (i = tailCount; i--; ) {
1349 array[tailNewPos+i] = array[tailOldPos+i];
1351 } // else, add == remove (nothing to do)
1353 if (add && pos === lengthAfterRemove) {
1354 array.length = lengthAfterRemove; // truncate array
1355 array.push.apply(array, insert);
1357 array.length = lengthAfterRemove + add; // reserves space
1358 for (i = 0; i < add; ++i) {
1359 array[pos+i] = insert[i];
1367 function replaceNative (array, index, removeCount, insert) {
1368 if (insert && insert.length) {
1369 if (index < array.length) {
1370 array.splice.apply(array, [index, removeCount].concat(insert));
1372 array.push.apply(array, insert);
1375 array.splice(index, removeCount);
1380 function eraseSim (array, index, removeCount) {
1381 return replaceSim(array, index, removeCount);
1384 function eraseNative (array, index, removeCount) {
1385 array.splice(index, removeCount);
1389 function spliceSim (array, index, removeCount) {
1390 var pos = fixArrayIndex(array, index),
1391 removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
1393 if (arguments.length < 4) {
1394 replaceSim(array, pos, removeCount);
1396 replaceSim(array, pos, removeCount, slice.call(arguments, 3));
1402 function spliceNative (array) {
1403 return array.splice.apply(array, slice.call(arguments, 1));
1406 var erase = supportsSplice ? eraseNative : eraseSim,
1407 replace = supportsSplice ? replaceNative : replaceSim,
1408 splice = supportsSplice ? spliceNative : spliceSim;
1410 // NOTE: from here on, use erase, replace or splice (not native methods)...
1412 ExtArray = Ext.Array = {
1414 * Iterates an array or an iterable value and invoke the given callback function for each item.
1416 * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
1418 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1419 * console.log(name);
1422 * var sum = function() {
1425 * Ext.Array.each(arguments, function(value) {
1432 * sum(1, 2, 3); // returns 6
1434 * The iteration can be stopped by returning false in the function callback.
1436 * Ext.Array.each(countries, function(name, index, countriesItSelf) {
1437 * if (name === 'Singapore') {
1438 * return false; // break here
1442 * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
1444 * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
1445 * argument is not iterable, the callback function is called once.
1446 * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
1447 * the current `index`. Arguments passed to this callback function are:
1449 * - `item` : Mixed - The item at the current `index` in the passed `array`
1450 * - `index` : Number - The current `index` within the `array`
1451 * - `allItems` : Array/NodeList/Mixed - The `array` passed as the first argument to `Ext.Array.each`
1453 * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
1454 * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
1456 * @return {Boolean} See description for the `fn` parameter.
1458 each: function(array, fn, scope, reverse) {
1459 array = ExtArray.from(array);
1464 if (reverse !== true) {
1465 for (i = 0; i < ln; i++) {
1466 if (fn.call(scope || array[i], array[i], i, array) === false) {
1472 for (i = ln - 1; i > -1; i--) {
1473 if (fn.call(scope || array[i], array[i], i, array) === false) {
1483 * Iterates an array and invoke the given callback function for each item. Note that this will simply
1484 * delegate to the native Array.prototype.forEach method if supported.
1485 * It doesn't support stopping the iteration by returning false in the callback function like
1486 * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
1487 * {@link Ext.Array#each}
1489 * @param {Array} array The array to iterate
1490 * @param {Function} fn The function callback, to be invoked these arguments:
1492 * - `item` : Mixed - The item at the current `index` in the passed `array`
1493 * - `index` : Number - The current `index` within the `array`
1494 * - `allItems` : Array - The `array` itself which was passed as the first argument
1496 * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
1498 forEach: function(array, fn, scope) {
1499 if (supportsForEach) {
1500 return array.forEach(fn, scope);
1506 for (; i < ln; i++) {
1507 fn.call(scope, array[i], i, array);
1512 * Get the index of the provided `item` in the given `array`, a supplement for the
1513 * missing arrayPrototype.indexOf in Internet Explorer.
1515 * @param {Array} array The array to check
1516 * @param {Mixed} item The item to look for
1517 * @param {Number} from (Optional) The index at which to begin the search
1518 * @return {Number} The index of item in the array (or -1 if it is not found)
1520 indexOf: function(array, item, from) {
1521 if (supportsIndexOf) {
1522 return array.indexOf(item, from);
1525 var i, length = array.length;
1527 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
1528 if (array[i] === item) {
1537 * Checks whether or not the given `array` contains the specified `item`
1539 * @param {Array} array The array to check
1540 * @param {Mixed} item The item to look for
1541 * @return {Boolean} True if the array contains the item, false otherwise
1543 contains: function(array, item) {
1544 if (supportsIndexOf) {
1545 return array.indexOf(item) !== -1;
1550 for (i = 0, ln = array.length; i < ln; i++) {
1551 if (array[i] === item) {
1560 * Converts any iterable (numeric indices and a length property) into a true array.
1563 * var args = Ext.Array.toArray(arguments),
1564 * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
1566 * alert(args.join(' '));
1567 * alert(fromSecondToLastArgs.join(' '));
1570 * test('just', 'testing', 'here'); // alerts 'just testing here';
1571 * // alerts 'testing here';
1573 * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
1574 * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1575 * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
1577 * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
1579 * @param {Mixed} iterable the iterable object to be turned into a true Array.
1580 * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
1581 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
1582 * index of the iterable value
1583 * @return {Array} array
1585 toArray: function(iterable, start, end){
1586 if (!iterable || !iterable.length) {
1590 if (typeof iterable === 'string') {
1591 iterable = iterable.split('');
1594 if (supportsSliceOnNodeList) {
1595 return slice.call(iterable, start || 0, end || iterable.length);
1602 end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
1604 for (i = start; i < end; i++) {
1605 array.push(iterable[i]);
1612 * Plucks the value of a property from each item in the Array. Example:
1614 * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
1616 * @param {Array|NodeList} array The Array of items to pluck the value from.
1617 * @param {String} propertyName The property name to pluck from each element.
1618 * @return {Array} The value from each item in the Array.
1620 pluck: function(array, propertyName) {
1624 for (i = 0, ln = array.length; i < ln; i++) {
1627 ret.push(item[propertyName]);
1634 * Creates a new array with the results of calling a provided function on every element in this array.
1636 * @param {Array} array
1637 * @param {Function} fn Callback function for each item
1638 * @param {Object} scope Callback function scope
1639 * @return {Array} results
1641 map: function(array, fn, scope) {
1643 return array.map(fn, scope);
1650 for (; i < len; i++) {
1651 results[i] = fn.call(scope, array[i], i, array);
1658 * Executes the specified function for each array element until the function returns a falsy value.
1659 * If such an item is found, the function will return false immediately.
1660 * Otherwise, it will return true.
1662 * @param {Array} array
1663 * @param {Function} fn Callback function for each item
1664 * @param {Object} scope Callback function scope
1665 * @return {Boolean} True if no false value is returned by the callback function.
1667 every: function(array, fn, scope) {
1669 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
1671 if (supportsEvery) {
1672 return array.every(fn, scope);
1678 for (; i < ln; ++i) {
1679 if (!fn.call(scope, array[i], i, array)) {
1688 * Executes the specified function for each array element until the function returns a truthy value.
1689 * If such an item is found, the function will return true immediately. Otherwise, it will return false.
1691 * @param {Array} array
1692 * @param {Function} fn Callback function for each item
1693 * @param {Object} scope Callback function scope
1694 * @return {Boolean} True if the callback function returns a truthy value.
1696 some: function(array, fn, scope) {
1698 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
1701 return array.some(fn, scope);
1707 for (; i < ln; ++i) {
1708 if (fn.call(scope, array[i], i, array)) {
1717 * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
1719 * See {@link Ext.Array#filter}
1721 * @param {Array} array
1722 * @return {Array} results
1724 clean: function(array) {
1730 for (; i < ln; i++) {
1733 if (!Ext.isEmpty(item)) {
1742 * Returns a new array with unique items
1744 * @param {Array} array
1745 * @return {Array} results
1747 unique: function(array) {
1753 for (; i < ln; i++) {
1756 if (ExtArray.indexOf(clone, item) === -1) {
1765 * Creates a new array with all of the elements of this array for which
1766 * the provided filtering function returns true.
1768 * @param {Array} array
1769 * @param {Function} fn Callback function for each item
1770 * @param {Object} scope Callback function scope
1771 * @return {Array} results
1773 filter: function(array, fn, scope) {
1774 if (supportsFilter) {
1775 return array.filter(fn, scope);
1782 for (; i < ln; i++) {
1783 if (fn.call(scope, array[i], i, array)) {
1784 results.push(array[i]);
1792 * Converts a value to an array if it's not already an array; returns:
1794 * - An empty array if given value is `undefined` or `null`
1795 * - Itself if given value is already an array
1796 * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
1797 * - An array with one item which is the given value, otherwise
1799 * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
1800 * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
1802 * @return {Array} array
1804 from: function(value, newReference) {
1805 if (value === undefined || value === null) {
1809 if (Ext.isArray(value)) {
1810 return (newReference) ? slice.call(value) : value;
1813 if (value && value.length !== undefined && typeof value !== 'string') {
1814 return Ext.toArray(value);
1821 * Removes the specified item from the array if it exists
1823 * @param {Array} array The array
1824 * @param {Mixed} item The item to remove
1825 * @return {Array} The passed array itself
1827 remove: function(array, item) {
1828 var index = ExtArray.indexOf(array, item);
1831 erase(array, index, 1);
1838 * Push an item into the array only if the array doesn't contain it yet
1840 * @param {Array} array The array
1841 * @param {Mixed} item The item to include
1843 include: function(array, item) {
1844 if (!ExtArray.contains(array, item)) {
1850 * Clone a flat array without referencing the previous one. Note that this is different
1851 * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
1852 * for Array.prototype.slice.call(array)
1854 * @param {Array} array The array
1855 * @return {Array} The clone array
1857 clone: function(array) {
1858 return slice.call(array);
1862 * Merge multiple arrays into one with unique items.
1864 * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
1866 * @param {Array} array1
1867 * @param {Array} array2
1868 * @param {Array} etc
1869 * @return {Array} merged
1872 var args = slice.call(arguments),
1876 for (i = 0, ln = args.length; i < ln; i++) {
1877 array = array.concat(args[i]);
1880 return ExtArray.unique(array);
1884 * Merge multiple arrays into one with unique items that exist in all of the arrays.
1886 * @param {Array} array1
1887 * @param {Array} array2
1888 * @param {Array} etc
1889 * @return {Array} intersect
1891 intersect: function() {
1893 arrays = slice.call(arguments),
1894 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
1896 if (!arrays.length) {
1900 // Find the smallest array
1901 for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
1902 if (!minArray || array.length < minArray.length) {
1908 minArray = ExtArray.unique(minArray);
1909 erase(arrays, x, 1);
1911 // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
1912 // an item in the small array, we're likely to find it before reaching the end
1913 // of the inner loop and can terminate the search early.
1914 for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
1917 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
1918 for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
1926 if (count === arraysLn) {
1935 * Perform a set difference A-B by subtracting all items in array B from array A.
1937 * @param {Array} arrayA
1938 * @param {Array} arrayB
1939 * @return {Array} difference
1941 difference: function(arrayA, arrayB) {
1942 var clone = slice.call(arrayA),
1946 for (i = 0,lnB = arrayB.length; i < lnB; i++) {
1947 for (j = 0; j < ln; j++) {
1948 if (clone[j] === arrayB[i]) {
1960 * Returns a shallow copy of a part of an array. This is equivalent to the native
1961 * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
1962 * is "arguments" since the arguments object does not supply a slice method but can
1963 * be the context object to Array.prototype.slice.
1965 * @param {Array} array The array (or arguments object).
1966 * @param {Number} begin The index at which to begin. Negative values are offsets from
1967 * the end of the array.
1968 * @param {Number} end The index at which to end. The copied items do not include
1969 * end. Negative values are offsets from the end of the array. If end is omitted,
1970 * all items up to the end of the array are copied.
1971 * @return {Array} The copied piece of the array.
1973 slice: function(array, begin, end) {
1974 return slice.call(array, begin, end);
1978 * Sorts the elements of an Array.
1979 * By default, this method sorts the elements alphabetically and ascending.
1981 * @param {Array} array The array to sort.
1982 * @param {Function} sortFn (optional) The comparison function.
1983 * @return {Array} The sorted array.
1985 sort: function(array, sortFn) {
1988 return array.sort(sortFn);
1990 return array.sort();
1994 var length = array.length,
1999 for (; i < length; i++) {
2001 for (j = i + 1; j < length; j++) {
2003 comparison = sortFn(array[j], array[min]);
2004 if (comparison < 0) {
2007 } else if (array[j] < array[min]) {
2013 array[i] = array[min];
2022 * Recursively flattens into 1-d Array. Injects Arrays inline.
2025 flatten: function(array) {
2028 function rFlatten(a) {
2031 for (i = 0, ln = a.length; i < ln; i++) {
2034 if (Ext.isArray(v)) {
2044 return rFlatten(array);
2048 * Returns the minimum value in the Array.
2050 * @param {Array|NodeList} array The Array from which to select the minimum value.
2051 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
2052 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
2053 * @return {Mixed} minValue The minimum value
2055 min: function(array, comparisonFn) {
2059 for (i = 0, ln = array.length; i < ln; i++) {
2063 if (comparisonFn(min, item) === 1) {
2078 * Returns the maximum value in the Array.
2080 * @param {Array|NodeList} array The Array from which to select the maximum value.
2081 * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
2082 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
2083 * @return {Mixed} maxValue The maximum value
2085 max: function(array, comparisonFn) {
2089 for (i = 0, ln = array.length; i < ln; i++) {
2093 if (comparisonFn(max, item) === -1) {
2108 * Calculates the mean of all items in the array.
2110 * @param {Array} array The Array to calculate the mean value of.
2111 * @return {Number} The mean.
2113 mean: function(array) {
2114 return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
2118 * Calculates the sum of all items in the given array.
2120 * @param {Array} array The Array to calculate the sum value of.
2121 * @return {Number} The sum.
2123 sum: function(array) {
2127 for (i = 0,ln = array.length; i < ln; i++) {
2136 _replaceSim: replaceSim, // for unit testing
2137 _spliceSim: spliceSim,
2140 * Removes items from an array. This is functionally equivalent to the splice method
2141 * of Array, but works around bugs in IE8's splice method and does not copy the
2142 * removed elements in order to return them (because very often they are ignored).
2144 * @param {Array} array The Array on which to replace.
2145 * @param {Number} index The index in the array at which to operate.
2146 * @param {Number} removeCount The number of items to remove at index.
2147 * @return {Array} The array passed.
2153 * Inserts items in to an array.
2155 * @param {Array} array The Array on which to replace.
2156 * @param {Number} index The index in the array at which to operate.
2157 * @param {Array} items The array of items to insert at index.
2158 * @return {Array} The array passed.
2160 insert: function (array, index, items) {
2161 return replace(array, index, 0, items);
2165 * Replaces items in an array. This is functionally equivalent to the splice method
2166 * of Array, but works around bugs in IE8's splice method and is often more convenient
2167 * to call because it accepts an array of items to insert rather than use a variadic
2170 * @param {Array} array The Array on which to replace.
2171 * @param {Number} index The index in the array at which to operate.
2172 * @param {Number} removeCount The number of items to remove at index (can be 0).
2173 * @param {Array} insert An optional array of items to insert at index.
2174 * @return {Array} The array passed.
2180 * Replaces items in an array. This is equivalent to the splice method of Array, but
2181 * works around bugs in IE8's splice method. The signature is exactly the same as the
2182 * splice method except that the array is the first argument. All arguments following
2183 * removeCount are inserted in the array at index.
2185 * @param {Array} array The Array on which to replace.
2186 * @param {Number} index The index in the array at which to operate.
2187 * @param {Number} removeCount The number of items to remove at index (can be 0).
2188 * @return {Array} An array containing the removed items.
2197 * @alias Ext.Array#each
2199 Ext.each = ExtArray.each;
2204 * @alias Ext.Array#merge
2206 ExtArray.union = ExtArray.merge;
2209 * Old alias to {@link Ext.Array#min}
2210 * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
2213 * @alias Ext.Array#min
2215 Ext.min = ExtArray.min;
2218 * Old alias to {@link Ext.Array#max}
2219 * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
2222 * @alias Ext.Array#max
2224 Ext.max = ExtArray.max;
2227 * Old alias to {@link Ext.Array#sum}
2228 * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
2231 * @alias Ext.Array#sum
2233 Ext.sum = ExtArray.sum;
2236 * Old alias to {@link Ext.Array#mean}
2237 * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
2240 * @alias Ext.Array#mean
2242 Ext.mean = ExtArray.mean;
2245 * Old alias to {@link Ext.Array#flatten}
2246 * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
2249 * @alias Ext.Array#flatten
2251 Ext.flatten = ExtArray.flatten;
2254 * Old alias to {@link Ext.Array#clean}
2255 * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
2258 * @alias Ext.Array#clean
2260 Ext.clean = ExtArray.clean;
2263 * Old alias to {@link Ext.Array#unique}
2264 * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
2267 * @alias Ext.Array#unique
2269 Ext.unique = ExtArray.unique;
2272 * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
2273 * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
2276 * @alias Ext.Array#pluck
2278 Ext.pluck = ExtArray.pluck;
2283 * @alias Ext.Array#toArray
2285 Ext.toArray = function() {
2286 return ExtArray.toArray.apply(ExtArray, arguments);
2291 * @class Ext.Function
2293 * A collection of useful static methods to deal with function callbacks
2299 * A very commonly used method throughout the framework. It acts as a wrapper around another method
2300 * which originally accepts 2 arguments for `name` and `value`.
2301 * The wrapped function then allows "flexible" value setting of either:
2303 * - `name` and `value` as 2 arguments
2304 * - one single object argument with multiple key - value pairs
2308 * var setValue = Ext.Function.flexSetter(function(name, value) {
2309 * this[name] = value;
2313 * // Setting a single name - value
2314 * setValue('name1', 'value1');
2316 * // Settings multiple name - value pairs
2323 * @param {Function} setter
2324 * @returns {Function} flexSetter
2326 flexSetter: function(fn) {
2327 return function(a, b) {
2334 if (typeof a !== 'string') {
2336 if (a.hasOwnProperty(k)) {
2337 fn.call(this, k, a[k]);
2341 if (Ext.enumerables) {
2342 for (i = Ext.enumerables.length; i--;) {
2343 k = Ext.enumerables[i];
2344 if (a.hasOwnProperty(k)) {
2345 fn.call(this, k, a[k]);
2350 fn.call(this, a, b);
2358 * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
2359 * overrides arguments for the call. (Defaults to the arguments passed by the caller)
2361 * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
2363 * @param {Function} fn The function to delegate.
2364 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2365 * **If omitted, defaults to the browser window.**
2366 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2367 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2368 * if a number the args are inserted at the specified position
2369 * @return {Function} The new function
2371 bind: function(fn, scope, args, appendArgs) {
2373 slice = Array.prototype.slice;
2376 var callArgs = args || arguments;
2378 if (appendArgs === true) {
2379 callArgs = slice.call(arguments, 0);
2380 callArgs = callArgs.concat(args);
2382 else if (Ext.isNumber(appendArgs)) {
2383 callArgs = slice.call(arguments, 0); // copy arguments first
2384 Ext.Array.insert(callArgs, appendArgs, args);
2387 return method.apply(scope || window, callArgs);
2392 * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
2393 * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
2394 * This is especially useful when creating callbacks.
2398 * var originalFunction = function(){
2399 * alert(Ext.Array.from(arguments).join(' '));
2402 * var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
2404 * callback(); // alerts 'Hello World'
2405 * callback('by Me'); // alerts 'Hello World by Me'
2407 * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
2409 * @param {Function} fn The original function
2410 * @param {Array} args The arguments to pass to new callback
2411 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2412 * @return {Function} The new callback function
2414 pass: function(fn, args, scope) {
2416 args = Ext.Array.from(args);
2420 return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
2425 * Create an alias to the provided method property with name `methodName` of `object`.
2426 * Note that the execution scope will still be bound to the provided `object` itself.
2428 * @param {Object/Function} object
2429 * @param {String} methodName
2430 * @return {Function} aliasFn
2432 alias: function(object, methodName) {
2434 return object[methodName].apply(object, arguments);
2439 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
2440 * the original one is not called. The resulting function returns the results of the original function.
2441 * The passed function is called with the parameters of the original function. Example usage:
2443 * var sayHi = function(name){
2444 * alert('Hi, ' + name);
2447 * sayHi('Fred'); // alerts "Hi, Fred"
2449 * // create a new function that validates input without
2450 * // directly modifying the original function:
2451 * var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
2452 * return name == 'Brian';
2455 * sayHiToFriend('Fred'); // no alert
2456 * sayHiToFriend('Brian'); // alerts "Hi, Brian"
2458 * @param {Function} origFn The original function.
2459 * @param {Function} newFn The function to call before the original
2460 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2461 * **If omitted, defaults to the scope in which the original function is called or the browser window.**
2462 * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null).
2463 * @return {Function} The new function
2465 createInterceptor: function(origFn, newFn, scope, returnValue) {
2466 var method = origFn;
2467 if (!Ext.isFunction(newFn)) {
2475 newFn.method = origFn;
2476 return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
2482 * Creates a delegate (callback) which, when called, executes after a specific delay.
2484 * @param {Function} fn The function which will be called on a delay when the returned function is called.
2485 * Optionally, a replacement (or additional) argument list may be specified.
2486 * @param {Number} delay The number of milliseconds to defer execution by whenever called.
2487 * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
2488 * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
2489 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2490 * if a number the args are inserted at the specified position.
2491 * @return {Function} A function which, when called, executes the original function after the specified delay.
2493 createDelayed: function(fn, delay, scope, args, appendArgs) {
2494 if (scope || args) {
2495 fn = Ext.Function.bind(fn, scope, args, appendArgs);
2499 setTimeout(function() {
2500 fn.apply(me, arguments);
2506 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
2508 * var sayHi = function(name){
2509 * alert('Hi, ' + name);
2512 * // executes immediately:
2515 * // executes after 2 seconds:
2516 * Ext.Function.defer(sayHi, 2000, this, ['Fred']);
2518 * // this syntax is sometimes useful for deferring
2519 * // execution of an anonymous function:
2520 * Ext.Function.defer(function(){
2521 * alert('Anonymous');
2524 * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
2526 * @param {Function} fn The function to defer.
2527 * @param {Number} millis The number of milliseconds for the setTimeout call
2528 * (if less than or equal to 0 the function is executed immediately)
2529 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
2530 * **If omitted, defaults to the browser window.**
2531 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
2532 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
2533 * if a number the args are inserted at the specified position
2534 * @return {Number} The timeout id that can be used with clearTimeout
2536 defer: function(fn, millis, obj, args, appendArgs) {
2537 fn = Ext.Function.bind(fn, obj, args, appendArgs);
2539 return setTimeout(fn, millis);
2546 * Create a combined function call sequence of the original function + the passed function.
2547 * The resulting function returns the results of the original function.
2548 * The passed function is called with the parameters of the original function. Example usage:
2550 * var sayHi = function(name){
2551 * alert('Hi, ' + name);
2554 * sayHi('Fred'); // alerts "Hi, Fred"
2556 * var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
2557 * alert('Bye, ' + name);
2560 * sayGoodbye('Fred'); // both alerts show
2562 * @param {Function} origFn The original function.
2563 * @param {Function} newFn The function to sequence
2564 * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
2565 * If omitted, defaults to the scope in which the original function is called or the browser window.
2566 * @return {Function} The new function
2568 createSequence: function(origFn, newFn, scope) {
2569 if (!Ext.isFunction(newFn)) {
2574 var retval = origFn.apply(this || window, arguments);
2575 newFn.apply(scope || this || window, arguments);
2582 * Creates a delegate function, optionally with a bound scope which, when called, buffers
2583 * the execution of the passed function for the configured number of milliseconds.
2584 * If called again within that period, the impending invocation will be canceled, and the
2585 * timeout period will begin again.
2587 * @param {Function} fn The function to invoke on a buffered timer.
2588 * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
2590 * @param {Object} scope (optional) The scope (`this` reference) in which
2591 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2592 * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
2593 * passed by the caller.
2594 * @return {Function} A function which invokes the passed function after buffering for the specified time.
2596 createBuffered: function(fn, buffer, scope, args) {
2602 clearInterval(timerId);
2605 timerId = setTimeout(function(){
2606 fn.apply(scope || me, args || arguments);
2613 * Creates a throttled version of the passed function which, when called repeatedly and
2614 * rapidly, invokes the passed function only after a certain interval has elapsed since the
2615 * previous invocation.
2617 * This is useful for wrapping functions which may be called repeatedly, such as
2618 * a handler of a mouse move event when the processing is expensive.
2620 * @param {Function} fn The function to execute at a regular time interval.
2621 * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
2622 * @param {Object} scope (optional) The scope (`this` reference) in which
2623 * the passed function is executed. If omitted, defaults to the scope specified by the caller.
2624 * @returns {Function} A function which invokes the passed function at the specified interval.
2626 createThrottled: function(fn, interval, scope) {
2627 var lastCallTime, elapsed, lastArgs, timer, execute = function() {
2628 fn.apply(scope || this, lastArgs);
2629 lastCallTime = new Date().getTime();
2633 elapsed = new Date().getTime() - lastCallTime;
2634 lastArgs = arguments;
2636 clearTimeout(timer);
2637 if (!lastCallTime || (elapsed >= interval)) {
2640 timer = setTimeout(execute, interval - elapsed);
2649 * @alias Ext.Function#defer
2651 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
2656 * @alias Ext.Function#pass
2658 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
2663 * @alias Ext.Function#bind
2665 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
2668 * @author Jacky Nguyen <jacky@sencha.com>
2669 * @docauthor Jacky Nguyen <jacky@sencha.com>
2672 * A collection of useful static methods to deal with objects
2679 var ExtObject = Ext.Object = {
2682 * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
2683 * query strings. For example:
2685 var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
2687 // objects then equals:
2689 { name: 'hobbies', value: 'reading' },
2690 { name: 'hobbies', value: 'cooking' },
2691 { name: 'hobbies', value: 'swimming' },
2694 var objects = Ext.Object.toQueryObjects('dateOfBirth', {
2702 }, true); // Recursive
2704 // objects then equals:
2706 { name: 'dateOfBirth[day]', value: 3 },
2707 { name: 'dateOfBirth[month]', value: 8 },
2708 { name: 'dateOfBirth[year]', value: 1987 },
2709 { name: 'dateOfBirth[extra][hour]', value: 4 },
2710 { name: 'dateOfBirth[extra][minute]', value: 30 },
2713 * @param {String} name
2714 * @param {Mixed} value
2715 * @param {Boolean} recursive
2718 toQueryObjects: function(name, value, recursive) {
2719 var self = ExtObject.toQueryObjects,
2723 if (Ext.isArray(value)) {
2724 for (i = 0, ln = value.length; i < ln; i++) {
2726 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2736 else if (Ext.isObject(value)) {
2738 if (value.hasOwnProperty(i)) {
2740 objects = objects.concat(self(name + '[' + i + ']', value[i], true));
2762 * Takes an object and converts it to an encoded query string
2766 Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
2767 Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
2768 Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
2769 Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
2770 Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
2774 Ext.Object.toQueryString({
2781 hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2782 }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
2784 // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
2785 // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
2788 * @param {Object} object The object to encode
2789 * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
2790 * (PHP / Ruby on Rails servers and similar). Defaults to false
2791 * @return {String} queryString
2794 toQueryString: function(object, recursive) {
2795 var paramObjects = [],
2797 i, j, ln, paramObject, value;
2800 if (object.hasOwnProperty(i)) {
2801 paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
2805 for (j = 0, ln = paramObjects.length; j < ln; j++) {
2806 paramObject = paramObjects[j];
2807 value = paramObject.value;
2809 if (Ext.isEmpty(value)) {
2812 else if (Ext.isDate(value)) {
2813 value = Ext.Date.toString(value);
2816 params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
2819 return params.join('&');
2823 * Converts a query string back into an object.
2827 Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
2828 Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
2829 Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
2830 Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}
2834 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);
2844 hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
2847 * @param {String} queryString The query string to decode
2848 * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
2849 * PHP / Ruby on Rails servers and similar. Defaults to false
2852 fromQueryString: function(queryString, recursive) {
2853 var parts = queryString.replace(/^\?/, '').split('&'),
2855 temp, components, name, value, i, ln,
2856 part, j, subLn, matchedKeys, matchedName,
2859 for (i = 0, ln = parts.length; i < ln; i++) {
2862 if (part.length > 0) {
2863 components = part.split('=');
2864 name = decodeURIComponent(components[0]);
2865 value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';
2868 if (object.hasOwnProperty(name)) {
2869 if (!Ext.isArray(object[name])) {
2870 object[name] = [object[name]];
2873 object[name].push(value);
2876 object[name] = value;
2880 matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
2881 matchedName = name.match(/^([^\[]+)/);
2885 sourceClass: "Ext.Object",
2886 sourceMethod: "fromQueryString",
2887 queryString: queryString,
2888 recursive: recursive,
2889 msg: 'Malformed query string given, failed parsing name from "' + part + '"'
2893 name = matchedName[0];
2896 if (matchedKeys === null) {
2897 object[name] = value;
2901 for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
2902 key = matchedKeys[j];
2903 key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
2911 for (j = 0, subLn = keys.length; j < subLn; j++) {
2914 if (j === subLn - 1) {
2915 if (Ext.isArray(temp) && key === '') {
2923 if (temp[key] === undefined || typeof temp[key] === 'string') {
2924 nextKey = keys[j+1];
2926 temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
2940 * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
2941 * by returning `false` in the callback function. For example:
2946 loves: ['food', 'sleeping', 'wife']
2949 Ext.Object.each(person, function(key, value, myself) {
2950 console.log(key + ":" + value);
2952 if (key === 'hairColor') {
2953 return false; // stop the iteration
2957 * @param {Object} object The object to iterate
2958 * @param {Function} fn The callback function. Passed arguments for each iteration are:
2962 - {Object} `object` The object itself
2964 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
2967 each: function(object, fn, scope) {
2968 for (var property in object) {
2969 if (object.hasOwnProperty(property)) {
2970 if (fn.call(scope || object, property, object[property], object) === false) {
2978 * Merges any number of objects recursively without referencing them or their children.
2981 companyName: 'Ext JS',
2982 products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
2986 location: 'Palo Alto',
2992 companyName: 'Sencha Inc.',
2993 products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
2996 location: 'Redwood City'
3000 var sencha = Ext.Object.merge(extjs, newStuff);
3002 // extjs and sencha then equals to
3004 companyName: 'Sencha Inc.',
3005 products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
3009 location: 'Redwood City'
3014 * @param {Object} object,...
3015 * @return {Object} merged The object that is created as a result of merging all the objects passed in.
3018 merge: function(source, key, value) {
3019 if (typeof key === 'string') {
3020 if (value && value.constructor === Object) {
3021 if (source[key] && source[key].constructor === Object) {
3022 ExtObject.merge(source[key], value);
3025 source[key] = Ext.clone(value);
3029 source[key] = value;
3036 ln = arguments.length,
3039 for (; i < ln; i++) {
3040 object = arguments[i];
3042 for (property in object) {
3043 if (object.hasOwnProperty(property)) {
3044 ExtObject.merge(source, property, object[property]);
3053 * Returns the first matching key corresponding to the given value.
3054 * If no matching value is found, null is returned.
3061 alert(Ext.Object.getKey(sencha, 'loves')); // alerts 'food'
3063 * @param {Object} object
3064 * @param {Object} value The value to find
3067 getKey: function(object, value) {
3068 for (var property in object) {
3069 if (object.hasOwnProperty(property) && object[property] === value) {
3078 * Gets all values of the given object as an array.
3080 var values = Ext.Object.getValues({
3083 }); // ['Jacky', 'food']
3085 * @param {Object} object
3086 * @return {Array} An array of values from the object
3089 getValues: function(object) {
3093 for (property in object) {
3094 if (object.hasOwnProperty(property)) {
3095 values.push(object[property]);
3103 * Gets all keys of the given object as an array.
3105 var values = Ext.Object.getKeys({
3108 }); // ['name', 'loves']
3110 * @param {Object} object
3111 * @return {Array} An array of keys from the object
3114 getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
3118 for (property in object) {
3119 if (object.hasOwnProperty(property)) {
3120 keys.push(property);
3128 * Gets the total number of this object's own properties
3130 var size = Ext.Object.getSize({
3133 }); // size equals 2
3135 * @param {Object} object
3136 * @return {Number} size
3139 getSize: function(object) {
3143 for (property in object) {
3144 if (object.hasOwnProperty(property)) {
3155 * A convenient alias method for {@link Ext.Object#merge}
3160 Ext.merge = Ext.Object.merge;
3163 * A convenient alias method for {@link Ext.Object#toQueryString}
3167 * @deprecated 4.0.0 Use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
3169 Ext.urlEncode = function() {
3170 var args = Ext.Array.from(arguments),
3173 // Support for the old `pre` argument
3174 if ((typeof args[1] === 'string')) {
3175 prefix = args[1] + '&';
3179 return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
3183 * A convenient alias method for {@link Ext.Object#fromQueryString}
3187 * @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
3189 Ext.urlDecode = function() {
3190 return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
3197 * A set of useful static methods to deal with date
3198 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
3199 * this object for convenience
3201 * The date parsing and formatting syntax contains a subset of
3202 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
3203 * supported will provide results equivalent to their PHP versions.
3205 * The following is a list of all currently supported formats:
3207 Format Description Example returned values
3208 ------ ----------------------------------------------------------------------- -----------------------
3209 d Day of the month, 2 digits with leading zeros 01 to 31
3210 D A short textual representation of the day of the week Mon to Sun
3211 j Day of the month without leading zeros 1 to 31
3212 l A full textual representation of the day of the week Sunday to Saturday
3213 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
3214 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
3215 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
3216 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
3217 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
3218 F A full textual representation of a month, such as January or March January to December
3219 m Numeric representation of a month, with leading zeros 01 to 12
3220 M A short textual representation of a month Jan to Dec
3221 n Numeric representation of a month, without leading zeros 1 to 12
3222 t Number of days in the given month 28 to 31
3223 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
3224 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
3225 belongs to the previous or next year, that year is used instead)
3226 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
3227 y A two digit representation of a year Examples: 99 or 03
3228 a Lowercase Ante meridiem and Post meridiem am or pm
3229 A Uppercase Ante meridiem and Post meridiem AM or PM
3230 g 12-hour format of an hour without leading zeros 1 to 12
3231 G 24-hour format of an hour without leading zeros 0 to 23
3232 h 12-hour format of an hour with leading zeros 01 to 12
3233 H 24-hour format of an hour with leading zeros 00 to 23
3234 i Minutes, with leading zeros 00 to 59
3235 s Seconds, with leading zeros 00 to 59
3236 u Decimal fraction of a second Examples:
3237 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
3238 100 (i.e. 0.100s) or
3239 999 (i.e. 0.999s) or
3240 999876543210 (i.e. 0.999876543210s)
3241 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
3242 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
3243 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
3244 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
3247 1) If unspecified, the month / day defaults to the current month / day, 1991 or
3248 the time defaults to midnight, while the timezone defaults to the 1992-10 or
3249 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
3250 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
3251 are optional. 1995-07-18T17:21:28-02:00 or
3252 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
3253 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
3254 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
3255 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
3256 date-time granularity which are supported, or see 2000-02-13T21:25:33
3257 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
3258 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
3259 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
3260 \/Date(1238606590509+0800)\/
3263 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
3266 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
3268 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
3269 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
3270 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
3271 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
3274 * Here are some standard date/time patterns that you might find helpful. They
3275 * are not part of the source of Ext.Date, but to use them you can simply copy this
3276 * block of code into any script that is included after Ext.Date and they will also become
3277 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
3279 Ext.Date.patterns = {
3280 ISO8601Long:"Y-m-d H:i:s",
3281 ISO8601Short:"Y-m-d",
3283 LongDate: "l, F d, Y",
3284 FullDateTime: "l, F d, Y g:i:s A",
3287 LongTime: "g:i:s A",
3288 SortableDateTime: "Y-m-d\\TH:i:s",
3289 UniversalSortableDateTime: "Y-m-d H:i:sO",
3296 var dt = new Date();
3297 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
3299 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
3300 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
3305 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
3306 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
3307 * They generate precompiled functions from format patterns instead of parsing and
3308 * processing each pattern every time a date is formatted. These functions are available
3309 * on every Date object.
3314 // create private copy of Ext's Ext.util.Format.format() method
3315 // - to remove unnecessary dependency
3316 // - to resolve namespace conflict with MS-Ajax's implementation
3317 function xf(format) {
3318 var args = Array.prototype.slice.call(arguments, 1);
3319 return format.replace(/\{(\d+)\}/g, function(m, i) {
3326 * Returns the current timestamp
3327 * @return {Date} The current timestamp
3330 now: Date.now || function() {
3338 toString: function(date) {
3339 var pad = Ext.String.leftPad;
3341 return date.getFullYear() + "-"
3342 + pad(date.getMonth() + 1, 2, '0') + "-"
3343 + pad(date.getDate(), 2, '0') + "T"
3344 + pad(date.getHours(), 2, '0') + ":"
3345 + pad(date.getMinutes(), 2, '0') + ":"
3346 + pad(date.getSeconds(), 2, '0');
3350 * Returns the number of milliseconds between two dates
3351 * @param {Date} dateA The first date
3352 * @param {Date} dateB (optional) The second date, defaults to now
3353 * @return {Number} The difference in milliseconds
3355 getElapsed: function(dateA, dateB) {
3356 return Math.abs(dateA - (dateB || new Date()));
3360 * Global flag which determines if strict date parsing should be used.
3361 * Strict date parsing will not roll-over invalid dates, which is the
3362 * default behaviour of javascript Date objects.
3363 * (see {@link #parse} for more information)
3364 * Defaults to <tt>false</tt>.
3371 formatCodeToRegex: function(character, currentGroup) {
3372 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
3373 var p = utilDate.parseCodes[character];
3376 p = typeof p == 'function'? p() : p;
3377 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
3380 return p ? Ext.applyIf({
3381 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
3385 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
3390 * <p>An object hash in which each property is a date parsing function. The property name is the
3391 * format string which that function parses.</p>
3392 * <p>This object is automatically populated with date parsing functions as
3393 * date formats are requested for Ext standard formatting strings.</p>
3394 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
3395 * may be used as a format string to {@link #parse}.<p>
3396 * <p>Example:</p><pre><code>
3397 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
3399 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
3400 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
3401 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
3402 * (i.e. prevent javascript Date "rollover") (The default must be false).
3403 * Invalid date strings should return null when parsed.</div></li>
3405 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
3406 * formatting function must be placed into the {@link #formatFunctions} property.
3407 * @property parseFunctions
3412 "MS": function(input, strict) {
3413 // note: the timezone offset is ignored since the MS Ajax server sends
3414 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
3415 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
3416 var r = (input || '').match(re);
3417 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
3423 * <p>An object hash in which each property is a date formatting function. The property name is the
3424 * format string which corresponds to the produced formatted date string.</p>
3425 * <p>This object is automatically populated with date formatting functions as
3426 * date formats are requested for Ext standard formatting strings.</p>
3427 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
3428 * may be used as a format string to {@link #format}. Example:</p><pre><code>
3429 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
3431 * <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>
3432 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
3434 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
3435 * parsing function must be placed into the {@link #parseFunctions} property.
3436 * @property formatFunctions
3442 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
3443 return '\\/Date(' + this.getTime() + ')\\/';
3450 * Date interval constant
3457 * Date interval constant
3464 * Date interval constant
3470 /** Date interval constant
3477 * Date interval constant
3484 * Date interval constant
3491 * Date interval constant
3498 * <p>An object hash containing default date values used during date parsing.</p>
3499 * <p>The following properties are available:<div class="mdetail-params"><ul>
3500 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
3501 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
3502 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
3503 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
3504 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
3505 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
3506 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
3508 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
3509 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
3510 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
3511 * It is the responsiblity of the developer to account for this.</b></p>
3514 // set default day value to the first day of the month
3515 Ext.Date.defaults.d = 1;
3517 // parse a February date string containing only year and month values.
3518 // setting the default day value to 1 prevents weird date rollover issues
3519 // when attempting to parse the following date string on, for example, March 31st 2009.
3520 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
3522 * @property defaults
3529 * An array of textual day names.
3530 * Override these values for international dates.
3533 Ext.Date.dayNames = [
3553 * An array of textual month names.
3554 * Override these values for international dates.
3557 Ext.Date.monthNames = [
3582 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
3583 * Override these values for international dates.
3586 Ext.Date.monthNumbers = {
3587 'ShortJanNameInYourLang':0,
3588 'ShortFebNameInYourLang':1,
3610 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
3611 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
3612 * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
3613 * @property defaultFormat
3617 defaultFormat : "m/d/Y",
3619 * Get the short month name for the given month number.
3620 * Override this function for international dates.
3621 * @param {Number} month A zero-based javascript month number.
3622 * @return {String} The short month name.
3625 getShortMonthName : function(month) {
3626 return utilDate.monthNames[month].substring(0, 3);
3630 * Get the short day name for the given day number.
3631 * Override this function for international dates.
3632 * @param {Number} day A zero-based javascript day number.
3633 * @return {String} The short day name.
3636 getShortDayName : function(day) {
3637 return utilDate.dayNames[day].substring(0, 3);
3641 * Get the zero-based javascript month number for the given short/full month name.
3642 * Override this function for international dates.
3643 * @param {String} name The short/full month name.
3644 * @return {Number} The zero-based javascript month number.
3647 getMonthNumber : function(name) {
3648 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
3649 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
3653 * Checks if the specified format contains hour information
3654 * @param {String} format The format to check
3655 * @return {Boolean} True if the format contains hour information
3659 formatContainsHourInfo : (function(){
3660 var stripEscapeRe = /(\\.)/g,
3661 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
3662 return function(format){
3663 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
3668 * Checks if the specified format contains information about
3669 * anything other than the time.
3670 * @param {String} format The format to check
3671 * @return {Boolean} True if the format contains information about
3672 * date/day information.
3676 formatContainsDateInfo : (function(){
3677 var stripEscapeRe = /(\\.)/g,
3678 dateInfoRe = /([djzmnYycU]|MS)/;
3680 return function(format){
3681 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
3686 * The base format-code to formatting-function hashmap used by the {@link #format} method.
3687 * Formatting functions are strings (or functions which return strings) which
3688 * will return the appropriate value when evaluated in the context of the Date object
3689 * from which the {@link #format} method is called.
3690 * Add to / override these mappings for custom date formatting.
3691 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
3694 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
3695 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
3701 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
3702 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
3703 j: "this.getDate()",
3704 l: "Ext.Date.dayNames[this.getDay()]",
3705 N: "(this.getDay() ? this.getDay() : 7)",
3706 S: "Ext.Date.getSuffix(this)",
3708 z: "Ext.Date.getDayOfYear(this)",
3709 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
3710 F: "Ext.Date.monthNames[this.getMonth()]",
3711 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
3712 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
3713 n: "(this.getMonth() + 1)",
3714 t: "Ext.Date.getDaysInMonth(this)",
3715 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
3716 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
3717 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
3718 y: "('' + this.getFullYear()).substring(2, 4)",
3719 a: "(this.getHours() < 12 ? 'am' : 'pm')",
3720 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
3721 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
3722 G: "this.getHours()",
3723 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
3724 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
3725 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
3726 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
3727 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
3728 O: "Ext.Date.getGMTOffset(this)",
3729 P: "Ext.Date.getGMTOffset(this, true)",
3730 T: "Ext.Date.getTimezone(this)",
3731 Z: "(this.getTimezoneOffset() * -60)",
3733 c: function() { // ISO-8601 -- GMT format
3734 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
3735 var e = c.charAt(i);
3736 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
3738 return code.join(" + ");
3741 c: function() { // ISO-8601 -- UTC format
3743 "this.getUTCFullYear()", "'-'",
3744 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
3745 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
3747 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
3748 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
3749 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
3755 U: "Math.round(this.getTime() / 1000)"
3759 * Checks if the passed Date parameters will cause a javascript Date "rollover".
3760 * @param {Number} year 4-digit year
3761 * @param {Number} month 1-based month-of-year
3762 * @param {Number} day Day of month
3763 * @param {Number} hour (optional) Hour
3764 * @param {Number} minute (optional) Minute
3765 * @param {Number} second (optional) Second
3766 * @param {Number} millisecond (optional) Millisecond
3767 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
3770 isValid : function(y, m, d, h, i, s, ms) {
3777 // Special handling for year < 100
3778 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
3780 return y == dt.getFullYear() &&
3781 m == dt.getMonth() + 1 &&
3782 d == dt.getDate() &&
3783 h == dt.getHours() &&
3784 i == dt.getMinutes() &&
3785 s == dt.getSeconds() &&
3786 ms == dt.getMilliseconds();
3790 * Parses the passed string using the specified date format.
3791 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
3792 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
3793 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
3794 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
3795 * Keep in mind that the input date string must precisely match the specified format string
3796 * in order for the parse operation to be successful (failed parse operations return a null value).
3797 * <p>Example:</p><pre><code>
3798 //dt = Fri May 25 2007 (current date)
3799 var dt = new Date();
3801 //dt = Thu May 25 2006 (today's month/day in 2006)
3802 dt = Ext.Date.parse("2006", "Y");
3804 //dt = Sun Jan 15 2006 (all date parts specified)
3805 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
3807 //dt = Sun Jan 15 2006 15:20:01
3808 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
3810 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
3811 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
3813 * @param {String} input The raw date string.
3814 * @param {String} format The expected date string format.
3815 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
3816 (defaults to false). Invalid date strings will return null when parsed.
3817 * @return {Date} The parsed Date.
3820 parse : function(input, format, strict) {
3821 var p = utilDate.parseFunctions;
3822 if (p[format] == null) {
3823 utilDate.createParser(format);
3825 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
3829 parseDate: function(input, format, strict){
3830 return utilDate.parse(input, format, strict);
3835 getFormatCode : function(character) {
3836 var f = utilDate.formatCodes[character];
3839 f = typeof f == 'function'? f() : f;
3840 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
3843 // note: unknown characters are treated as literals
3844 return f || ("'" + Ext.String.escape(character) + "'");
3848 createFormat : function(format) {
3853 for (var i = 0; i < format.length; ++i) {
3854 ch = format.charAt(i);
3855 if (!special && ch == "\\") {
3857 } else if (special) {
3859 code.push("'" + Ext.String.escape(ch) + "'");
3861 code.push(utilDate.getFormatCode(ch));
3864 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
3868 createParser : (function() {
3870 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
3871 "def = Ext.Date.defaults,",
3872 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
3877 "if(u != null){", // i.e. unix time is defined
3878 "v = new Date(u * 1000);", // give top priority to UNIX time
3880 // create Date object representing midnight of the current day;
3881 // this will provide us with our date defaults
3882 // (note: clearTime() handles Daylight Saving Time automatically)
3883 "dt = Ext.Date.clearTime(new Date);",
3885 // date calculations (note: these calculations create a dependency on Ext.Number.from())
3886 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
3887 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
3888 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
3890 // time calculations (note: these calculations create a dependency on Ext.Number.from())
3891 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
3892 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
3893 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
3894 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
3896 "if(z >= 0 && y >= 0){",
3897 // both the year and zero-based day of year are defined and >= 0.
3898 // these 2 values alone provide sufficient info to create a full date object
3900 // create Date object representing January 1st for the given year
3901 // handle years < 100 appropriately
3902 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3904 // then add day of year, checking for Date "rollover" if necessary
3905 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
3906 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
3907 "v = null;", // invalid date, so return null
3909 // plain old Date object
3910 // handle years < 100 properly
3911 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
3917 // favour UTC offset over GMT offset
3919 // reset to UTC, then add offset
3920 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
3922 // reset to GMT, then add offset
3923 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
3930 return function(format) {
3931 var regexNum = utilDate.parseRegexes.length,
3938 for (var i = 0; i < format.length; ++i) {
3939 ch = format.charAt(i);
3940 if (!special && ch == "\\") {
3942 } else if (special) {
3944 regex.push(Ext.String.escape(ch));
3946 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
3947 currentGroup += obj.g;
3949 if (obj.g && obj.c) {
3955 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
3956 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
3964 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
3965 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
3966 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
3970 c:"d = parseInt(results[{0}], 10);\n",
3971 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
3975 c:"d = parseInt(results[{0}], 10);\n",
3976 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
3979 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
3983 s:"(?:" + a.join("|") +")"
3990 s:"(?:" + utilDate.dayNames.join("|") + ")"
3996 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4006 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4010 c:"z = parseInt(results[{0}], 10);\n",
4011 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4016 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4021 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4022 s:"(" + utilDate.monthNames.join("|") + ")"
4026 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
4027 return Ext.applyIf({
4028 s:"(" + a.join("|") + ")"
4029 }, utilDate.formatCodeToRegex("F"));
4033 c:"m = parseInt(results[{0}], 10) - 1;\n",
4034 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4038 c:"m = parseInt(results[{0}], 10) - 1;\n",
4039 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4044 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4052 return utilDate.formatCodeToRegex("Y");
4056 c:"y = parseInt(results[{0}], 10);\n",
4057 s:"(\\d{4})" // 4-digit year
4061 c:"var ty = parseInt(results[{0}], 10);\n"
4062 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4066 * In the am/pm parsing routines, we allow both upper and lower case
4067 * even though it doesn't exactly match the spec. It gives much more flexibility
4068 * in being able to specify case insensitive regexes.
4072 c:"if (/(am)/i.test(results[{0}])) {\n"
4073 + "if (!h || h == 12) { h = 0; }\n"
4074 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4079 c:"if (/(am)/i.test(results[{0}])) {\n"
4080 + "if (!h || h == 12) { h = 0; }\n"
4081 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4085 return utilDate.formatCodeToRegex("G");
4089 c:"h = parseInt(results[{0}], 10);\n",
4090 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4093 return utilDate.formatCodeToRegex("H");
4097 c:"h = parseInt(results[{0}], 10);\n",
4098 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4102 c:"i = parseInt(results[{0}], 10);\n",
4103 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4107 c:"s = parseInt(results[{0}], 10);\n",
4108 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4112 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4113 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4118 "o = results[{0}];",
4119 "var sn = o.substring(0,1),", // get + / - sign
4120 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4121 "mn = o.substring(3,5) % 60;", // get minutes
4122 "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
4124 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4129 "o = results[{0}];",
4130 "var sn = o.substring(0,1),", // get + / - sign
4131 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4132 "mn = o.substring(4,6) % 60;", // get minutes
4133 "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
4135 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4140 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4144 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4145 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4146 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4151 utilDate.formatCodeToRegex("Y", 1), // year
4152 utilDate.formatCodeToRegex("m", 2), // month
4153 utilDate.formatCodeToRegex("d", 3), // day
4154 utilDate.formatCodeToRegex("h", 4), // hour
4155 utilDate.formatCodeToRegex("i", 5), // minute
4156 utilDate.formatCodeToRegex("s", 6), // second
4157 {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)
4158 {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
4159 "if(results[8]) {", // timezone specified
4160 "if(results[8] == 'Z'){",
4162 "}else if (results[8].indexOf(':') > -1){",
4163 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
4165 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
4171 for (var i = 0, l = arr.length; i < l; ++i) {
4172 calc.push(arr[i].c);
4179 arr[0].s, // year (required)
4180 "(?:", "-", arr[1].s, // month (optional)
4181 "(?:", "-", arr[2].s, // day (optional)
4183 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4184 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
4185 "(?::", arr[5].s, ")?", // seconds (optional)
4186 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4187 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4196 c:"u = parseInt(results[{0}], 10);\n",
4197 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4201 //Old Ext.Date prototype methods.
4203 dateFormat: function(date, format) {
4204 return utilDate.format(date, format);
4208 * Formats a date given the supplied format string.
4209 * @param {Date} date The date to format
4210 * @param {String} format The format string
4211 * @return {String} The formatted date
4213 format: function(date, format) {
4214 if (utilDate.formatFunctions[format] == null) {
4215 utilDate.createFormat(format);
4217 var result = utilDate.formatFunctions[format].call(date);
4222 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4224 * Note: The date string returned by the javascript Date object's toString() method varies
4225 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4226 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4227 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4228 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4229 * from the GMT offset portion of the date string.
4230 * @param {Date} date The date
4231 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4233 getTimezone : function(date) {
4234 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4236 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4237 // 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)
4238 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4239 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4240 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
4242 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
4243 // step 1: (?:\((.*)\) -- find timezone in parentheses
4244 // 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
4245 // step 3: remove all non uppercase characters found in step 1 and 2
4246 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
4250 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
4251 * @param {Date} date The date
4252 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
4253 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
4255 getGMTOffset : function(date, colon) {
4256 var offset = date.getTimezoneOffset();
4257 return (offset > 0 ? "-" : "+")
4258 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
4259 + (colon ? ":" : "")
4260 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
4264 * Get the numeric day number of the year, adjusted for leap year.
4265 * @param {Date} date The date
4266 * @return {Number} 0 to 364 (365 in leap years).
4268 getDayOfYear: function(date) {
4270 d = Ext.Date.clone(date),
4271 m = date.getMonth(),
4274 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
4275 num += utilDate.getDaysInMonth(d);
4277 return num + date.getDate() - 1;
4281 * Get the numeric ISO-8601 week number of the year.
4282 * (equivalent to the format specifier 'W', but without a leading zero).
4283 * @param {Date} date The date
4284 * @return {Number} 1 to 53
4287 getWeekOfYear : (function() {
4288 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
4289 var ms1d = 864e5, // milliseconds in a day
4290 ms7d = 7 * ms1d; // milliseconds in a week
4292 return function(date) { // return a closure so constants get calculated only once
4293 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
4294 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
4295 Wyr = new Date(AWN * ms7d).getUTCFullYear();
4297 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
4302 * Checks if the current date falls within a leap year.
4303 * @param {Date} date The date
4304 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
4306 isLeapYear : function(date) {
4307 var year = date.getFullYear();
4308 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
4312 * Get the first day of the current month, adjusted for leap year. The returned value
4313 * is the numeric day index within the week (0-6) which can be used in conjunction with
4314 * the {@link #monthNames} array to retrieve the textual day name.
4317 var dt = new Date('1/10/2007'),
4318 firstDay = Ext.Date.getFirstDayOfMonth(dt);
4319 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
4321 * @param {Date} date The date
4322 * @return {Number} The day number (0-6).
4324 getFirstDayOfMonth : function(date) {
4325 var day = (date.getDay() - (date.getDate() - 1)) % 7;
4326 return (day < 0) ? (day + 7) : day;
4330 * Get the last day of the current month, adjusted for leap year. The returned value
4331 * is the numeric day index within the week (0-6) which can be used in conjunction with
4332 * the {@link #monthNames} array to retrieve the textual day name.
4335 var dt = new Date('1/10/2007'),
4336 lastDay = Ext.Date.getLastDayOfMonth(dt);
4337 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
4339 * @param {Date} date The date
4340 * @return {Number} The day number (0-6).
4342 getLastDayOfMonth : function(date) {
4343 return utilDate.getLastDateOfMonth(date).getDay();
4348 * Get the date of the first day of the month in which this date resides.
4349 * @param {Date} date The date
4352 getFirstDateOfMonth : function(date) {
4353 return new Date(date.getFullYear(), date.getMonth(), 1);
4357 * Get the date of the last day of the month in which this date resides.
4358 * @param {Date} date The date
4361 getLastDateOfMonth : function(date) {
4362 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
4366 * Get the number of days in the current month, adjusted for leap year.
4367 * @param {Date} date The date
4368 * @return {Number} The number of days in the month.
4371 getDaysInMonth: (function() {
4372 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4374 return function(date) { // return a closure for efficiency
4375 var m = date.getMonth();
4377 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
4382 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
4383 * @param {Date} date The date
4384 * @return {String} 'st, 'nd', 'rd' or 'th'.
4386 getSuffix : function(date) {
4387 switch (date.getDate()) {
4404 * Creates and returns a new Date instance with the exact same date value as the called instance.
4405 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
4406 * variable will also be changed. When the intention is to create a new variable that will not
4407 * modify the original instance, you should create a clone.
4409 * Example of correctly cloning a date:
4412 var orig = new Date('10/1/2006');
4415 console.log(orig); //returns 'Thu Oct 05 2006'!
4418 var orig = new Date('10/1/2006'),
4419 copy = Ext.Date.clone(orig);
4421 console.log(orig); //returns 'Thu Oct 01 2006'
4423 * @param {Date} date The date
4424 * @return {Date} The new Date instance.
4426 clone : function(date) {
4427 return new Date(date.getTime());
4431 * Checks if the current date is affected by Daylight Saving Time (DST).
4432 * @param {Date} date The date
4433 * @return {Boolean} True if the current date is affected by DST.
4435 isDST : function(date) {
4436 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
4437 // courtesy of @geoffrey.mcgill
4438 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
4442 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
4443 * automatically adjusting for Daylight Saving Time (DST) where applicable.
4444 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
4445 * @param {Date} date The date
4446 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
4447 * @return {Date} this or the clone.
4449 clearTime : function(date, clone) {
4451 return Ext.Date.clearTime(Ext.Date.clone(date));
4454 // get current date before clearing time
4455 var d = date.getDate();
4461 date.setMilliseconds(0);
4463 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
4464 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
4465 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
4467 // increment hour until cloned date == current date
4468 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
4471 date.setHours(c.getHours());
4478 * Provides a convenient method for performing basic date arithmetic. This method
4479 * does not modify the Date instance being called - it creates and returns
4480 * a new Date instance containing the resulting date value.
4485 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
4486 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
4488 // Negative values will be subtracted:
4489 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
4490 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
4494 * @param {Date} date The date to modify
4495 * @param {String} interval A valid date interval enum value.
4496 * @param {Number} value The amount to add to the current date.
4497 * @return {Date} The new Date instance.
4499 add : function(date, interval, value) {
4500 var d = Ext.Date.clone(date),
4502 if (!interval || value === 0) return d;
4504 switch(interval.toLowerCase()) {
4505 case Ext.Date.MILLI:
4506 d.setMilliseconds(d.getMilliseconds() + value);
4508 case Ext.Date.SECOND:
4509 d.setSeconds(d.getSeconds() + value);
4511 case Ext.Date.MINUTE:
4512 d.setMinutes(d.getMinutes() + value);
4515 d.setHours(d.getHours() + value);
4518 d.setDate(d.getDate() + value);
4520 case Ext.Date.MONTH:
4521 var day = date.getDate();
4523 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
4526 d.setMonth(date.getMonth() + value);
4529 d.setFullYear(date.getFullYear() + value);
4536 * Checks if a date falls on or between the given start and end dates.
4537 * @param {Date} date The date to check
4538 * @param {Date} start Start date
4539 * @param {Date} end End date
4540 * @return {Boolean} true if this date falls on or between the given start and end dates.
4542 between : function(date, start, end) {
4543 var t = date.getTime();
4544 return start.getTime() <= t && t <= end.getTime();
4547 //Maintains compatibility with old static and prototype window.Date methods.
4548 compat: function() {
4549 var nativeDate = window.Date,
4551 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'],
4552 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
4555 Ext.Array.forEach(statics, function(s) {
4556 nativeDate[s] = utilDate[s];
4559 //Append to prototype
4560 Ext.Array.forEach(proto, function(s) {
4561 nativeDate.prototype[s] = function() {
4562 var args = Array.prototype.slice.call(arguments);
4564 return utilDate[s].apply(utilDate, args);
4570 var utilDate = Ext.Date;
4575 * @author Jacky Nguyen <jacky@sencha.com>
4576 * @docauthor Jacky Nguyen <jacky@sencha.com>
4579 * The root of all classes created with {@link Ext#define}
4580 * All prototype and static members of this class are inherited by any other class
4583 (function(flexSetter) {
4585 var Base = Ext.Base = function() {};
4587 $className: 'Ext.Base',
4592 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
4593 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
4594 * for a detailed comparison
4596 * Ext.define('My.Cat', {
4598 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4601 * constructor: function() {
4602 * alert(this.self.speciesName); / dependent on 'this'
4607 * clone: function() {
4608 * return new this.self();
4613 * Ext.define('My.SnowLeopard', {
4616 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4620 * var cat = new My.Cat(); // alerts 'Cat'
4621 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
4623 * var clone = snowLeopard.clone();
4624 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4631 // Default constructor, simply returns `this`
4632 constructor: function() {
4637 * Initialize configuration for this class. a typical example:
4639 * Ext.define('My.awesome.Class', {
4640 * // The default config
4646 * constructor: function(config) {
4647 * this.initConfig(config);
4653 * var awesome = new My.awesome.Class({
4654 * name: 'Super Awesome'
4657 * alert(awesome.getName()); // 'Super Awesome'
4660 * @param {Object} config
4661 * @return {Object} mixins The mixin prototypes as key - value pairs
4663 initConfig: function(config) {
4664 if (!this.$configInited) {
4665 this.config = Ext.Object.merge({}, this.config || {}, config || {});
4667 this.applyConfig(this.config);
4669 this.$configInited = true;
4678 setConfig: function(config) {
4679 this.applyConfig(config || {});
4687 applyConfig: flexSetter(function(name, value) {
4688 var setter = 'set' + Ext.String.capitalize(name);
4690 if (typeof this[setter] === 'function') {
4691 this[setter].call(this, value);
4698 * Call the parent's overridden method. For example:
4700 * Ext.define('My.own.A', {
4701 * constructor: function(test) {
4706 * Ext.define('My.own.B', {
4707 * extend: 'My.own.A',
4709 * constructor: function(test) {
4712 * this.callParent([test + 1]);
4716 * Ext.define('My.own.C', {
4717 * extend: 'My.own.B',
4719 * constructor: function() {
4720 * alert("Going to call parent's overriden constructor...");
4722 * this.callParent(arguments);
4726 * var a = new My.own.A(1); // alerts '1'
4727 * var b = new My.own.B(1); // alerts '1', then alerts '2'
4728 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
4729 * // alerts '2', then alerts '3'
4732 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4733 * from the current method, for example: `this.callParent(arguments)`
4734 * @return {Mixed} Returns the result from the superclass' method
4736 callParent: function(args) {
4737 var method = this.callParent.caller,
4738 parentClass, methodName;
4740 if (!method.$owner) {
4741 if (!method.caller) {
4743 sourceClass: Ext.getClassName(this),
4744 sourceMethod: "callParent",
4745 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4749 method = method.caller;
4752 parentClass = method.$owner.superclass;
4753 methodName = method.$name;
4755 if (!(methodName in parentClass)) {
4757 sourceClass: Ext.getClassName(this),
4758 sourceMethod: methodName,
4759 msg: "this.callParent() was called but there's no such method (" + methodName +
4760 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
4764 return parentClass[methodName].apply(this, args || []);
4769 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
4770 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
4771 * `this` points to during run-time
4773 * Ext.define('My.Cat', {
4776 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
4779 * constructor: function() {
4780 * var statics = this.statics();
4782 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
4783 * // equivalent to: My.Cat.speciesName
4785 * alert(this.self.speciesName); // dependent on 'this'
4787 * statics.totalCreated++;
4792 * clone: function() {
4793 * var cloned = new this.self; // dependent on 'this'
4795 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
4802 * Ext.define('My.SnowLeopard', {
4806 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
4809 * constructor: function() {
4810 * this.callParent();
4814 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
4816 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
4818 * var clone = snowLeopard.clone();
4819 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
4820 * alert(clone.groupName); // alerts 'Cat'
4822 * alert(My.Cat.totalCreated); // alerts 3
4827 statics: function() {
4828 var method = this.statics.caller,
4835 return method.$owner;
4839 * Call the original method that was previously overridden with {@link Ext.Base#override}
4841 * Ext.define('My.Cat', {
4842 * constructor: function() {
4843 * alert("I'm a cat!");
4850 * constructor: function() {
4851 * alert("I'm going to be a cat!");
4853 * var instance = this.callOverridden();
4855 * alert("Meeeeoooowwww");
4861 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
4862 * // alerts "I'm a cat!"
4863 * // alerts "Meeeeoooowwww"
4865 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
4866 * @return {Mixed} Returns the result after calling the overridden method
4868 callOverridden: function(args) {
4869 var method = this.callOverridden.caller;
4871 if (!method.$owner) {
4873 sourceClass: Ext.getClassName(this),
4874 sourceMethod: "callOverridden",
4875 msg: "Attempting to call a protected method from the public scope, which is not allowed"
4879 if (!method.$previous) {
4881 sourceClass: Ext.getClassName(this),
4882 sourceMethod: "callOverridden",
4883 msg: "this.callOverridden was called in '" + method.$name +
4884 "' but this method has never been overridden"
4888 return method.$previous.apply(this, args || []);
4891 destroy: function() {}
4894 // These static properties will be copied to every newly created class with {@link Ext#define}
4895 Ext.apply(Ext.Base, {
4897 * Create a new instance of this Class.
4899 * Ext.define('My.cool.Class', {
4903 * My.cool.Class.create({
4907 * All parameters are passed to the constructor of the class.
4909 * @return {Object} the created instance.
4912 create: function() {
4913 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
4919 own: flexSetter(function(name, value) {
4920 if (typeof value === 'function') {
4921 this.ownMethod(name, value);
4924 this.prototype[name] = value;
4931 ownMethod: function(name, fn) {
4934 if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
4938 return originalFn.apply(this, arguments);
4943 className = Ext.getClassName(this);
4945 fn.displayName = className + '#' + name;
4950 this.prototype[name] = fn;
4954 * Add / override static properties of this class.
4956 * Ext.define('My.cool.Class', {
4960 * My.cool.Class.addStatics({
4961 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
4962 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
4963 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
4966 * @param {Object} members
4967 * @return {Ext.Base} this
4970 addStatics: function(members) {
4971 for (var name in members) {
4972 if (members.hasOwnProperty(name)) {
4973 this[name] = members[name];
4981 * Add methods / properties to the prototype of this class.
4983 * Ext.define('My.awesome.Cat', {
4984 * constructor: function() {
4989 * My.awesome.Cat.implement({
4990 * meow: function() {
4991 * alert('Meowww...');
4995 * var kitty = new My.awesome.Cat;
4998 * @param {Object} members
5001 implement: function(members) {
5002 var prototype = this.prototype,
5003 name, i, member, previous;
5004 var className = Ext.getClassName(this);
5005 for (name in members) {
5006 if (members.hasOwnProperty(name)) {
5007 member = members[name];
5009 if (typeof member === 'function') {
5010 member.$owner = this;
5011 member.$name = name;
5013 member.displayName = className + '#' + name;
5017 prototype[name] = member;
5021 if (Ext.enumerables) {
5022 var enumerables = Ext.enumerables;
5024 for (i = enumerables.length; i--;) {
5025 name = enumerables[i];
5027 if (members.hasOwnProperty(name)) {
5028 member = members[name];
5029 member.$owner = this;
5030 member.$name = name;
5031 prototype[name] = member;
5038 * Borrow another class' members to the prototype of this class.
5040 * Ext.define('Bank', {
5042 * printMoney: function() {
5047 * Ext.define('Thief', {
5051 * Thief.borrow(Bank, ['money', 'printMoney']);
5053 * var steve = new Thief();
5055 * alert(steve.money); // alerts '$$$'
5056 * steve.printMoney(); // alerts '$$$$$$$'
5058 * @param {Ext.Base} fromClass The class to borrow members from
5059 * @param {Array/String} members The names of the members to borrow
5060 * @return {Ext.Base} this
5064 borrow: function(fromClass, members) {
5065 var fromPrototype = fromClass.prototype,
5068 members = Ext.Array.from(members);
5070 for (i = 0, ln = members.length; i < ln; i++) {
5071 member = members[i];
5073 this.own(member, fromPrototype[member]);
5080 * Override prototype members of this class. Overridden methods can be invoked via
5081 * {@link Ext.Base#callOverridden}
5083 * Ext.define('My.Cat', {
5084 * constructor: function() {
5085 * alert("I'm a cat!");
5092 * constructor: function() {
5093 * alert("I'm going to be a cat!");
5095 * var instance = this.callOverridden();
5097 * alert("Meeeeoooowwww");
5103 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
5104 * // alerts "I'm a cat!"
5105 * // alerts "Meeeeoooowwww"
5107 * @param {Object} members
5108 * @return {Ext.Base} this
5111 override: function(members) {
5112 var prototype = this.prototype,
5113 name, i, member, previous;
5115 for (name in members) {
5116 if (members.hasOwnProperty(name)) {
5117 member = members[name];
5119 if (typeof member === 'function') {
5120 if (typeof prototype[name] === 'function') {
5121 previous = prototype[name];
5122 member.$previous = previous;
5125 this.ownMethod(name, member);
5128 prototype[name] = member;
5133 if (Ext.enumerables) {
5134 var enumerables = Ext.enumerables;
5136 for (i = enumerables.length; i--;) {
5137 name = enumerables[i];
5139 if (members.hasOwnProperty(name)) {
5140 if (prototype[name] !== undefined) {
5141 previous = prototype[name];
5142 members[name].$previous = previous;
5145 this.ownMethod(name, members[name]);
5154 * Used internally by the mixins pre-processor
5157 mixin: flexSetter(function(name, cls) {
5158 var mixin = cls.prototype,
5159 my = this.prototype,
5163 if (mixin.hasOwnProperty(i)) {
5164 if (my[i] === undefined) {
5165 if (typeof mixin[i] === 'function') {
5168 if (fn.$owner === undefined) {
5169 this.ownMethod(i, fn);
5179 else if (i === 'config' && my.config && mixin.config) {
5180 Ext.Object.merge(my.config, mixin.config);
5185 if (my.mixins === undefined) {
5189 my.mixins[name] = mixin;
5193 * Get the current class' name in string format.
5195 * Ext.define('My.cool.Class', {
5196 * constructor: function() {
5197 * alert(this.self.getName()); // alerts 'My.cool.Class'
5201 * My.cool.Class.getName(); // 'My.cool.Class'
5203 * @return {String} className
5205 getName: function() {
5206 return Ext.getClassName(this);
5210 * Create aliases for existing prototype methods. Example:
5212 * Ext.define('My.cool.Class', {
5213 * method1: function() { ... },
5214 * method2: function() { ... }
5217 * var test = new My.cool.Class();
5219 * My.cool.Class.createAlias({
5220 * method3: 'method1',
5221 * method4: 'method2'
5224 * test.method3(); // test.method1()
5226 * My.cool.Class.createAlias('method5', 'method3');
5228 * test.method5(); // test.method3() -> test.method1()
5230 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
5231 * {@link Ext.Function#flexSetter flexSetter}
5232 * @param {String/Object} origin The original method name
5236 createAlias: flexSetter(function(alias, origin) {
5237 this.prototype[alias] = this.prototype[origin];
5241 })(Ext.Function.flexSetter);
5244 * @author Jacky Nguyen <jacky@sencha.com>
5245 * @docauthor Jacky Nguyen <jacky@sencha.com>
5248 * Handles class creation throughout the whole framework. Note that most of the time {@link Ext#define Ext.define} should
5249 * be used instead, since it's a higher level wrapper that aliases to {@link Ext.ClassManager#create}
5250 * to enable namespacing and dynamic dependency resolution.
5254 * Ext.define(className, properties);
5256 * in which `properties` is an object represent a collection of properties that apply to the class. See
5257 * {@link Ext.ClassManager#create} for more detailed instructions.
5259 * Ext.define('Person', {
5262 * constructor: function(name) {
5270 * eat: function(foodType) {
5271 * alert("I'm eating: " + foodType);
5277 * var aaron = new Person("Aaron");
5278 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
5280 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
5281 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
5285 * Ext.define('Developer', {
5288 * constructor: function(name, isGeek) {
5289 * this.isGeek = isGeek;
5291 * // Apply a method from the parent class' prototype
5292 * this.callParent([name]);
5298 * code: function(language) {
5299 * alert("I'm coding in: " + language);
5307 * var jacky = new Developer("Jacky", true);
5308 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
5309 * // alert("I'm eating: Bugs");
5311 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
5315 * Ext.define('CanPlayGuitar', {
5316 * playGuitar: function() {
5317 * alert("F#...G...D...A");
5321 * Ext.define('CanComposeSongs', {
5322 * composeSongs: function() { ... }
5325 * Ext.define('CanSing', {
5326 * sing: function() {
5327 * alert("I'm on the highway to hell...")
5331 * Ext.define('Musician', {
5335 * canPlayGuitar: 'CanPlayGuitar',
5336 * canComposeSongs: 'CanComposeSongs',
5337 * canSing: 'CanSing'
5341 * Ext.define('CoolPerson', {
5345 * canPlayGuitar: 'CanPlayGuitar',
5346 * canSing: 'CanSing'
5349 * sing: function() {
5350 * alert("Ahem....");
5352 * this.mixins.canSing.sing.call(this);
5354 * alert("[Playing guitar at the same time...]");
5356 * this.playGuitar();
5360 * var me = new CoolPerson("Jacky");
5362 * me.sing(); // alert("Ahem...");
5363 * // alert("I'm on the highway to hell...");
5364 * // alert("[Playing guitar at the same time...]");
5365 * // alert("F#...G...D...A");
5369 * Ext.define('SmartPhone', {
5371 * hasTouchScreen: false,
5372 * operatingSystem: 'Other',
5376 * isExpensive: false,
5378 * constructor: function(config) {
5379 * this.initConfig(config);
5384 * applyPrice: function(price) {
5385 * this.isExpensive = (price > 500);
5390 * applyOperatingSystem: function(operatingSystem) {
5391 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
5395 * return operatingSystem;
5399 * var iPhone = new SmartPhone({
5400 * hasTouchScreen: true,
5401 * operatingSystem: 'iOS'
5404 * iPhone.getPrice(); // 500;
5405 * iPhone.getOperatingSystem(); // 'iOS'
5406 * iPhone.getHasTouchScreen(); // true;
5407 * iPhone.hasTouchScreen(); // true
5409 * iPhone.isExpensive; // false;
5410 * iPhone.setPrice(600);
5411 * iPhone.getPrice(); // 600
5412 * iPhone.isExpensive; // true;
5414 * iPhone.setOperatingSystem('AlienOS');
5415 * iPhone.getOperatingSystem(); // 'Other'
5419 * Ext.define('Computer', {
5421 * factory: function(brand) {
5422 * // 'this' in static methods refer to the class itself
5423 * return new this(brand);
5427 * constructor: function() { ... }
5430 * var dellComputer = Computer.factory('Dell');
5432 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
5433 * static properties within class methods
5440 baseStaticProperties = [],
5443 for (baseStaticProperty in Base) {
5444 if (Base.hasOwnProperty(baseStaticProperty)) {
5445 baseStaticProperties.push(baseStaticProperty);
5450 * @method constructor
5451 * Creates new class.
5452 * @param {Object} classData An object represent the properties of this class
5453 * @param {Function} createdFn Optional, the callback function to be executed when this class is fully created.
5454 * Note that the creation process can be asynchronous depending on the pre-processors used.
5455 * @return {Ext.Base} The newly created class
5457 Ext.Class = Class = function(newClass, classData, onClassCreated) {
5458 if (typeof newClass !== 'function') {
5459 onClassCreated = classData;
5460 classData = newClass;
5461 newClass = function() {
5462 return this.constructor.apply(this, arguments);
5470 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
5471 registeredPreprocessors = Class.getPreprocessors(),
5474 preprocessor, preprocessors, staticPropertyName, process, i, j, ln;
5476 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
5477 staticPropertyName = baseStaticProperties[i];
5478 newClass[staticPropertyName] = Base[staticPropertyName];
5481 delete classData.preprocessors;
5483 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
5484 preprocessor = preprocessorStack[j];
5486 if (typeof preprocessor === 'string') {
5487 preprocessor = registeredPreprocessors[preprocessor];
5489 if (!preprocessor.always) {
5490 if (classData.hasOwnProperty(preprocessor.name)) {
5491 preprocessors.push(preprocessor.fn);
5495 preprocessors.push(preprocessor.fn);
5499 preprocessors.push(preprocessor);
5503 classData.onClassCreated = onClassCreated;
5505 classData.onBeforeClassCreated = function(cls, data) {
5506 onClassCreated = data.onClassCreated;
5508 delete data.onBeforeClassCreated;
5509 delete data.onClassCreated;
5511 cls.implement(data);
5513 if (onClassCreated) {
5514 onClassCreated.call(cls, cls);
5518 process = function(cls, data) {
5519 preprocessor = preprocessors[index++];
5521 if (!preprocessor) {
5522 data.onBeforeClassCreated.apply(this, arguments);
5526 if (preprocessor.call(this, cls, data, process) !== false) {
5527 process.apply(this, arguments);
5531 process.call(Class, newClass, classData);
5542 * Register a new pre-processor to be used during the class creation process
5544 * @member Ext.Class registerPreprocessor
5545 * @param {String} name The pre-processor's name
5546 * @param {Function} fn The callback function to be executed. Typical format:
5548 function(cls, data, fn) {
5551 // Execute this when the processing is finished.
5552 // Asynchronous processing is perfectly ok
5554 fn.call(this, cls, data);
5558 * Passed arguments for this function are:
5560 * - `{Function} cls`: The created class
5561 * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
5562 * - `{Function} fn`: The callback function that <b>must</b> to be executed when this pre-processor finishes,
5563 * regardless of whether the processing is synchronous or aynchronous
5565 * @return {Ext.Class} this
5568 registerPreprocessor: function(name, fn, always) {
5569 this.preprocessors[name] = {
5571 always: always || false,
5579 * Retrieve a pre-processor callback function by its name, which has been registered before
5581 * @param {String} name
5582 * @return {Function} preprocessor
5584 getPreprocessor: function(name) {
5585 return this.preprocessors[name];
5588 getPreprocessors: function() {
5589 return this.preprocessors;
5593 * Retrieve the array stack of default pre-processors
5595 * @return {Function} defaultPreprocessors
5597 getDefaultPreprocessors: function() {
5598 return this.defaultPreprocessors || [];
5602 * Set the default array stack of default pre-processors
5604 * @param {Array} preprocessors
5605 * @return {Ext.Class} this
5607 setDefaultPreprocessors: function(preprocessors) {
5608 this.defaultPreprocessors = Ext.Array.from(preprocessors);
5614 * Insert this pre-processor at a specific position in the stack, optionally relative to
5615 * any existing pre-processor. For example:
5617 Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
5621 fn.call(this, cls, data);
5623 }).insertDefaultPreprocessor('debug', 'last');
5625 * @param {String} name The pre-processor name. Note that it needs to be registered with
5626 * {@link Ext#registerPreprocessor registerPreprocessor} before this
5627 * @param {String} offset The insertion position. Four possible values are:
5628 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
5629 * @param {String} relativeName
5630 * @return {Ext.Class} this
5633 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
5634 var defaultPreprocessors = this.defaultPreprocessors,
5637 if (typeof offset === 'string') {
5638 if (offset === 'first') {
5639 defaultPreprocessors.unshift(name);
5643 else if (offset === 'last') {
5644 defaultPreprocessors.push(name);
5649 offset = (offset === 'after') ? 1 : -1;
5652 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
5655 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
5663 * @cfg {String} extend
5664 * The parent class that this class extends. For example:
5666 * Ext.define('Person', {
5667 * say: function(text) { alert(text); }
5670 * Ext.define('Developer', {
5672 * say: function(text) { this.callParent(["print "+text]); }
5675 Class.registerPreprocessor('extend', function(cls, data) {
5676 var extend = data.extend,
5678 basePrototype = base.prototype,
5679 prototype = function() {},
5680 parent, i, k, ln, staticName, parentStatics,
5681 parentPrototype, clsPrototype;
5683 if (extend && extend !== Object) {
5690 parentPrototype = parent.prototype;
5692 prototype.prototype = parentPrototype;
5693 clsPrototype = cls.prototype = new prototype();
5695 if (!('$class' in parent)) {
5696 for (i in basePrototype) {
5697 if (!parentPrototype[i]) {
5698 parentPrototype[i] = basePrototype[i];
5703 clsPrototype.self = cls;
5705 cls.superclass = clsPrototype.superclass = parentPrototype;
5709 // Statics inheritance
5710 parentStatics = parentPrototype.$inheritableStatics;
5712 if (parentStatics) {
5713 for (k = 0, ln = parentStatics.length; k < ln; k++) {
5714 staticName = parentStatics[k];
5716 if (!cls.hasOwnProperty(staticName)) {
5717 cls[staticName] = parent[staticName];
5722 // Merge the parent class' config object without referencing it
5723 if (parentPrototype.config) {
5724 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
5727 clsPrototype.config = {};
5730 if (clsPrototype.$onExtended) {
5731 clsPrototype.$onExtended.call(cls, cls, data);
5734 if (data.onClassExtended) {
5735 clsPrototype.$onExtended = data.onClassExtended;
5736 delete data.onClassExtended;
5742 * @cfg {Object} statics
5743 * List of static methods for this class. For example:
5745 * Ext.define('Computer', {
5747 * factory: function(brand) {
5748 * // 'this' in static methods refer to the class itself
5749 * return new this(brand);
5753 * constructor: function() { ... }
5756 * var dellComputer = Computer.factory('Dell');
5758 Class.registerPreprocessor('statics', function(cls, data) {
5759 var statics = data.statics,
5762 for (name in statics) {
5763 if (statics.hasOwnProperty(name)) {
5764 cls[name] = statics[name];
5768 delete data.statics;
5772 * @cfg {Object} inheritableStatics
5773 * List of inheritable static methods for this class.
5774 * Otherwise just like {@link #statics} but subclasses inherit these methods.
5776 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
5777 var statics = data.inheritableStatics,
5779 prototype = cls.prototype,
5782 inheritableStatics = prototype.$inheritableStatics;
5784 if (!inheritableStatics) {
5785 inheritableStatics = prototype.$inheritableStatics = [];
5788 for (name in statics) {
5789 if (statics.hasOwnProperty(name)) {
5790 cls[name] = statics[name];
5791 inheritableStatics.push(name);
5795 delete data.inheritableStatics;
5799 * @cfg {Object} mixins
5800 * List of classes to mix into this class. For example:
5802 * Ext.define('CanSing', {
5803 * sing: function() {
5804 * alert("I'm on the highway to hell...")
5808 * Ext.define('Musician', {
5812 * canSing: 'CanSing'
5816 Class.registerPreprocessor('mixins', function(cls, data) {
5817 cls.mixin(data.mixins);
5823 * @cfg {Object} config
5824 * List of configuration options with their default values, for which automatically
5825 * accessor methods are generated. For example:
5827 * Ext.define('SmartPhone', {
5829 * hasTouchScreen: false,
5830 * operatingSystem: 'Other',
5833 * constructor: function(cfg) {
5834 * this.initConfig(cfg);
5838 * var iPhone = new SmartPhone({
5839 * hasTouchScreen: true,
5840 * operatingSystem: 'iOS'
5843 * iPhone.getPrice(); // 500;
5844 * iPhone.getOperatingSystem(); // 'iOS'
5845 * iPhone.getHasTouchScreen(); // true;
5846 * iPhone.hasTouchScreen(); // true
5848 Class.registerPreprocessor('config', function(cls, data) {
5849 var prototype = cls.prototype;
5851 Ext.Object.each(data.config, function(name) {
5852 var cName = name.charAt(0).toUpperCase() + name.substr(1),
5854 apply = 'apply' + cName,
5855 setter = 'set' + cName,
5856 getter = 'get' + cName;
5858 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
5859 data[apply] = function(val) {
5864 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
5865 data[setter] = function(val) {
5866 var ret = this[apply].call(this, val, this[pName]);
5868 if (ret !== undefined) {
5876 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
5877 data[getter] = function() {
5883 Ext.Object.merge(prototype.config, data.config);
5887 Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);
5889 // Backwards compatible
5890 Ext.extend = function(subclass, superclass, members) {
5891 if (arguments.length === 2 && Ext.isObject(superclass)) {
5892 members = superclass;
5893 superclass = subclass;
5900 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
5903 members.extend = superclass;
5904 members.preprocessors = ['extend', 'mixins', 'config', 'statics'];
5907 cls = new Class(subclass, members);
5910 cls = new Class(members);
5913 cls.prototype.override = function(o) {
5915 if (o.hasOwnProperty(m)) {
5927 * @author Jacky Nguyen <jacky@sencha.com>
5928 * @docauthor Jacky Nguyen <jacky@sencha.com>
5929 * @class Ext.ClassManager
5931 * Ext.ClassManager manages all classes and handles mapping from string class name to
5932 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
5933 * these convenient shorthands:
5935 * - {@link Ext#define Ext.define}
5936 * - {@link Ext#create Ext.create}
5937 * - {@link Ext#widget Ext.widget}
5938 * - {@link Ext#getClass Ext.getClass}
5939 * - {@link Ext#getClassName Ext.getClassName}
5943 (function(Class, alias) {
5945 var slice = Array.prototype.slice;
5947 var Manager = Ext.ClassManager = {
5950 * @property {Object} classes
5951 * All classes which were defined through the ClassManager. Keys are the
5952 * name of the classes and the values are references to the classes.
5965 namespaceRewrites: [{
5974 alternateToName: {},
5980 enableNamespaceParseCache: true,
5983 namespaceParseCache: {},
5989 instantiationCounts: {},
5992 * Checks if a class has already been created.
5994 * @param {String} className
5995 * @return {Boolean} exist
5997 isCreated: function(className) {
5998 var i, ln, part, root, parts;
6000 if (typeof className !== 'string' || className.length < 1) {
6002 sourceClass: "Ext.ClassManager",
6003 sourceMethod: "exist",
6004 msg: "Invalid classname, must be a string and must not be empty"
6008 if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
6013 parts = this.parseNamespace(className);
6015 for (i = 0, ln = parts.length; i < ln; i++) {
6018 if (typeof part !== 'string') {
6021 if (!root || !root[part]) {
6029 Ext.Loader.historyPush(className);
6031 this.existCache[className] = true;
6037 * Supports namespace rewriting
6040 parseNamespace: function(namespace) {
6041 if (typeof namespace !== 'string') {
6043 sourceClass: "Ext.ClassManager",
6044 sourceMethod: "parseNamespace",
6045 msg: "Invalid namespace, must be a string"
6049 var cache = this.namespaceParseCache;
6051 if (this.enableNamespaceParseCache) {
6052 if (cache.hasOwnProperty(namespace)) {
6053 return cache[namespace];
6058 rewrites = this.namespaceRewrites,
6059 rewrite, from, to, i, ln, root = Ext.global;
6061 for (i = 0, ln = rewrites.length; i < ln; i++) {
6062 rewrite = rewrites[i];
6063 from = rewrite.from;
6066 if (namespace === from || namespace.substring(0, from.length) === from) {
6067 namespace = namespace.substring(from.length);
6069 if (typeof to !== 'string') {
6072 parts = parts.concat(to.split('.'));
6081 parts = parts.concat(namespace.split('.'));
6083 if (this.enableNamespaceParseCache) {
6084 cache[namespace] = parts;
6091 * Creates a namespace and assign the `value` to the created object
6093 * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
6095 * alert(MyCompany.pkg.Example === someObject); // alerts true
6097 * @param {String} name
6098 * @param {Mixed} value
6100 setNamespace: function(name, value) {
6101 var root = Ext.global,
6102 parts = this.parseNamespace(name),
6106 for (i = 0, ln = parts.length; i < ln; i++) {
6109 if (typeof part !== 'string') {
6126 * The new Ext.ns, supports namespace rewriting
6129 createNamespaces: function() {
6130 var root = Ext.global,
6131 parts, part, i, j, ln, subLn;
6133 for (i = 0, ln = arguments.length; i < ln; i++) {
6134 parts = this.parseNamespace(arguments[i]);
6136 for (j = 0, subLn = parts.length; j < subLn; j++) {
6139 if (typeof part !== 'string') {
6155 * Sets a name reference to a class.
6157 * @param {String} name
6158 * @param {Object} value
6159 * @return {Ext.ClassManager} this
6161 set: function(name, value) {
6162 var targetName = this.getName(value);
6164 this.classes[name] = this.setNamespace(name, value);
6166 if (targetName && targetName !== name) {
6167 this.maps.alternateToName[name] = targetName;
6174 * Retrieve a class by its name.
6176 * @param {String} name
6177 * @return {Class} class
6179 get: function(name) {
6180 if (this.classes.hasOwnProperty(name)) {
6181 return this.classes[name];
6184 var root = Ext.global,
6185 parts = this.parseNamespace(name),
6188 for (i = 0, ln = parts.length; i < ln; i++) {
6191 if (typeof part !== 'string') {
6194 if (!root || !root[part]) {
6206 * Register the alias for a class.
6208 * @param {Class/String} cls a reference to a class or a className
6209 * @param {String} alias Alias to use when referring to this class
6211 setAlias: function(cls, alias) {
6212 var aliasToNameMap = this.maps.aliasToName,
6213 nameToAliasesMap = this.maps.nameToAliases,
6216 if (typeof cls === 'string') {
6219 className = this.getName(cls);
6222 if (alias && aliasToNameMap[alias] !== className) {
6223 if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
6224 Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
6225 "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
6228 aliasToNameMap[alias] = className;
6231 if (!nameToAliasesMap[className]) {
6232 nameToAliasesMap[className] = [];
6236 Ext.Array.include(nameToAliasesMap[className], alias);
6243 * Get a reference to the class by its alias.
6245 * @param {String} alias
6246 * @return {Class} class
6248 getByAlias: function(alias) {
6249 return this.get(this.getNameByAlias(alias));
6253 * Get the name of a class by its alias.
6255 * @param {String} alias
6256 * @return {String} className
6258 getNameByAlias: function(alias) {
6259 return this.maps.aliasToName[alias] || '';
6263 * Get the name of a class by its alternate name.
6265 * @param {String} alternate
6266 * @return {String} className
6268 getNameByAlternate: function(alternate) {
6269 return this.maps.alternateToName[alternate] || '';
6273 * Get the aliases of a class by the class name
6275 * @param {String} name
6276 * @return {Array} aliases
6278 getAliasesByName: function(name) {
6279 return this.maps.nameToAliases[name] || [];
6283 * Get the name of the class by its reference or its instance.
6285 * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
6287 * {@link Ext#getClassName Ext.getClassName} is alias for {@link Ext.ClassManager#getName Ext.ClassManager.getName}.
6289 * @param {Class/Object} object
6290 * @return {String} className
6292 getName: function(object) {
6293 return object && object.$className || '';
6297 * Get the class of the provided object; returns null if it's not an instance
6298 * of any class created with Ext.define.
6300 * var component = new Ext.Component();
6302 * Ext.ClassManager.getClass(component); // returns Ext.Component
6304 * {@link Ext#getClass Ext.getClass} is alias for {@link Ext.ClassManager#getClass Ext.ClassManager.getClass}.
6306 * @param {Object} object
6307 * @return {Class} class
6309 getClass: function(object) {
6310 return object && object.self || null;
6316 * Ext.ClassManager.create('My.awesome.Class', {
6317 * someProperty: 'something',
6318 * someMethod: function() { ... }
6322 * alert('Created!');
6323 * alert(this === My.awesome.Class); // alerts true
6325 * var myInstance = new this();
6328 * {@link Ext#define Ext.define} is alias for {@link Ext.ClassManager#create Ext.ClassManager.create}.
6330 * @param {String} className The class name to create in string dot-namespaced format, for example:
6331 * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
6332 * It is highly recommended to follow this simple convention:
6334 * - The root and the class name are 'CamelCased'
6335 * - Everything else is lower-cased
6337 * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
6338 * strings, except those in the reserved list below:
6340 * - {@link Ext.Base#self self}
6341 * - {@link Ext.Class#alias alias}
6342 * - {@link Ext.Class#alternateClassName alternateClassName}
6343 * - {@link Ext.Class#config config}
6344 * - {@link Ext.Class#extend extend}
6345 * - {@link Ext.Class#inheritableStatics inheritableStatics}
6346 * - {@link Ext.Class#mixins mixins}
6347 * - {@link Ext.Class#requires requires}
6348 * - {@link Ext.Class#singleton singleton}
6349 * - {@link Ext.Class#statics statics}
6350 * - {@link Ext.Class#uses uses}
6352 * @param {Function} createdFn Optional callback to execute after the class is created, the execution scope of which
6353 * (`this`) will be the newly created class itself.
6354 * @return {Ext.Base}
6356 create: function(className, data, createdFn) {
6359 if (typeof className !== 'string') {
6362 sourceMethod: "define",
6363 msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
6367 data.$className = className;
6369 return new Class(data, function() {
6370 var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
6371 registeredPostprocessors = manager.postprocessors,
6373 postprocessors = [],
6374 postprocessor, postprocessors, process, i, ln;
6376 delete data.postprocessors;
6378 for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
6379 postprocessor = postprocessorStack[i];
6381 if (typeof postprocessor === 'string') {
6382 postprocessor = registeredPostprocessors[postprocessor];
6384 if (!postprocessor.always) {
6385 if (data[postprocessor.name] !== undefined) {
6386 postprocessors.push(postprocessor.fn);
6390 postprocessors.push(postprocessor.fn);
6394 postprocessors.push(postprocessor);
6398 process = function(clsName, cls, clsData) {
6399 postprocessor = postprocessors[index++];
6401 if (!postprocessor) {
6402 manager.set(className, cls);
6404 Ext.Loader.historyPush(className);
6407 createdFn.call(cls, cls);
6413 if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
6414 process.apply(this, arguments);
6418 process.call(manager, className, this, data);
6423 * Instantiate a class by its alias.
6425 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6426 * attempt to load the class via synchronous loading.
6428 * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
6430 * {@link Ext#createByAlias Ext.createByAlias} is alias for {@link Ext.ClassManager#instantiateByAlias Ext.ClassManager.instantiateByAlias}.
6432 * @param {String} alias
6433 * @param {Mixed} args,... Additional arguments after the alias will be passed to the
6434 * class constructor.
6435 * @return {Object} instance
6437 instantiateByAlias: function() {
6438 var alias = arguments[0],
6439 args = slice.call(arguments),
6440 className = this.getNameByAlias(alias);
6443 className = this.maps.aliasToName[alias];
6448 sourceMethod: "createByAlias",
6449 msg: "Cannot create an instance of unrecognized alias: " + alias
6453 if (Ext.global.console) {
6454 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
6455 "Ext.require('" + alias + "') above Ext.onReady");
6458 Ext.syncRequire(className);
6461 args[0] = className;
6463 return this.instantiate.apply(this, args);
6467 * Instantiate a class by either full name, alias or alternate name.
6469 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
6470 * attempt to load the class via synchronous loading.
6472 * For example, all these three lines return the same result:
6475 * var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
6478 * var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
6480 * // full class name
6481 * var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
6483 * {@link Ext#create Ext.create} is alias for {@link Ext.ClassManager#instantiate Ext.ClassManager.instantiate}.
6485 * @param {String} name
6486 * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
6487 * @return {Object} instance
6489 instantiate: function() {
6490 var name = arguments[0],
6491 args = slice.call(arguments, 1),
6495 if (typeof name !== 'function') {
6496 if ((typeof name !== 'string' || name.length < 1)) {
6499 sourceMethod: "create",
6500 msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
6504 cls = this.get(name);
6510 // No record of this class name, it's possibly an alias, so look it up
6512 possibleName = this.getNameByAlias(name);
6515 name = possibleName;
6517 cls = this.get(name);
6521 // Still no record of this class name, it's possibly an alternate name, so look it up
6523 possibleName = this.getNameByAlternate(name);
6526 name = possibleName;
6528 cls = this.get(name);
6532 // Still not existing at this point, try to load it via synchronous mode as the last resort
6534 if (Ext.global.console) {
6535 Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
6536 "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
6539 Ext.syncRequire(name);
6541 cls = this.get(name);
6547 sourceMethod: "create",
6548 msg: "Cannot create an instance of unrecognized class name / alias: " + alias
6552 if (typeof cls !== 'function') {
6555 sourceMethod: "create",
6556 msg: "'" + name + "' is a singleton and cannot be instantiated"
6560 if (!this.instantiationCounts[name]) {
6561 this.instantiationCounts[name] = 0;
6564 this.instantiationCounts[name]++;
6566 return this.getInstantiator(args.length)(cls, args);
6574 dynInstantiate: function(name, args) {
6575 args = Ext.Array.from(args, true);
6578 return this.instantiate.apply(this, args);
6585 getInstantiator: function(length) {
6586 if (!this.instantiators[length]) {
6590 for (i = 0; i < length; i++) {
6591 args.push('a['+i+']');
6594 this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
6597 return this.instantiators[length];
6608 defaultPostprocessors: [],
6611 * Register a post-processor function.
6613 * @param {String} name
6614 * @param {Function} postprocessor
6616 registerPostprocessor: function(name, fn, always) {
6617 this.postprocessors[name] = {
6619 always: always || false,
6627 * Set the default post processors array stack which are applied to every class.
6629 * @param {String/Array} The name of a registered post processor or an array of registered names.
6630 * @return {Ext.ClassManager} this
6632 setDefaultPostprocessors: function(postprocessors) {
6633 this.defaultPostprocessors = Ext.Array.from(postprocessors);
6639 * Insert this post-processor at a specific position in the stack, optionally relative to
6640 * any existing post-processor
6642 * @param {String} name The post-processor name. Note that it needs to be registered with
6643 * {@link Ext.ClassManager#registerPostprocessor} before this
6644 * @param {String} offset The insertion position. Four possible values are:
6645 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
6646 * @param {String} relativeName
6647 * @return {Ext.ClassManager} this
6649 setDefaultPostprocessorPosition: function(name, offset, relativeName) {
6650 var defaultPostprocessors = this.defaultPostprocessors,
6653 if (typeof offset === 'string') {
6654 if (offset === 'first') {
6655 defaultPostprocessors.unshift(name);
6659 else if (offset === 'last') {
6660 defaultPostprocessors.push(name);
6665 offset = (offset === 'after') ? 1 : -1;
6668 index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
6671 Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
6678 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
6679 * or class names. Expressions support wildcards:
6681 * // returns ['Ext.window.Window']
6682 * var window = Ext.ClassManager.getNamesByExpression('widget.window');
6684 * // returns ['widget.panel', 'widget.window', ...]
6685 * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
6687 * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
6688 * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
6690 * @param {String} expression
6691 * @return {Array} classNames
6694 getNamesByExpression: function(expression) {
6695 var nameToAliasesMap = this.maps.nameToAliases,
6697 name, alias, aliases, possibleName, regex, i, ln;
6699 if (typeof expression !== 'string' || expression.length < 1) {
6701 sourceClass: "Ext.ClassManager",
6702 sourceMethod: "getNamesByExpression",
6703 msg: "Expression " + expression + " is invalid, must be a non-empty string"
6707 if (expression.indexOf('*') !== -1) {
6708 expression = expression.replace(/\*/g, '(.*?)');
6709 regex = new RegExp('^' + expression + '$');
6711 for (name in nameToAliasesMap) {
6712 if (nameToAliasesMap.hasOwnProperty(name)) {
6713 aliases = nameToAliasesMap[name];
6715 if (name.search(regex) !== -1) {
6719 for (i = 0, ln = aliases.length; i < ln; i++) {
6722 if (alias.search(regex) !== -1) {
6732 possibleName = this.getNameByAlias(expression);
6735 names.push(possibleName);
6737 possibleName = this.getNameByAlternate(expression);
6740 names.push(possibleName);
6742 names.push(expression);
6752 * @cfg {[String]} alias
6754 * List of short aliases for class names. Most useful for defining xtypes for widgets:
6756 * Ext.define('MyApp.CoolPanel', {
6757 * extend: 'Ext.panel.Panel',
6758 * alias: ['widget.coolpanel'],
6762 * // Using Ext.create
6763 * Ext.widget('widget.coolpanel');
6764 * // Using the shorthand for widgets and in xtypes
6765 * Ext.widget('panel', {
6767 * {xtype: 'coolpanel', html: 'Foo'},
6768 * {xtype: 'coolpanel', html: 'Bar'}
6772 Manager.registerPostprocessor('alias', function(name, cls, data) {
6773 var aliases = data.alias,
6774 widgetPrefix = 'widget.',
6777 if (!(aliases instanceof Array)) {
6778 aliases = [aliases];
6781 for (i = 0, ln = aliases.length; i < ln; i++) {
6784 if (typeof alias !== 'string') {
6787 sourceMethod: "define",
6788 msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
6792 this.setAlias(cls, alias);
6795 // This is ugly, will change to make use of parseNamespace for alias later on
6796 for (i = 0, ln = aliases.length; i < ln; i++) {
6799 if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
6800 // Only the first alias with 'widget.' prefix will be used for xtype
6801 cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
6808 * @cfg {Boolean} singleton
6810 * When set to true, the class will be instanciated as singleton. For example:
6812 * Ext.define('Logger', {
6814 * log: function(msg) {
6819 * Logger.log('Hello');
6821 Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
6822 fn.call(this, name, new cls(), data);
6827 * @cfg {String/[String]} alternateClassName
6829 * Defines alternate names for this class. For example:
6831 * Ext.define('Developer', {
6832 * alternateClassName: ['Coder', 'Hacker'],
6833 * code: function(msg) {
6834 * alert('Typing... ' + msg);
6838 * var joe = Ext.create('Developer');
6839 * joe.code('stackoverflow');
6841 * var rms = Ext.create('Hacker');
6842 * rms.code('hack hack');
6844 Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
6845 var alternates = data.alternateClassName,
6848 if (!(alternates instanceof Array)) {
6849 alternates = [alternates];
6852 for (i = 0, ln = alternates.length; i < ln; i++) {
6853 alternate = alternates[i];
6855 if (typeof alternate !== 'string') {
6858 sourceMethod: "define",
6859 msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
6863 this.set(alternate, cls);
6867 Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);
6873 * @alias Ext.ClassManager#instantiate
6875 create: alias(Manager, 'instantiate'),
6879 * API to be stablized
6881 * @param {Mixed} item
6882 * @param {String} namespace
6884 factory: function(item, namespace) {
6885 if (item instanceof Array) {
6888 for (i = 0, ln = item.length; i < ln; i++) {
6889 item[i] = Ext.factory(item[i], namespace);
6895 var isString = (typeof item === 'string');
6897 if (isString || (item instanceof Object && item.constructor === Object)) {
6898 var name, config = {};
6904 name = item.className;
6906 delete config.className;
6909 if (namespace !== undefined && name.indexOf(namespace) === -1) {
6910 name = namespace + '.' + Ext.String.capitalize(name);
6913 return Ext.create(name, config);
6916 if (typeof item === 'function') {
6917 return Ext.create(item);
6924 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
6926 * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
6927 * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
6931 * @param {String} name xtype of the widget to create.
6932 * @return {Object} widget instance
6934 widget: function(name) {
6935 var args = slice.call(arguments);
6936 args[0] = 'widget.' + name;
6938 return Manager.instantiateByAlias.apply(Manager, args);
6944 * @alias Ext.ClassManager#instantiateByAlias
6946 createByAlias: alias(Manager, 'instantiateByAlias'),
6951 * @alias Ext.ClassManager#create
6953 define: alias(Manager, 'create'),
6958 * @alias Ext.ClassManager#getName
6960 getClassName: alias(Manager, 'getName'),
6964 * @param {Mixed} object
6966 getDisplayName: function(object) {
6967 if (object.displayName) {
6968 return object.displayName;
6971 if (object.$name && object.$class) {
6972 return Ext.getClassName(object.$class) + '#' + object.$name;
6975 if (object.$className) {
6976 return object.$className;
6985 * @alias Ext.ClassManager#getClass
6987 getClass: alias(Manager, 'getClass'),
6990 * Creates namespaces to be used for scoping variables and classes so that they are not global.
6991 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
6993 * Ext.namespace('Company', 'Company.data');
6995 * // equivalent and preferable to the above syntax
6996 * Ext.namespace('Company.data');
6998 * Company.Widget = function() { ... };
7000 * Company.data.CustomStore = function(config) { ... };
7004 * @param {String} namespace1
7005 * @param {String} namespace2
7006 * @param {String} etc
7007 * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
7009 namespace: alias(Manager, 'createNamespaces')
7013 * Old name for {@link Ext#widget}.
7014 * @deprecated 4.0.0 Use {@link Ext#widget} instead.
7019 Ext.createWidget = Ext.widget;
7022 * Convenient alias for {@link Ext#namespace Ext.namespace}
7025 * @alias Ext#namespace
7027 Ext.ns = Ext.namespace;
7029 Class.registerPreprocessor('className', function(cls, data) {
7030 if (data.$className) {
7031 cls.$className = data.$className;
7032 cls.displayName = cls.$className;
7036 Class.setDefaultPreprocessorPosition('className', 'first');
7038 })(Ext.Class, Ext.Function.alias);
7043 * @author Jacky Nguyen <jacky@sencha.com>
7044 * @docauthor Jacky Nguyen <jacky@sencha.com>
7046 * Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
7047 * via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
7048 * approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons
7051 * # Asynchronous Loading
7055 * + No web server needed: you can run the application via the file system protocol
7056 * (i.e: `file://path/to/your/index.html`)
7057 * + Best possible debugging experience: error messages come with the exact file name and line number
7059 * - *Disadvantages:*
7060 * + Dependencies need to be specified before-hand
7062 * ### Method 1: Explicitly include what you need:
7065 * Ext.require({String/Array} expressions);
7067 * // Example: Single alias
7068 * Ext.require('widget.window');
7070 * // Example: Single class name
7071 * Ext.require('Ext.window.Window');
7073 * // Example: Multiple aliases / class names mix
7074 * Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
7077 * Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
7079 * ### Method 2: Explicitly exclude what you don't need:
7081 * // Syntax: Note that it must be in this chaining format.
7082 * Ext.exclude({String/Array} expressions)
7083 * .require({String/Array} expressions);
7085 * // Include everything except Ext.data.*
7086 * Ext.exclude('Ext.data.*').require('*');Â
7088 * // Include all widgets except widget.checkbox*,
7089 * // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
7090 * Ext.exclude('widget.checkbox*').require('widget.*');
7092 * # Synchronous Loading on Demand
7095 * + There's no need to specify dependencies before-hand, which is always the convenience of including
7098 * - *Disadvantages:*
7099 * + Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
7100 * + Must be from the same domain due to XHR restriction
7101 * + Need a web server, same reason as above
7103 * There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword
7105 * Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});
7107 * Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias
7109 * Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`
7111 * Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
7112 * existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load
7113 * the given class and all its dependencies.
7115 * # Hybrid Loading - The Best of Both Worlds
7117 * It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:
7119 * ### Step 1: Start writing your application using synchronous approach.
7121 * Ext.Loader will automatically fetch all dependencies on demand as they're needed during run-time. For example:
7123 * Ext.onReady(function(){
7124 * var window = Ext.createWidget('window', {
7131 * title: 'Hello Dialog',
7133 * title: 'Navigation',
7134 * collapsible: true,
7140 * title: 'TabPanel',
7148 * ### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these:
7150 * [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code ClassManager.js:432
7151 * [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code
7153 * Simply copy and paste the suggested code above `Ext.onReady`, e.g.:
7155 * Ext.require('Ext.window.Window');
7156 * Ext.require('Ext.layout.container.Border');
7160 * Everything should now load via asynchronous mode.
7164 * It's important to note that dynamic loading should only be used during development on your local machines.
7165 * During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
7166 * the whole process of transitioning from / to between development / maintenance and production as easy as
7167 * possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies
7168 * your application needs in the exact loading sequence. It's as simple as concatenating all files in this
7169 * array into one, then include it on top of your application.
7171 * This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.
7173 (function(Manager, Class, flexSetter, alias) {
7176 dependencyProperties = ['extend', 'mixins', 'requires'],
7179 Loader = Ext.Loader = {
7183 documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
7186 * Flag indicating whether there are still files being loaded
7192 * Maintain the queue for all dependencies. Each item in the array is an object of the format:
7194 * requires: [...], // The required classes for this queue item
7195 * callback: function() { ... } // The function to execute when all classes specified in requires exist
7202 * Maintain the list of files that have already been handled so that they never get double-loaded
7208 * Maintain the list of listeners to execute when all required scripts are fully loaded
7214 * Contains optional dependencies to be loaded last
7217 optionalRequires: [],
7220 * Map of fully qualified class names to an array of dependent classes.
7236 hasFileLoadError: false,
7241 classNameToFilePathMap: {},
7244 * @property {[String]} history
7245 * An array of class names to keep track of the dependency loading order.
7246 * This is not guaranteed to be the same everytime due to the asynchronous nature of the Loader.
7256 * @cfg {Boolean} enabled
7257 * Whether or not to enable the dynamic dependency loading feature Defaults to false
7262 * @cfg {Boolean} disableCaching
7263 * Appends current timestamp to script files to prevent caching Defaults to true
7265 disableCaching: true,
7268 * @cfg {String} disableCachingParam
7269 * The get parameter name for the cache buster's timestamp. Defaults to '_dc'
7271 disableCachingParam: '_dc',
7274 * @cfg {Object} paths
7275 * The mapping from namespaces to file paths
7278 * 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
7279 * // loaded from ./layout/Container.js
7281 * 'My': './src/my_own_folder' // My.layout.Container will be loaded from
7282 * // ./src/my_own_folder/layout/Container.js
7285 * Note that all relative paths are relative to the current HTML document.
7286 * If not being specified, for example, `Other.awesome.Class`
7287 * will simply be loaded from `./Other/awesome/Class.js`
7295 * Set the configuration for the loader. This should be called right after ext-core.js
7296 * (or ext-core-debug.js) is included in the page, e.g.:
7298 * <script type="text/javascript" src="ext-core-debug.js"></script>
7299 * <script type="text/javascript">
7300 * Ext.Loader.setConfig({
7303 * 'My': 'my_own_path'
7307 * <script type="text/javascript">
7310 * Ext.onReady(function() {
7311 * // application code here
7315 * Refer to config options of {@link Ext.Loader} for the list of possible properties.
7317 * @param {String/Object} name Name of the value to override, or a config object to override multiple values.
7318 * @param {Object} value (optional) The new value to set, needed if first parameter is String.
7319 * @return {Ext.Loader} this
7321 setConfig: function(name, value) {
7322 if (Ext.isObject(name) && arguments.length === 1) {
7323 Ext.Object.merge(this.config, name);
7326 this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
7333 * Get the config value corresponding to the specified name.
7334 * If no name is given, will return the config object.
7335 * @param {String} name The config property name
7336 * @return {Object/Mixed}
7338 getConfig: function(name) {
7340 return this.config[name];
7347 * Sets the path of a namespace. For Example:
7349 * Ext.Loader.setPath('Ext', '.');
7351 * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
7352 * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
7353 * @return {Ext.Loader} this
7356 setPath: flexSetter(function(name, path) {
7357 this.config.paths[name] = path;
7363 * Translates a className to a file path by adding the the proper prefix and converting the .'s to /'s.
7366 * Ext.Loader.setPath('My', '/path/to/My');
7368 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
7370 * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:
7372 * Ext.Loader.setPath({
7373 * 'My': '/path/to/lib',
7374 * 'My.awesome': '/other/path/for/awesome/stuff',
7375 * 'My.awesome.more': '/more/awesome/path'
7378 * alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
7380 * alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
7382 * alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
7384 * alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
7386 * @param {String} className
7387 * @return {String} path
7389 getPath: function(className) {
7391 paths = this.config.paths,
7392 prefix = this.getPrefix(className);
7394 if (prefix.length > 0) {
7395 if (prefix === className) {
7396 return paths[prefix];
7399 path = paths[prefix];
7400 className = className.substring(prefix.length + 1);
7403 if (path.length > 0) {
7407 return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
7412 * @param {String} className
7414 getPrefix: function(className) {
7415 var paths = this.config.paths,
7416 prefix, deepestPrefix = '';
7418 if (paths.hasOwnProperty(className)) {
7422 for (prefix in paths) {
7423 if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
7424 if (prefix.length > deepestPrefix.length) {
7425 deepestPrefix = prefix;
7430 return deepestPrefix;
7434 * Refresh all items in the queue. If all dependencies for an item exist during looping,
7435 * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
7439 refreshQueue: function() {
7440 var ln = this.queue.length,
7441 i, item, j, requires;
7444 this.triggerReady();
7448 for (i = 0; i < ln; i++) {
7449 item = this.queue[i];
7452 requires = item.requires;
7454 // Don't bother checking when the number of files loaded
7455 // is still less than the array length
7456 if (requires.length > this.numLoadedFiles) {
7463 if (Manager.isCreated(requires[j])) {
7464 // Take out from the queue
7465 Ext.Array.erase(requires, j, 1);
7470 } while (j < requires.length);
7472 if (item.requires.length === 0) {
7473 Ext.Array.erase(this.queue, i, 1);
7474 item.callback.call(item.scope);
7475 this.refreshQueue();
7485 * Inject a script element to document's head, call onLoad and onError accordingly
7488 injectScriptElement: function(url, onLoad, onError, scope) {
7489 var script = document.createElement('script'),
7491 onLoadFn = function() {
7492 me.cleanupScriptElement(script);
7495 onErrorFn = function() {
7496 me.cleanupScriptElement(script);
7497 onError.call(scope);
7500 script.type = 'text/javascript';
7502 script.onload = onLoadFn;
7503 script.onerror = onErrorFn;
7504 script.onreadystatechange = function() {
7505 if (this.readyState === 'loaded' || this.readyState === 'complete') {
7510 this.documentHead.appendChild(script);
7518 cleanupScriptElement: function(script) {
7519 script.onload = null;
7520 script.onreadystatechange = null;
7521 script.onerror = null;
7527 * Load a script file, supports both asynchronous and synchronous approaches
7529 * @param {String} url
7530 * @param {Function} onLoad
7531 * @param {Scope} scope
7532 * @param {Boolean} synchronous
7535 loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
7537 noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
7538 fileName = url.split('/').pop(),
7539 isCrossOriginRestricted = false,
7540 xhr, status, onScriptError;
7542 scope = scope || this;
7544 this.isLoading = true;
7547 onScriptError = function() {
7548 onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
7551 if (!Ext.isReady && Ext.onDocumentReady) {
7552 Ext.onDocumentReady(function() {
7553 me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7557 this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
7561 if (typeof XMLHttpRequest !== 'undefined') {
7562 xhr = new XMLHttpRequest();
7564 xhr = new ActiveXObject('Microsoft.XMLHTTP');
7568 xhr.open('GET', noCacheUrl, false);
7571 isCrossOriginRestricted = true;
7574 status = (xhr.status === 1223) ? 204 : xhr.status;
7576 if (!isCrossOriginRestricted) {
7577 isCrossOriginRestricted = (status === 0);
7580 if (isCrossOriginRestricted
7582 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
7583 "being loaded from a different domain or from the local file system whereby cross origin " +
7584 "requests are not allowed due to security reasons. Use asynchronous loading with " +
7585 "Ext.require instead.", synchronous);
7587 else if (status >= 200 && status < 300
7589 // Firebug friendly, file names are still shown even though they're eval'ed code
7590 new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();
7595 onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
7596 "verify that the file exists. " +
7597 "XHR status code: " + status, synchronous);
7600 // Prevent potential IE memory leak
7606 * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
7607 * Can be chained with more `require` and `exclude` methods, e.g.:
7609 * Ext.exclude('Ext.data.*').require('*');
7611 * Ext.exclude('widget.button*').require('widget.*');
7613 * {@link Ext#exclude Ext.exclude} is alias for {@link Ext.Loader#exclude Ext.Loader.exclude} for convenience.
7615 * @param {String/[String]} excludes
7616 * @return {Object} object contains `require` method for chaining
7618 exclude: function(excludes) {
7622 require: function(expressions, fn, scope) {
7623 return me.require(expressions, fn, scope, excludes);
7626 syncRequire: function(expressions, fn, scope) {
7627 return me.syncRequire(expressions, fn, scope, excludes);
7633 * Synchronously loads all classes by the given names and all their direct dependencies;
7634 * optionally executes the given callback function when finishes, within the optional scope.
7636 * {@link Ext#syncRequire Ext.syncRequire} is alias for {@link Ext.Loader#syncRequire Ext.Loader.syncRequire} for convenience.
7638 * @param {String/[String]} expressions Can either be a string or an array of string
7639 * @param {Function} fn (Optional) The callback function
7640 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7641 * @param {String/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7643 syncRequire: function() {
7644 this.syncModeEnabled = true;
7645 this.require.apply(this, arguments);
7646 this.refreshQueue();
7647 this.syncModeEnabled = false;
7651 * Loads all classes by the given names and all their direct dependencies;
7652 * optionally executes the given callback function when finishes, within the optional scope.
7654 * {@link Ext#require Ext.require} is alias for {@link Ext.Loader#require Ext.Loader.require} for convenience.
7656 * @param {String/[String]} expressions Can either be a string or an array of string
7657 * @param {Function} fn (Optional) The callback function
7658 * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
7659 * @param {String/[String]} excludes (Optional) Classes to be excluded, useful when being used with expressions
7661 require: function(expressions, fn, scope, excludes) {
7662 var filePath, expression, exclude, className, excluded = {},
7663 excludedClassNames = [],
7664 possibleClassNames = [],
7665 possibleClassName, classNames = [],
7668 expressions = Ext.Array.from(expressions);
7669 excludes = Ext.Array.from(excludes);
7671 fn = fn || Ext.emptyFn;
7673 scope = scope || Ext.global;
7675 for (i = 0, ln = excludes.length; i < ln; i++) {
7676 exclude = excludes[i];
7678 if (typeof exclude === 'string' && exclude.length > 0) {
7679 excludedClassNames = Manager.getNamesByExpression(exclude);
7681 for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
7682 excluded[excludedClassNames[j]] = true;
7687 for (i = 0, ln = expressions.length; i < ln; i++) {
7688 expression = expressions[i];
7690 if (typeof expression === 'string' && expression.length > 0) {
7691 possibleClassNames = Manager.getNamesByExpression(expression);
7693 for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
7694 possibleClassName = possibleClassNames[j];
7696 if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
7697 Ext.Array.include(classNames, possibleClassName);
7703 // If the dynamic dependency feature is not being used, throw an error
7704 // if the dependencies are not defined
7705 if (!this.config.enabled) {
7706 if (classNames.length > 0) {
7708 sourceClass: "Ext.Loader",
7709 sourceMethod: "require",
7710 msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
7711 "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
7716 if (classNames.length === 0) {
7722 requires: classNames,
7727 classNames = classNames.slice();
7729 for (i = 0, ln = classNames.length; i < ln; i++) {
7730 className = classNames[i];
7732 if (!this.isFileLoaded.hasOwnProperty(className)) {
7733 this.isFileLoaded[className] = false;
7735 filePath = this.getPath(className);
7737 this.classNameToFilePathMap[className] = filePath;
7739 this.numPendingFiles++;
7741 this.loadScriptFile(
7743 Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
7744 Ext.Function.pass(this.onFileLoadError, [className, filePath]),
7746 this.syncModeEnabled
7756 * @param {String} className
7757 * @param {String} filePath
7759 onFileLoaded: function(className, filePath) {
7760 this.numLoadedFiles++;
7762 this.isFileLoaded[className] = true;
7764 this.numPendingFiles--;
7766 if (this.numPendingFiles === 0) {
7767 this.refreshQueue();
7770 if (this.numPendingFiles <= 1) {
7771 window.status = "Finished loading all dependencies, onReady fired!";
7774 window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
7777 if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
7778 var queue = this.queue,
7780 i, ln, j, subLn, missingClasses = [], missingPaths = [];
7782 for (i = 0, ln = queue.length; i < ln; i++) {
7783 requires = queue[i].requires;
7785 for (j = 0, subLn = requires.length; j < ln; j++) {
7786 if (this.isFileLoaded[requires[j]]) {
7787 missingClasses.push(requires[j]);
7792 if (missingClasses.length < 1) {
7796 missingClasses = Ext.Array.filter(missingClasses, function(item) {
7797 return !this.requiresMap.hasOwnProperty(item);
7800 for (i = 0,ln = missingClasses.length; i < ln; i++) {
7801 missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
7805 sourceClass: "Ext.Loader",
7806 sourceMethod: "onFileLoaded",
7807 msg: "The following classes are not declared even if their files have been " +
7808 "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
7809 "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
7817 onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
7818 this.numPendingFiles--;
7819 this.hasFileLoadError = true;
7822 sourceClass: "Ext.Loader",
7823 classToLoad: className,
7825 loadingType: isSynchronous ? 'synchronous' : 'async',
7833 addOptionalRequires: function(requires) {
7834 var optionalRequires = this.optionalRequires,
7837 requires = Ext.Array.from(requires);
7839 for (i = 0, ln = requires.length; i < ln; i++) {
7840 require = requires[i];
7842 Ext.Array.include(optionalRequires, require);
7851 triggerReady: function(force) {
7852 var readyListeners = this.readyListeners,
7853 optionalRequires, listener;
7855 if (this.isLoading || force) {
7856 this.isLoading = false;
7858 if (this.optionalRequires.length) {
7859 // Clone then empty the array to eliminate potential recursive loop issue
7860 optionalRequires = Ext.Array.clone(this.optionalRequires);
7862 // Empty the original array
7863 this.optionalRequires.length = 0;
7865 this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
7869 while (readyListeners.length) {
7870 listener = readyListeners.shift();
7871 listener.fn.call(listener.scope);
7873 if (this.isLoading) {
7883 * Adds new listener to be executed when all required scripts are fully loaded.
7885 * @param {Function} fn The function callback to be executed
7886 * @param {Object} scope The execution scope (`this`) of the callback function
7887 * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
7889 onReady: function(fn, scope, withDomReady, options) {
7892 if (withDomReady !== false && Ext.onDocumentReady) {
7896 Ext.onDocumentReady(oldFn, scope, options);
7900 if (!this.isLoading) {
7904 this.readyListeners.push({
7913 * @param {String} className
7915 historyPush: function(className) {
7916 if (className && this.isFileLoaded.hasOwnProperty(className)) {
7917 Ext.Array.include(this.history, className);
7927 * @alias Ext.Loader#require
7929 Ext.require = alias(Loader, 'require');
7933 * @method syncRequire
7934 * @alias Ext.Loader#syncRequire
7936 Ext.syncRequire = alias(Loader, 'syncRequire');
7941 * @alias Ext.Loader#exclude
7943 Ext.exclude = alias(Loader, 'exclude');
7948 * @alias Ext.Loader#onReady
7950 Ext.onReady = function(fn, scope, options) {
7951 Loader.onReady(fn, scope, true, options);
7955 * @cfg {[String]} requires
7957 * List of classes that have to be loaded before instanciating this class.
7960 * Ext.define('Mother', {
7961 * requires: ['Child'],
7962 * giveBirth: function() {
7963 * // we can be sure that child class is available.
7964 * return new Child();
7968 Class.registerPreprocessor('loader', function(cls, data, continueFn) {
7971 className = Manager.getName(cls),
7972 i, j, ln, subLn, value, propertyName, propertyValue;
7975 Basically loop through the dependencyProperties, look for string class names and push
7976 them into a stack, regardless of whether the property's value is a string, array or object. For example:
7978 extend: 'Ext.MyClass',
7979 requires: ['Ext.some.OtherClass'],
7981 observable: 'Ext.util.Observable';
7984 which will later be transformed into:
7986 extend: Ext.MyClass,
7987 requires: [Ext.some.OtherClass],
7989 observable: Ext.util.Observable;
7994 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
7995 propertyName = dependencyProperties[i];
7997 if (data.hasOwnProperty(propertyName)) {
7998 propertyValue = data[propertyName];
8000 if (typeof propertyValue === 'string') {
8001 dependencies.push(propertyValue);
8003 else if (propertyValue instanceof Array) {
8004 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8005 value = propertyValue[j];
8007 if (typeof value === 'string') {
8008 dependencies.push(value);
8013 for (j in propertyValue) {
8014 if (propertyValue.hasOwnProperty(j)) {
8015 value = propertyValue[j];
8017 if (typeof value === 'string') {
8018 dependencies.push(value);
8026 if (dependencies.length === 0) {
8027 // Loader.historyPush(className);
8031 var deadlockPath = [],
8032 requiresMap = Loader.requiresMap,
8036 Automatically detect deadlocks before-hand,
8037 will throw an error with detailed path for ease of debugging. Examples of deadlock cases:
8039 - A extends B, then B extends A
8040 - A requires B, B requires C, then C requires A
8042 The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
8043 no matter how deep the path is.
8047 requiresMap[className] = dependencies;
8049 detectDeadlock = function(cls) {
8050 deadlockPath.push(cls);
8052 if (requiresMap[cls]) {
8053 if (Ext.Array.contains(requiresMap[cls], className)) {
8055 sourceClass: "Ext.Loader",
8056 msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
8057 deadlockPath[1] + "' " + "mutually require each other. Path: " +
8058 deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
8062 for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
8063 detectDeadlock(requiresMap[cls][i]);
8068 detectDeadlock(className);
8072 Loader.require(dependencies, function() {
8073 for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
8074 propertyName = dependencyProperties[i];
8076 if (data.hasOwnProperty(propertyName)) {
8077 propertyValue = data[propertyName];
8079 if (typeof propertyValue === 'string') {
8080 data[propertyName] = Manager.get(propertyValue);
8082 else if (propertyValue instanceof Array) {
8083 for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
8084 value = propertyValue[j];
8086 if (typeof value === 'string') {
8087 data[propertyName][j] = Manager.get(value);
8092 for (var k in propertyValue) {
8093 if (propertyValue.hasOwnProperty(k)) {
8094 value = propertyValue[k];
8096 if (typeof value === 'string') {
8097 data[propertyName][k] = Manager.get(value);
8105 continueFn.call(me, cls, data);
8111 Class.setDefaultPreprocessorPosition('loader', 'after', 'className');
8114 * @cfg {[String]} uses
8116 * List of classes to load together with this class. These aren't neccessarily loaded before
8117 * this class is instanciated. For example:
8119 * Ext.define('Mother', {
8121 * giveBirth: function() {
8122 * // This code might, or might not work:
8123 * // return new Child();
8125 * // Instead use Ext.create() to load the class at the spot if not loaded already:
8126 * return Ext.create('Child');
8130 Manager.registerPostprocessor('uses', function(name, cls, data) {
8131 var uses = Ext.Array.from(data.uses),
8135 for (i = 0, ln = uses.length; i < ln; i++) {
8138 if (typeof item === 'string') {
8143 Loader.addOptionalRequires(items);
8146 Manager.setDefaultPostprocessorPosition('uses', 'last');
8148 })(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);
8155 A wrapper class for the native JavaScript Error object that adds a few useful capabilities for handling
8156 errors in an Ext application. When you use Ext.Error to {@link #raise} an error from within any class that
8157 uses the Ext 4 class system, the Error class can automatically add the source class and method from which
8158 the error was raised. It also includes logic to automatically log the eroor to the console, if available,
8159 with additional metadata about the error. In all cases, the error will always be thrown at the end so that
8160 execution will halt.
8162 Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
8163 handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
8164 although in a real application it's usually a better idea to override the handling function and perform
8165 logging or some other method of reporting the errors in a way that is meaningful to the application.
8167 At its simplest you can simply raise an error as a simple string from within any code:
8171 Ext.Error.raise('Something bad happened!');
8173 If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
8174 displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
8175 additional metadata about the error being raised. The {@link #raise} method can also take a config object.
8176 In this form the `msg` attribute becomes the error description, and any other data added to the config gets
8177 added to the error object and, if the console is available, logged to the console for inspection.
8181 Ext.define('Ext.Foo', {
8182 doSomething: function(option){
8183 if (someCondition === false) {
8185 msg: 'You cannot do that!',
8186 option: option, // whatever was passed into the method
8187 'error code': 100 // other arbitrary info
8193 If a console is available (that supports the `console.dir` function) you'll see console output like:
8195 An error was raised with the following data:
8196 option: Object { foo: "bar"}
8199 msg: "You cannot do that!"
8200 sourceClass: "Ext.Foo"
8201 sourceMethod: "doSomething"
8203 uncaught exception: You cannot do that!
8205 As you can see, the error will report exactly where it was raised and will include as much information as the
8206 raising code can usefully provide.
8208 If you want to handle all application errors globally you can simply override the static {@link #handle} method
8209 and provide whatever handling logic you need. If the method returns true then the error is considered handled
8210 and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
8214 Ext.Error.handle = function(err) {
8215 if (err.someProperty == 'NotReallyAnError') {
8216 // maybe log something to the application here if applicable
8219 // any non-true return value (including none) will cause the error to be thrown
8222 * Create a new Error object
8223 * @param {Object} config The config object
8225 * @author Brian Moeskau <brian@sencha.com>
8226 * @docauthor Brian Moeskau <brian@sencha.com>
8228 Ext.Error = Ext.extend(Error, {
8232 Static flag that can be used to globally disable error reporting to the browser if set to true
8233 (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
8234 and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
8235 be preferable to supply a custom error {@link #handle handling} function instead.
8239 Ext.Error.ignore = true;
8248 Static flag that can be used to globally control error notification to the user. Unlike
8249 Ex.Error.ignore, this does not effect exceptions. They are still thrown. This value can be
8250 set to false to disable the alert notification (default is true for IE6 and IE7).
8252 Only the first error will generate an alert. Internally this flag is set to false when the
8253 first error occurs prior to displaying the alert.
8255 This flag is not used in a release build.
8259 Ext.Error.notify = false;
8264 //notify: Ext.isIE6 || Ext.isIE7,
8267 Raise an error that can include additional data and supports automatic console logging if available.
8268 You can pass a string error message or an object with the `msg` attribute which will be used as the
8269 error message. The object can contain any other name-value attributes (or objects) to be logged
8270 along with the error.
8272 Note that after displaying the error message a JavaScript error will ultimately be thrown so that
8273 execution will halt.
8277 Ext.Error.raise('A simple string error message');
8281 Ext.define('Ext.Foo', {
8282 doSomething: function(option){
8283 if (someCondition === false) {
8285 msg: 'You cannot do that!',
8286 option: option, // whatever was passed into the method
8287 'error code': 100 // other arbitrary info
8292 * @param {String/Object} err The error message string, or an object containing the
8293 * attribute "msg" that will be used as the error message. Any other data included in
8294 * the object will also be logged to the browser console, if available.
8298 raise: function(err){
8300 if (Ext.isString(err)) {
8304 var method = this.raise.caller;
8308 err.sourceMethod = method.$name;
8310 if (method.$owner) {
8311 err.sourceClass = method.$owner.$className;
8315 if (Ext.Error.handle(err) !== true) {
8316 var msg = Ext.Error.prototype.toString.call(err);
8325 throw new Ext.Error(err);
8330 Globally handle any Ext errors that may be raised, optionally providing custom logic to
8331 handle different errors individually. Return true from the function to bypass throwing the
8332 error to the browser, otherwise the error will be thrown and execution will halt.
8336 Ext.Error.handle = function(err) {
8337 if (err.someProperty == 'NotReallyAnError') {
8338 // maybe log something to the application here if applicable
8341 // any non-true return value (including none) will cause the error to be thrown
8344 * @param {Ext.Error} err The Ext.Error object being raised. It will contain any attributes
8345 * that were originally raised with it, plus properties about the method and class from which
8346 * the error originated (if raised from a class that uses the Ext 4 class system).
8351 return Ext.Error.ignore;
8355 // This is the standard property that is the name of the constructor.
8359 * @param {String/Object} config The error message string, or an object containing the
8360 * attribute "msg" that will be used as the error message. Any other data included in
8361 * the object will be applied to the error instance and logged to the browser console, if available.
8363 constructor: function(config){
8364 if (Ext.isString(config)) {
8365 config = { msg: config };
8370 Ext.apply(me, config);
8372 me.message = me.message || me.msg; // 'message' is standard ('msg' is non-standard)
8373 // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
8377 Provides a custom string representation of the error object. This is an override of the base JavaScript
8378 `Object.toString` method, which is useful so that when logged to the browser console, an error object will
8379 be displayed with a useful message instead of `[object Object]`, the default `toString` result.
8381 The default implementation will include the error message along with the raising class and method, if available,
8382 but this can be overridden with a custom implementation either at the prototype level (for all errors) or on
8383 a particular error instance, if you want to provide a custom description that will show up in the console.
8385 * @return {String} The error message. If raised from within the Ext 4 class system, the error message
8386 * will also include the raising class and method names, if available.
8388 toString: function(){
8390 className = me.className ? me.className : '',
8391 methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
8392 msg = me.msg || '(No description provided)';
8394 return className + methodName + msg;
8399 * This mechanism is used to notify the user of the first error encountered on the page. This
8400 * was previously internal to Ext.Error.raise and is a desirable feature since errors often
8401 * slip silently under the radar. It cannot live in Ext.Error.raise since there are times
8402 * where exceptions are handled in a try/catch.
8405 var prevOnError, timer, errors = 0,
8406 extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
8409 if (typeof window === 'undefined') {
8410 return; // build system or some such environment...
8413 // This method is called to notify the user of the current error status.
8414 function notify () {
8415 var counters = Ext.log.counters,
8416 supports = Ext.supports,
8417 hasOnError = supports && supports.WindowOnError; // TODO - timing
8419 // Put log counters to the status bar (for most browsers):
8420 if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
8421 var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
8422 'Info:',counters.info, 'Log:',counters.log].join(' ');
8424 msg = '*** Errors: ' + errors + ' - ' + msg;
8425 } else if (counters.error) {
8431 // Display an alert on the first error:
8432 if (!Ext.isDefined(Ext.Error.notify)) {
8433 Ext.Error.notify = Ext.isIE6 || Ext.isIE7; // TODO - timing
8435 if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
8436 Ext.Error.notify = false;
8439 win.clearInterval(timer); // ticks can queue up so stop...
8443 alert('Unhandled error on page: See console or log');
8448 // Sets up polling loop. This is the only way to know about errors in some browsers
8449 // (Opera/Safari) and is the only way to update the status bar for warnings and other
8452 timer = win.setInterval(notify, 1000);
8455 // window.onerror is ideal (esp in IE) because you get full context. This is harmless
8456 // otherwise (never called) which is good because you cannot feature detect it.
8457 prevOnError = win.onerror || Ext.emptyFn;
8458 win.onerror = function (message) {
8461 if (!extraordinarilyBad.test(message)) {
8462 // too much recursion + our alert right now = crash IE
8463 // our polling loop will pick it up even if we don't alert now
8467 return prevOnError.apply(this, arguments);
8476 This file is part of Ext JS 4
8478 Copyright (c) 2011 Sencha Inc
8480 Contact: http://www.sencha.com/contact
8482 GNU General Public License Usage
8483 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.
8485 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
8490 * Modified version of Douglas Crockford"s json.js that doesn"t
8491 * mess with the Object prototype
8492 * http://www.json.org/js.html
8495 Ext.JSON = new(function() {
8496 var useHasOwn = !! {}.hasOwnProperty,
8497 isNative = function() {
8498 var useNative = null;
8501 if (useNative === null) {
8502 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
8509 return n < 10 ? "0" + n : n;
8511 doDecode = function(json) {
8512 return eval("(" + json + ')');
8514 doEncode = function(o) {
8515 if (!Ext.isDefined(o) || o === null) {
8517 } else if (Ext.isArray(o)) {
8518 return encodeArray(o);
8519 } else if (Ext.isDate(o)) {
8520 return Ext.JSON.encodeDate(o);
8521 } else if (Ext.isString(o)) {
8522 return encodeString(o);
8523 } else if (typeof o == "number") {
8524 //don't use isNumber here, since finite checks happen inside isNumber
8525 return isFinite(o) ? String(o) : "null";
8526 } else if (Ext.isBoolean(o)) {
8528 } else if (Ext.isObject(o)) {
8529 return encodeObject(o);
8530 } else if (typeof o === "function") {
8543 '\x0b': '\\u000b' //ie doesn't handle \v
8545 charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
8546 encodeString = function(s) {
8547 return '"' + s.replace(charToReplace, function(a) {
8549 return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
8552 encodeArray = function(o) {
8554 // Note empty string in case there are no serializable members.
8557 for (i = 0; i < len; i += 1) {
8558 a.push(doEncode(o[i]), ',');
8560 // Overwrite trailing comma (or empty string)
8561 a[a.length - 1] = ']';
8564 encodeObject = function(o) {
8566 // Note empty string in case there are no serializable members.
8569 if (!useHasOwn || o.hasOwnProperty(i)) {
8570 a.push(doEncode(i), ":", doEncode(o[i]), ',');
8573 // Overwrite trailing comma (or empty string)
8574 a[a.length - 1] = '}';
8579 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
8580 * <b>The returned value includes enclosing double quotation marks.</b></p>
8581 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
8582 * <p>To override this:</p><pre><code>
8583 Ext.JSON.encodeDate = function(d) {
8584 return d.format('"Y-m-d"');
8587 * @param {Date} d The Date to encode
8588 * @return {String} The string literal to use in a JSON string.
8590 this.encodeDate = function(o) {
8591 return '"' + o.getFullYear() + "-"
8592 + pad(o.getMonth() + 1) + "-"
8593 + pad(o.getDate()) + "T"
8594 + pad(o.getHours()) + ":"
8595 + pad(o.getMinutes()) + ":"
8596 + pad(o.getSeconds()) + '"';
8600 * Encodes an Object, Array or other value
8601 * @param {Mixed} o The variable to encode
8602 * @return {String} The JSON string
8604 this.encode = function() {
8606 return function(o) {
8608 // setup encoding function on first access
8609 ec = isNative() ? JSON.stringify : doEncode;
8617 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
8618 * @param {String} json The JSON string
8619 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8620 * @return {Object} The resulting object
8622 this.decode = function() {
8624 return function(json, safe) {
8626 // setup decoding function on first access
8627 dc = isNative() ? JSON.parse : doDecode;
8632 if (safe === true) {
8636 sourceClass: "Ext.JSON",
8637 sourceMethod: "decode",
8638 msg: "You're trying to decode an invalid JSON String: " + json
8646 * Shorthand for {@link Ext.JSON#encode}
8647 * @param {Mixed} o The variable to encode
8648 * @return {String} The JSON string
8652 Ext.encode = Ext.JSON.encode;
8654 * Shorthand for {@link Ext.JSON#decode}
8655 * @param {String} json The JSON string
8656 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
8657 * @return {Object} The resulting object
8661 Ext.decode = Ext.JSON.decode;
8667 The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
8668 Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
8669 as direct properties of the Ext namespace.
8671 Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
8672 For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
8674 Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
8675 This ensures all scripts have been loaded, preventing dependency issues. For example
8677 Ext.onReady(function(){
8679 renderTo: document.body,
8684 For more information about how to use the Ext classes, see
8686 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
8687 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
8688 - <a href="http://www.sencha.com/forum/">The forums</a>
8694 userAgent: navigator.userAgent.toLowerCase(),
8697 BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
8698 isStrict: document.compatMode == "CSS1Compat",
8699 windowId: 'ext-window',
8700 documentId: 'ext-document',
8703 * True when the document is fully initialized and ready for action
8709 * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true)
8712 enableGarbageCollector: true,
8715 * True to automatically purge event listeners during garbageCollection (defaults to true).
8718 enableListenerCollection: true,
8721 * Generates unique ids. If the element already has an id, it is unchanged
8722 * @param {Mixed} el (optional) The element to generate an id for
8723 * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
8724 * @return {String} The generated Id.
8726 id: function(el, prefix) {
8729 el = Ext.getDom(el, true) || {};
8730 if (el === document) {
8731 el.id = me.documentId;
8733 else if (el === window) {
8734 el.id = me.windowId;
8737 if (me.isSandboxed) {
8738 if (!me.uniqueGlobalNamespace) {
8739 me.getUniqueGlobalNamespace();
8741 sandboxPrefix = me.uniqueGlobalNamespace + '-';
8743 el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
8749 * Returns the current document body as an {@link Ext.core.Element}.
8750 * @return Ext.core.Element The document body
8752 getBody: function() {
8753 return Ext.get(document.body || false);
8757 * Returns the current document head as an {@link Ext.core.Element}.
8758 * @return Ext.core.Element The document head
8761 getHead: function() {
8765 if (head == undefined) {
8766 head = Ext.get(document.getElementsByTagName("head")[0]);
8774 * Returns the current HTML document object as an {@link Ext.core.Element}.
8775 * @return Ext.core.Element The document
8777 getDoc: function() {
8778 return Ext.get(document);
8782 * This is shorthand reference to {@link Ext.ComponentManager#get}.
8783 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
8784 * @param {String} id The component {@link Ext.Component#id id}
8785 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
8788 getCmp: function(id) {
8789 return Ext.ComponentManager.get(id);
8793 * Returns the current orientation of the mobile device
8794 * @return {String} Either 'portrait' or 'landscape'
8796 getOrientation: function() {
8797 return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
8801 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
8802 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
8803 * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of
8804 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
8805 * passed into this function in a single call as separate arguments.
8806 * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy
8807 * @param {Mixed} arg2 (optional)
8808 * @param {Mixed} etc... (optional)
8810 destroy: function() {
8811 var ln = arguments.length,
8814 for (i = 0; i < ln; i++) {
8817 if (Ext.isArray(arg)) {
8818 this.destroy.apply(this, arg);
8820 else if (Ext.isFunction(arg.destroy)) {
8831 * Execute a callback function in a particular scope. If no function is passed the call is ignored.
8833 * For example, these lines are equivalent:
8835 * Ext.callback(myFunc, this, [arg1, arg2]);
8836 * Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
8838 * @param {Function} callback The callback to execute
8839 * @param {Object} scope (optional) The scope to execute in
8840 * @param {Array} args (optional) The arguments to pass to the function
8841 * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
8843 callback: function(callback, scope, args, delay){
8844 if(Ext.isFunction(callback)){
8846 scope = scope || window;
8848 Ext.defer(callback, delay, scope, args);
8850 callback.apply(scope, args);
8856 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
8857 * @param {String} value The string to encode
8858 * @return {String} The encoded text
8860 htmlEncode : function(value) {
8861 return Ext.String.htmlEncode(value);
8865 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
8866 * @param {String} value The string to decode
8867 * @return {String} The decoded text
8869 htmlDecode : function(value) {
8870 return Ext.String.htmlDecode(value);
8874 * Appends content to the query string of a URL, handling logic for whether to place
8875 * a question mark or ampersand.
8876 * @param {String} url The URL to append to.
8877 * @param {String} s The content to append to the URL.
8878 * @return (String) The resulting URL
8880 urlAppend : function(url, s) {
8881 if (!Ext.isEmpty(s)) {
8882 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
8889 Ext.ns = Ext.namespace;
8892 window.undefined = window.undefined;
8896 * Ext core utilities and functions.
8900 var check = function(regex){
8901 return regex.test(Ext.userAgent);
8903 docMode = document.documentMode,
8904 isOpera = check(/opera/),
8905 isOpera10_5 = isOpera && check(/version\/10\.5/),
8906 isChrome = check(/\bchrome\b/),
8907 isWebKit = check(/webkit/),
8908 isSafari = !isChrome && check(/safari/),
8909 isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
8910 isSafari3 = isSafari && check(/version\/3/),
8911 isSafari4 = isSafari && check(/version\/4/),
8912 isIE = !isOpera && check(/msie/),
8913 isIE7 = isIE && (check(/msie 7/) || docMode == 7),
8914 isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
8915 isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
8916 isIE6 = isIE && check(/msie 6/),
8917 isGecko = !isWebKit && check(/gecko/),
8918 isGecko3 = isGecko && check(/rv:1\.9/),
8919 isGecko4 = isGecko && check(/rv:2\.0/),
8920 isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
8921 isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
8922 isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
8923 isWindows = check(/windows|win32/),
8924 isMac = check(/macintosh|mac os x/),
8925 isLinux = check(/linux/),
8926 scrollbarSize = null,
8927 webKitVersion = isWebKit && (/webkit\/(\d+\.\d+)/.exec(Ext.userAgent));
8929 // remove css image flicker
8931 document.execCommand("BackgroundImageCache", false, true);
8934 Ext.setVersion('extjs', '4.0.2');
8937 * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
8938 * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
8941 SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',
8944 * True if the {@link Ext.fx.Anim} Class is available
8946 * @property enableFx
8950 * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
8951 * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
8952 * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
8955 scopeResetCSS : Ext.buildSettings.scopeResetCSS,
8958 * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
8959 * Currently not optimized for performance.
8962 enableNestedListenerRemoval : false,
8965 * Indicates whether to use native browser parsing for JSON methods.
8966 * This option is ignored if the browser does not support native JSON methods.
8967 * <b>Note: Native JSON methods will not work with objects that have functions.
8968 * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
8971 USE_NATIVE_JSON : false,
8974 * Return the dom node for the passed String (id), dom node, or Ext.core.Element.
8975 * Optional 'strict' flag is needed for IE since it can return 'name' and
8976 * 'id' elements by using getElementById.
8977 * Here are some examples:
8979 // gets dom node based on id
8980 var elDom = Ext.getDom('elId');
8981 // gets dom node based on the dom node
8982 var elDom1 = Ext.getDom(elDom);
8984 // If we don't know if we are working with an
8985 // Ext.core.Element or a dom node use Ext.getDom
8987 var dom = Ext.getDom(el);
8988 // do something with the dom node
8991 * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
8992 * when this method is called to be successful.
8994 * @return HTMLElement
8996 getDom : function(el, strict) {
8997 if (!el || !document) {
9003 if (typeof el == 'string') {
9004 var e = document.getElementById(el);
9005 // IE returns elements with the 'name' and 'id' attribute.
9006 // we do a strict check to return the element with only the id attribute
9007 if (e && isIE && strict) {
9008 if (el == e.getAttribute('id')) {
9022 * Removes a DOM node from the document.
9023 * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
9024 * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
9025 * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
9026 * will be ignored if passed in.</p>
9027 * @param {HTMLElement} node The node to remove
9030 removeNode : isIE6 || isIE7 ? function() {
9033 if(n && n.tagName != 'BODY'){
9034 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9035 d = d || document.createElement('div');
9038 delete Ext.cache[n.id];
9042 if (n && n.parentNode && n.tagName != 'BODY') {
9043 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
9044 n.parentNode.removeChild(n);
9045 delete Ext.cache[n.id];
9050 * True if the detected browser is Opera.
9056 * True if the detected browser is Opera 10.5x.
9059 isOpera10_5 : isOpera10_5,
9062 * True if the detected browser uses WebKit.
9065 isWebKit : isWebKit,
9068 * True if the detected browser is Chrome.
9071 isChrome : isChrome,
9074 * True if the detected browser is Safari.
9077 isSafari : isSafari,
9080 * True if the detected browser is Safari 3.x.
9083 isSafari3 : isSafari3,
9086 * True if the detected browser is Safari 4.x.
9089 isSafari4 : isSafari4,
9092 * True if the detected browser is Safari 2.x.
9095 isSafari2 : isSafari2,
9098 * True if the detected browser is Internet Explorer.
9104 * True if the detected browser is Internet Explorer 6.x.
9110 * True if the detected browser is Internet Explorer 7.x.
9116 * True if the detected browser is Internet Explorer 8.x.
9122 * True if the detected browser is Internet Explorer 9.x.
9128 * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
9134 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
9137 isGecko3 : isGecko3,
9140 * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
9143 isGecko4 : isGecko4,
9146 * True if the detected browser uses FireFox 3.0
9152 * True if the detected browser uses FireFox 3.5
9158 * True if the detected browser uses FireFox 3.6
9164 * True if the detected platform is Linux.
9170 * True if the detected platform is Windows.
9173 isWindows : isWindows,
9176 * True if the detected platform is Mac OS.
9182 * The current version of WebKit (-1 if the browser does not use WebKit).
9185 webKitVersion: webKitVersion ? parseFloat(webKitVersion[1]) : -1,
9188 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
9189 * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
9190 * For other browsers it uses an inline data URL.
9193 BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
9196 * <p>Utility method for returning a default value if the passed value is empty.</p>
9197 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
9199 * <li>undefined</li>
9200 * <li>an empty array</li>
9201 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
9203 * @param {Mixed} value The value to test
9204 * @param {Mixed} defaultValue The value to return if the original value is empty
9205 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
9206 * @return {Mixed} value, if non-empty, else defaultValue
9207 * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
9209 value : function(v, defaultValue, allowBlank){
9210 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
9214 * Escapes the passed string for use in a regular expression
9215 * @param {String} str
9217 * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
9219 escapeRe : function(s) {
9220 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
9224 * Applies event listeners to elements by selectors when the document is ready.
9225 * The event name is specified with an <tt>@</tt> suffix.
9228 // add a listener for click on all anchors in element with id foo
9229 '#foo a@click' : function(e, t){
9233 // add the same listener to multiple selectors (separated by comma BEFORE the @)
9234 '#foo a, #bar span.some-class@mouseover' : function(){
9239 * @param {Object} obj The list of behaviors to apply
9241 addBehaviors : function(o){
9243 Ext.onReady(function(){
9244 Ext.addBehaviors(o);
9247 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
9252 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
9255 cache[s] = Ext.select(s);
9257 cache[s].on(parts[1], o[b]);
9265 * Returns the size of the browser scrollbars. This can differ depending on
9266 * operating system settings, such as the theme or font size.
9267 * @param {Boolean} force (optional) true to force a recalculation of the value.
9268 * @return {Object} An object containing the width of a vertical scrollbar and the
9269 * height of a horizontal scrollbar.
9271 getScrollbarSize: function (force) {
9276 if(force === true || scrollbarSize === null){
9278 // When IE9 positions an element offscreen via offsets, the offsetWidth is
9279 // inaccurately reported. For IE9 only, we render on screen before removing.
9280 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
9281 // Append our div, do our calculation and then remove it
9282 div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
9283 child = div.child('div', true),
9284 w1 = child.offsetWidth;
9286 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
9288 var w2 = child.offsetWidth, width = w1 - w2;
9291 // We assume width == height for now. TODO: is this always true?
9292 scrollbarSize = { width: width, height: width };
9295 return scrollbarSize;
9299 * Utility method for getting the width of the browser's vertical scrollbar. This
9300 * can differ depending on operating system settings, such as the theme or font size.
9302 * This method is deprected in favor of {@link #getScrollbarSize}.
9304 * @param {Boolean} force (optional) true to force a recalculation of the value.
9305 * @return {Number} The width of a vertical scrollbar.
9308 getScrollBarWidth: function(force){
9309 var size = Ext.getScrollbarSize(force);
9310 return size.width + 2; // legacy fudge factor
9314 * Copies a set of named properties fom the source object to the destination object.
9318 * ImageComponent = Ext.extend(Ext.Component, {
9319 * initComponent: function() {
9320 * this.autoEl = { tag: 'img' };
9321 * MyComponent.superclass.initComponent.apply(this, arguments);
9322 * this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
9326 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
9328 * @param {Object} dest The destination object.
9329 * @param {Object} source The source object.
9330 * @param {Array/String} names Either an Array of property names, or a comma-delimited list
9331 * of property names to copy.
9332 * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
9333 * @return {Object} The modified object.
9335 copyTo : function(dest, source, names, usePrototypeKeys){
9336 if(typeof names == 'string'){
9337 names = names.split(/[,;\s]/);
9339 Ext.each(names, function(name){
9340 if(usePrototypeKeys || source.hasOwnProperty(name)){
9341 dest[name] = source[name];
9348 * Attempts to destroy and then remove a set of named properties of the passed object.
9349 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
9350 * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
9351 * @param {Mixed} etc... More property names to destroy and remove.
9353 destroyMembers : function(o){
9354 for (var i = 1, a = arguments, len = a.length; i < len; i++) {
9355 Ext.destroy(o[a[i]]);
9361 * Logs a message. If a console is present it will be used. On Opera, the method
9362 * "opera.postError" is called. In other cases, the message is logged to an array
9363 * "Ext.log.out". An attached debugger can watch this array and view the log. The
9364 * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 100).
9366 * If additional parameters are passed, they are joined and appended to the message.
9368 * This method does nothing in a release build.
9370 * @param {String|Object} message The message to log or an options object with any
9371 * of the following properties:
9373 * - `msg`: The message to log (required).
9374 * - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
9375 * - `dump`: An object to dump to the log as part of the message.
9376 * - `stack`: True to include a stack trace in the log.
9379 log : function (message) {
9381 con = Ext.global.console,
9388 if (!Ext.isString(message)) {
9390 message = options.msg || '';
9391 level = options.level || level;
9392 dump = options.dump;
9393 stack = options.stack;
9395 if (dump && !(con && con.dir)) {
9398 // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
9399 // ...and the data could be prettier!
9400 Ext.Object.each(dump, function (name, value) {
9401 if (typeof(value) === "function") {
9405 if (!Ext.isDefined(value) || value === null ||
9406 Ext.isDate(value) ||
9407 Ext.isString(value) || (typeof(value) == "number") ||
9408 Ext.isBoolean(value)) {
9409 member = Ext.encode(value);
9410 } else if (Ext.isArray(value)) {
9412 } else if (Ext.isObject(value)) {
9415 member = 'undefined';
9417 members.push(Ext.encode(name) + ': ' + member);
9420 if (members.length) {
9421 message += ' \nData: {\n ' + members.join(',\n ') + '\n}';
9427 if (arguments.length > 1) {
9428 message += Array.prototype.slice.call(arguments, 1).join('');
9431 // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
9432 // an early test may fail either direction if Firebug is toggled.
9434 if (con) { // if (Firebug-like console)
9436 con[level](message);
9445 if (stack && con.trace) {
9446 // Firebug's console.error() includes a trace already...
9447 if (!con.firebug || level != 'error') {
9452 // w/o console, all messages are equal, so munge the level into the message:
9453 if (level != 'log') {
9454 message = level.toUpperCase() + ': ' + message;
9458 opera.postError(message);
9460 var out = log.out || (log.out = []),
9461 max = log.max || (log.max = 100);
9463 if (out.length >= max) {
9464 // this formula allows out.max to change (via debugger), where the
9465 // more obvious "max/4" would not quite be the same
9466 Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
9473 // Mostly informational, but the Ext.Error notifier uses them:
9474 var counters = log.counters ||
9475 (log.counters = { error: 0, warn: 0, info: 0, log: 0 });
9481 * Partitions the set into two sets: a true set and a false set.
9486 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
9492 return val.className == "class1"
9495 // true are those paragraph elements with a className of "class1",
9496 // false set are those that do not have that className.
9498 * @param {Array|NodeList} arr The array to partition
9499 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
9500 * itself must be able to be evaluated for its truthfulness.
9501 * @return {Array} [array of truish values, array of falsy values]
9502 * @deprecated 4.0.0 Will be removed in the next major version
9504 partition : function(arr, truth){
9506 Ext.each(arr, function(v, i, a) {
9507 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
9513 * Invokes a method on each item in an Array.
9516 Ext.invoke(Ext.query("p"), "getAttribute", "id");
9517 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
9519 * @param {Array|NodeList} arr The Array of items to invoke the method on.
9520 * @param {String} methodName The method name to invoke.
9521 * @param {...*} args Arguments to send into the method invocation.
9522 * @return {Array} The results of invoking the method on each item in the array.
9523 * @deprecated 4.0.0 Will be removed in the next major version
9525 invoke : function(arr, methodName){
9527 args = Array.prototype.slice.call(arguments, 2);
9528 Ext.each(arr, function(v,i) {
9529 if (v && typeof v[methodName] == 'function') {
9530 ret.push(v[methodName].apply(v, args));
9532 ret.push(undefined);
9539 * <p>Zips N sets together.</p>
9542 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
9549 return "$" + a + "" + b + "." + c
9551 ); // ["$+12.43", "$-10.15", "$+22.96"]
9553 * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
9554 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
9555 * @return {Array} The zipped set.
9556 * @deprecated 4.0.0 Will be removed in the next major version
9559 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
9562 len = Ext.max(Ext.pluck(arrs, "length")),
9565 for (var i = 0; i < len; i++) {
9568 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
9570 for (var j = 0, aLen = arrs.length; j < aLen; j++){
9571 ret[i].push( arrs[j][i] );
9579 * Turns an array into a sentence, joined by a specified connector - e.g.:
9580 * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
9581 * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
9582 * @param {Array} items The array to create a sentence from
9583 * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
9584 * @return {String} The sentence string
9585 * @deprecated 4.0.0 Will be removed in the next major version
9587 toSentence: function(items, connector) {
9588 var length = items.length;
9593 var head = items.slice(0, length - 1),
9594 tail = items[length - 1];
9596 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
9601 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
9602 * you may want to set this to true.
9610 * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
9612 * See Ext.app.Application for details.
9614 * @param {Object} config
9616 Ext.application = function(config) {
9617 Ext.require('Ext.app.Application');
9619 Ext.onReady(function() {
9620 Ext.create('Ext.app.Application', config);
9625 * @class Ext.util.Format
9627 This class is a centralized place for formatting functions inside the library. It includes
9628 functions to format various different types of data, such as text, dates and numeric values.
9631 This class contains several options for localization. These can be set once the library has loaded,
9632 all calls to the functions from that point will use the locale settings that were specified.
9639 This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.
9641 __Using with renderers__
9642 There are two helper functions that return a new function that can be used in conjunction with
9647 renderer: Ext.util.Format.dateRenderer('Y-m-d')
9650 renderer: Ext.util.Format.numberRenderer('0.000')
9653 Functions that only take a single argument can also be passed directly:
9656 renderer: Ext.util.Format.usMoney
9658 dataIndex: 'productCode',
9659 renderer: Ext.util.Format.uppercase
9662 __Using with XTemplates__
9663 XTemplates can also directly use Ext.util.Format functions:
9666 'Date: {startDate:date("Y-m-d")}',
9667 'Cost: {cost:usMoney}'
9676 Ext.util.Format = {};
9677 var UtilFormat = Ext.util.Format,
9678 stripTagsRE = /<\/?[^>]+>/gi,
9679 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
9682 // A RegExp to remove from a number format string, all characters except digits and '.'
9683 formatCleanRe = /[^\d\.]/g,
9685 // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
9686 // Created on first use. The local decimal separator character must be initialized for this to be created.
9689 Ext.apply(UtilFormat, {
9692 * @property thousandSeparator
9693 * <p>The character that the {@link #number} function uses as a thousand separator.</p>
9694 * <p>This defaults to <code>,</code>, but may be overridden in a locale file.</p>
9696 thousandSeparator: ',',
9700 * @property decimalSeparator
9701 * <p>The character that the {@link #number} function uses as a decimal point.</p>
9702 * <p>This defaults to <code>.</code>, but may be overridden in a locale file.</p>
9704 decimalSeparator: '.',
9708 * @property currencyPrecision
9709 * <p>The number of decimal places that the {@link #currency} function displays.</p>
9710 * <p>This defaults to <code>2</code>, but may be overridden in a locale file.</p>
9712 currencyPrecision: 2,
9716 * @property currencySign
9717 * <p>The currency sign that the {@link #currency} function displays.</p>
9718 * <p>This defaults to <code>$</code>, but may be overridden in a locale file.</p>
9724 * @property currencyAtEnd
9725 * <p>This may be set to <code>true</code> to make the {@link #currency} function
9726 * append the currency sign to the formatted value.</p>
9727 * <p>This defaults to <code>false</code>, but may be overridden in a locale file.</p>
9729 currencyAtEnd: false,
9732 * Checks a reference and converts it to empty string if it is undefined
9733 * @param {Mixed} value Reference to check
9734 * @return {Mixed} Empty string if converted, otherwise the original value
9736 undef : function(value) {
9737 return value !== undefined ? value : "";
9741 * Checks a reference and converts it to the default value if it's empty
9742 * @param {Mixed} value Reference to check
9743 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
9746 defaultValue : function(value, defaultValue) {
9747 return value !== undefined && value !== '' ? value : defaultValue;
9751 * Returns a substring from within an original string
9752 * @param {String} value The original text
9753 * @param {Number} start The start index of the substring
9754 * @param {Number} length The length of the substring
9755 * @return {String} The substring
9757 substr : function(value, start, length) {
9758 return String(value).substr(start, length);
9762 * Converts a string to all lower case letters
9763 * @param {String} value The text to convert
9764 * @return {String} The converted text
9766 lowercase : function(value) {
9767 return String(value).toLowerCase();
9771 * Converts a string to all upper case letters
9772 * @param {String} value The text to convert
9773 * @return {String} The converted text
9775 uppercase : function(value) {
9776 return String(value).toUpperCase();
9780 * Format a number as US currency
9781 * @param {Number/String} value The numeric value to format
9782 * @return {String} The formatted currency string
9784 usMoney : function(v) {
9785 return UtilFormat.currency(v, '$', 2);
9789 * Format a number as a currency
9790 * @param {Number/String} value The numeric value to format
9791 * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
9792 * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
9793 * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
9794 * @return {String} The formatted currency string
9796 currency: function(v, currencySign, decimals, end) {
9797 var negativeSign = '',
9805 decimals = decimals || UtilFormat.currencyPrecision;
9806 format += format + (decimals > 0 ? '.' : '');
9807 for (; i < decimals; i++) {
9810 v = UtilFormat.number(v, format);
9811 if ((end || UtilFormat.currencyAtEnd) === true) {
9812 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
9814 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
9819 * Formats the passed date using the specified format pattern.
9820 * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
9821 * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
9822 * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9823 * @return {String} The formatted date string.
9825 date: function(v, format) {
9829 if (!Ext.isDate(v)) {
9830 v = new Date(Date.parse(v));
9832 return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
9836 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
9837 * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
9838 * @return {Function} The date formatting function
9840 dateRenderer : function(format) {
9841 return function(v) {
9842 return UtilFormat.date(v, format);
9847 * Strips all HTML tags
9848 * @param {Mixed} value The text from which to strip tags
9849 * @return {String} The stripped text
9851 stripTags : function(v) {
9852 return !v ? v : String(v).replace(stripTagsRE, "");
9856 * Strips all script tags
9857 * @param {Mixed} value The text from which to strip script tags
9858 * @return {String} The stripped text
9860 stripScripts : function(v) {
9861 return !v ? v : String(v).replace(stripScriptsRe, "");
9865 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
9866 * @param {Number/String} size The numeric value to format
9867 * @return {String} The formatted file size
9869 fileSize : function(size) {
9871 return size + " bytes";
9872 } else if (size < 1048576) {
9873 return (Math.round(((size*10) / 1024))/10) + " KB";
9875 return (Math.round(((size*10) / 1048576))/10) + " MB";
9880 * It does simple math for use in a template, for example:<pre><code>
9881 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
9883 * @return {Function} A function that operates on the passed value.
9889 return function(v, a){
9891 fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
9898 * Rounds the passed number to the required decimal precision.
9899 * @param {Number/String} value The numeric value to round.
9900 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
9901 * @return {Number} The rounded value.
9903 round : function(value, precision) {
9904 var result = Number(value);
9905 if (typeof precision == 'number') {
9906 precision = Math.pow(10, precision);
9907 result = Math.round(value * precision) / precision;
9913 * <p>Formats the passed number according to the passed format string.</p>
9914 * <p>The number of digits after the decimal separator character specifies the number of
9915 * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
9916 * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
9917 * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
9918 * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
9919 * <p><b>New to Ext4</b></p>
9920 * <p>Locale-specific characters are always used in the formatted output when inserting
9921 * thousand and decimal separators.</p>
9922 * <p>The format string must specify separator characters according to US/UK conventions ("," as the
9923 * thousand separator, and "." as the decimal separator)</p>
9924 * <p>To allow specification of format strings according to local conventions for separator characters, add
9925 * the string <code>/i</code> to the end of the format string.</p>
9926 * <div style="margin-left:40px">examples (123456.789):
9927 * <div style="margin-left:10px">
9928 * 0 - (123456) show only digits, no precision<br>
9929 * 0.00 - (123456.78) show only digits, 2 precision<br>
9930 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
9931 * 0,000 - (123,456) show comma and digits, no precision<br>
9932 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
9933 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
9934 * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
9935 * For example: 0.000,00/i
9937 * @param {Number} v The number to format.
9938 * @param {String} format The way you would like to format this text.
9939 * @return {String} The formatted number.
9941 number: function(v, formatString) {
9942 if (!formatString) {
9945 v = Ext.Number.from(v, NaN);
9949 var comma = UtilFormat.thousandSeparator,
9950 dec = UtilFormat.decimalSeparator,
9958 // The "/i" suffix allows caller to use a locale-specific formatting string.
9959 // Clean the format string by removing all but numerals and the decimal separator.
9960 // Then split the format string into pre and post decimal segments according to *what* the
9961 // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
9962 if (formatString.substr(formatString.length - 2) == '/i') {
9963 if (!I18NFormatCleanRe) {
9964 I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
9966 formatString = formatString.substr(0, formatString.length - 2);
9968 hasComma = formatString.indexOf(comma) != -1;
9969 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
9971 hasComma = formatString.indexOf(',') != -1;
9972 psplit = formatString.replace(formatCleanRe, '').split('.');
9975 if (1 < psplit.length) {
9976 v = v.toFixed(psplit[1].length);
9977 } else if(2 < psplit.length) {
9979 sourceClass: "Ext.util.Format",
9980 sourceMethod: "number",
9982 formatString: formatString,
9983 msg: "Invalid number format, should have no more than 1 decimal"
9989 var fnum = v.toString();
9991 psplit = fnum.split('.');
9994 var cnum = psplit[0],
9997 m = Math.floor(j / 3),
9998 n = cnum.length % 3 || 3,
10001 for (i = 0; i < j; i += n) {
10006 parr[parr.length] = cnum.substr(i, n);
10009 fnum = parr.join(comma);
10011 fnum += dec + psplit[1];
10015 fnum = psplit[0] + dec + psplit[1];
10021 * Edge case. If we have a very small negative number it will get rounded to 0,
10022 * however the initial check at the top will still report as negative. Replace
10023 * everything but 1-9 and check if the string is empty to determine a 0 value.
10025 neg = fnum.replace(/[^1-9]/g, '') !== '';
10028 return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
10032 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
10033 * @param {String} format Any valid number format string for {@link #number}
10034 * @return {Function} The number formatting function
10036 numberRenderer : function(format) {
10037 return function(v) {
10038 return UtilFormat.number(v, format);
10043 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
10044 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
10045 * if the value is 0 or greater than 1.
10046 * @param {Number} value The value to compare against
10047 * @param {String} singular The singular form of the word
10048 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
10050 plural : function(v, s, p) {
10051 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
10055 * Converts newline characters to the HTML tag <br/>
10056 * @param {String} The string value to format.
10057 * @return {String} The string with embedded <br/> tags in place of newlines.
10059 nl2br : function(v) {
10060 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
10064 * Capitalize the given string. See {@link Ext.String#capitalize}.
10067 capitalize: Ext.String.capitalize,
10070 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
10071 * See {@link Ext.String#ellipsis}.
10074 ellipsis: Ext.String.ellipsis,
10077 * Formats to a string. See {@link Ext.String#format}
10080 format: Ext.String.format,
10083 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
10084 * See {@link Ext.String#htmlDecode}.
10087 htmlDecode: Ext.String.htmlDecode,
10090 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
10091 * See {@link Ext.String#htmlEncode}.
10094 htmlEncode: Ext.String.htmlEncode,
10097 * Adds left padding to a string. See {@link Ext.String#leftPad}
10100 leftPad: Ext.String.leftPad,
10103 * Trims any whitespace from either side of a string. See {@link Ext.String#trim}.
10106 trim : Ext.String.trim,
10109 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
10110 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
10111 * @param {Number|String} v The encoded margins
10112 * @return {Object} An object with margin sizes for top, right, bottom and left
10114 parseBox : function(box) {
10115 if (Ext.isNumber(box)) {
10116 box = box.toString();
10118 var parts = box.split(' '),
10122 parts[1] = parts[2] = parts[3] = parts[0];
10124 else if (ln == 2) {
10125 parts[2] = parts[0];
10126 parts[3] = parts[1];
10128 else if (ln == 3) {
10129 parts[3] = parts[1];
10133 top :parseInt(parts[0], 10) || 0,
10134 right :parseInt(parts[1], 10) || 0,
10135 bottom:parseInt(parts[2], 10) || 0,
10136 left :parseInt(parts[3], 10) || 0
10141 * Escapes the passed string for use in a regular expression
10142 * @param {String} str
10145 escapeRegex : function(s) {
10146 return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
10152 * @class Ext.util.TaskRunner
10153 * Provides the ability to execute one or more arbitrary tasks in a multithreaded
10154 * manner. Generally, you can use the singleton {@link Ext.TaskManager} instead, but
10155 * if needed, you can create separate instances of TaskRunner. Any number of
10156 * separate tasks can be started at any time and will run independently of each
10157 * other. Example usage:
10159 // Start a simple clock task that updates a div once per second
10160 var updateClock = function(){
10161 Ext.fly('clock').update(new Date().format('g:i:s A'));
10165 interval: 1000 //1 second
10167 var runner = new Ext.util.TaskRunner();
10168 runner.start(task);
10170 // equivalent using TaskManager
10171 Ext.TaskManager.start({
10177 * <p>See the {@link #start} method for details about how to configure a task object.</p>
10178 * Also see {@link Ext.util.DelayedTask}.
10181 * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance
10184 Ext.ns('Ext.util');
10186 Ext.util.TaskRunner = function(interval) {
10187 interval = interval || 10;
10194 stopThread = function() {
10201 startThread = function() {
10204 id = setInterval(runTasks, interval);
10209 removeTask = function(t) {
10210 removeQueue.push(t);
10212 t.onStop.apply(t.scope || t);
10217 runTasks = function() {
10218 var rqLen = removeQueue.length,
10219 now = new Date().getTime(),
10223 for (i = 0; i < rqLen; i++) {
10224 Ext.Array.remove(tasks, removeQueue[i]);
10227 if (tasks.length < 1) {
10236 len = tasks.length;
10237 for (; i < len; ++i) {
10239 itime = now - t.taskRunTime;
10240 if (t.interval <= itime) {
10241 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
10242 t.taskRunTime = now;
10243 if (rt === false || t.taskRunCount === t.repeat) {
10248 if (t.duration && t.duration <= (now - t.taskStartTime)) {
10255 * Starts a new task.
10257 * @param {Object} task <p>A config object that supports the following properties:<ul>
10258 * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
10259 * function will be called at each interval and passed the <code>args</code> argument if specified, and the
10260 * current invocation count if not.</p>
10261 * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
10262 * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
10263 * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
10264 * should be invoked.</div></li>
10265 * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
10266 * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
10267 * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
10268 * <code>run</code> function. Defaults to the task config object.</div></li>
10269 * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
10270 * the task before stopping automatically (defaults to indefinite).</div></li>
10271 * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
10272 * stopping automatically (defaults to indefinite).</div></li>
10274 * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
10275 * that calculations based on the repeat count can be performed.</p>
10276 * @return {Object} The task
10278 this.start = function(task) {
10280 task.taskStartTime = new Date().getTime();
10281 task.taskRunTime = 0;
10282 task.taskRunCount = 0;
10288 * Stops an existing running task.
10290 * @param {Object} task The task to stop
10291 * @return {Object} The task
10293 this.stop = function(task) {
10299 * Stops all tasks that are currently running.
10302 this.stopAll = function() {
10304 for (var i = 0, len = tasks.length; i < len; i++) {
10305 if (tasks[i].onStop) {
10315 * @class Ext.TaskManager
10316 * @extends Ext.util.TaskRunner
10317 * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks. See
10318 * {@link Ext.util.TaskRunner} for supported methods and task config properties.
10320 // Start a simple clock task that updates a div once per second
10323 Ext.fly('clock').update(new Date().format('g:i:s A'));
10325 interval: 1000 //1 second
10327 Ext.TaskManager.start(task);
10329 * <p>See the {@link #start} method for details about how to configure a task object.</p>
10332 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
10336 * Determines information about the current platform the application is running on.
10341 init : function(navigator) {
10342 var platforms = this.platforms,
10343 ln = platforms.length,
10346 navigator = navigator || window.navigator;
10348 for (i = 0; i < ln; i++) {
10349 platform = platforms[i];
10350 this[platform.identity] = platform.regex.test(navigator[platform.property]);
10354 * @property Desktop True if the browser is running on a desktop machine
10357 this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
10359 * @property Tablet True if the browser is running on a tablet (iPad)
10361 this.Tablet = this.iPad;
10363 * @property Phone True if the browser is running on a phone.
10366 this.Phone = !this.Desktop && !this.Tablet;
10368 * @property iOS True if the browser is running on iOS
10371 this.iOS = this.iPhone || this.iPad || this.iPod;
10374 * @property Standalone Detects when application has been saved to homescreen.
10377 this.Standalone = !!window.navigator.standalone;
10381 * @property iPhone True when the browser is running on a iPhone
10385 property: 'platform',
10391 * @property iPod True when the browser is running on a iPod
10395 property: 'platform',
10401 * @property iPad True when the browser is running on a iPad
10405 property: 'userAgent',
10411 * @property Blackberry True when the browser is running on a Blackberry
10415 property: 'userAgent',
10416 regex: /Blackberry/i,
10417 identity: 'Blackberry'
10421 * @property Android True when the browser is running on an Android device
10425 property: 'userAgent',
10427 identity: 'Android'
10431 * @property Mac True when the browser is running on a Mac
10435 property: 'platform',
10441 * @property Windows True when the browser is running on Windows
10445 property: 'platform',
10447 identity: 'Windows'
10451 * @property Linux True when the browser is running on Linux
10455 property: 'platform',
10464 * @class Ext.supports
10466 * Determines information about features are supported in the current environment
10471 init : function() {
10472 var doc = document,
10473 div = doc.createElement('div'),
10474 tests = this.tests,
10479 '<div style="height:30px;width:50px;">',
10480 '<div style="height:20px;width:20px;"></div>',
10482 '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
10483 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
10485 '<div style="float:left; background-color:transparent;"></div>'
10488 doc.body.appendChild(div);
10490 for (i = 0; i < ln; i++) {
10492 this[test.identity] = test.fn.call(this, doc, div);
10495 doc.body.removeChild(div);
10499 * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
10502 CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
10505 * @property ClassList True if document environment supports the HTML5 classList API.
10508 ClassList: !!document.documentElement.classList,
10511 * @property OrientationChange True if the device supports orientation change
10514 OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
10517 * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
10520 DeviceMotion: ('ondevicemotion' in window),
10523 * @property Touch True if the device supports touch
10526 // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
10527 // and Safari 4.0 (they all have 'ontouchstart' in the window object).
10528 Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
10532 * @property Transitions True if the device supports CSS3 Transitions
10536 identity: 'Transitions',
10537 fn: function(doc, div) {
10545 TE = 'TransitionEnd',
10546 transitionEndName = [
10548 'transitionend', //Moz bucks the prefixing convention
10553 ln = prefix.length,
10556 div = Ext.get(div);
10557 for (; i < ln; i++) {
10558 if (div.getStyle(prefix[i] + "TransitionProperty")) {
10559 Ext.supports.CSS3Prefix = prefix[i];
10560 Ext.supports.CSS3TransitionEnd = transitionEndName[i];
10570 * @property RightMargin True if the device supports right margin.
10571 * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
10575 identity: 'RightMargin',
10576 fn: function(doc, div) {
10577 var view = doc.defaultView;
10578 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
10583 * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
10584 * selection when their display style is changed. Essentially, if a text input
10585 * has focus and its display style is changed, the I-beam disappears.
10587 * This bug is encountered due to the work around in place for the {@link #RightMargin}
10588 * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
10589 * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
10590 * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
10593 identity: 'DisplayChangeInputSelectionBug',
10595 var webKitVersion = Ext.webKitVersion;
10596 // WebKit but older than Safari 5 or Chrome 6:
10597 return 0 < webKitVersion && webKitVersion < 533;
10602 * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
10603 * selection when their display style is changed. Essentially, if a text area has
10604 * focus and its display style is changed, the I-beam disappears.
10606 * This bug is encountered due to the work around in place for the {@link #RightMargin}
10607 * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
10608 * be fixed in Chrome 11.
10611 identity: 'DisplayChangeTextAreaSelectionBug',
10613 var webKitVersion = Ext.webKitVersion;
10616 Has bug w/textarea:
10618 (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
10619 AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
10621 (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
10622 AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
10627 (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
10628 AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
10631 return 0 < webKitVersion && webKitVersion < 534.24;
10636 * @property TransparentColor True if the device supports transparent color
10640 identity: 'TransparentColor',
10641 fn: function(doc, div, view) {
10642 view = doc.defaultView;
10643 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
10648 * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
10652 identity: 'ComputedStyle',
10653 fn: function(doc, div, view) {
10654 view = doc.defaultView;
10655 return view && view.getComputedStyle;
10660 * @property SVG True if the device supports SVG
10665 fn: function(doc) {
10666 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
10671 * @property Canvas True if the device supports Canvas
10675 identity: 'Canvas',
10676 fn: function(doc) {
10677 return !!doc.createElement('canvas').getContext;
10682 * @property VML True if the device supports VML
10687 fn: function(doc) {
10688 var d = doc.createElement("div");
10689 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
10690 return (d.childNodes.length == 2);
10695 * @property Float True if the device supports CSS float
10700 fn: function(doc, div) {
10701 return !!div.lastChild.style.cssFloat;
10706 * @property AudioTag True if the device supports the HTML5 audio tag
10710 identity: 'AudioTag',
10711 fn: function(doc) {
10712 return !!doc.createElement('audio').canPlayType;
10717 * @property History True if the device supports HTML5 history
10721 identity: 'History',
10723 return !!(window.history && history.pushState);
10728 * @property CSS3DTransform True if the device supports CSS3DTransform
10732 identity: 'CSS3DTransform',
10734 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
10739 * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
10743 identity: 'CSS3LinearGradient',
10744 fn: function(doc, div) {
10745 var property = 'background-image:',
10746 webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
10747 w3c = 'linear-gradient(left top, black, white)',
10748 moz = '-moz-' + w3c,
10749 options = [property + webkit, property + w3c, property + moz];
10751 div.style.cssText = options.join(';');
10753 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
10758 * @property CSS3BorderRadius True if the device supports CSS3 border radius
10762 identity: 'CSS3BorderRadius',
10763 fn: function(doc, div) {
10764 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
10767 for (i = 0; i < domPrefixes.length; i++) {
10768 if (document.body.style[domPrefixes[i]] !== undefined) {
10777 * @property GeoLocation True if the device supports GeoLocation
10781 identity: 'GeoLocation',
10783 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
10787 * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
10791 identity: 'MouseEnterLeave',
10792 fn: function(doc, div){
10793 return ('onmouseenter' in div && 'onmouseleave' in div);
10797 * @property MouseWheel True if the browser supports the mousewheel event
10801 identity: 'MouseWheel',
10802 fn: function(doc, div) {
10803 return ('onmousewheel' in div);
10807 * @property Opacity True if the browser supports normal css opacity
10811 identity: 'Opacity',
10812 fn: function(doc, div){
10813 // Not a strict equal comparison in case opacity can be converted to a number.
10814 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
10817 div.firstChild.style.cssText = 'opacity:0.73';
10818 return div.firstChild.style.opacity == '0.73';
10822 * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
10826 identity: 'Placeholder',
10827 fn: function(doc) {
10828 return 'placeholder' in doc.createElement('input');
10833 * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
10834 * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
10838 identity: 'Direct2DBug',
10840 return Ext.isString(document.body.style.msTransformOrigin);
10844 * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
10848 identity: 'BoundingClientRect',
10849 fn: function(doc, div) {
10850 return Ext.isFunction(div.getBoundingClientRect);
10854 identity: 'IncludePaddingInWidthCalculation',
10855 fn: function(doc, div){
10856 var el = Ext.get(div.childNodes[1].firstChild);
10857 return el.getWidth() == 210;
10861 identity: 'IncludePaddingInHeightCalculation',
10862 fn: function(doc, div){
10863 var el = Ext.get(div.childNodes[1].firstChild);
10864 return el.getHeight() == 210;
10869 * @property ArraySort True if the Array sort native method isn't bugged.
10873 identity: 'ArraySort',
10875 var a = [1,2,3,4,5].sort(function(){ return 0; });
10876 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
10880 * @property Range True if browser support document.createRange native method.
10886 return !!document.createRange;
10890 * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
10894 identity: 'CreateContextualFragment',
10896 var range = Ext.supports.Range ? document.createRange() : false;
10898 return range && !!range.createContextualFragment;
10903 * @property WindowOnError True if browser supports window.onerror.
10907 identity: 'WindowOnError',
10909 // sadly, we cannot feature detect this...
10910 return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+
10920 This file is part of Ext JS 4
10922 Copyright (c) 2011 Sencha Inc
10924 Contact: http://www.sencha.com/contact
10926 GNU General Public License Usage
10927 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.
10929 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
10933 * @class Ext.core.DomHelper
10934 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10935 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
10936 * from your DOM building code.</p>
10938 * <p><b><u>DomHelper element specification object</u></b></p>
10939 * <p>A specification object is used when creating elements. Attributes of this object
10940 * are assumed to be element attributes, except for 4 special attributes:
10941 * <div class="mdetail-params"><ul>
10942 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
10943 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
10944 * same kind of element definition objects to be created and appended. These can be nested
10945 * as deep as you want.</div></li>
10946 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
10947 * This will end up being either the "class" attribute on a HTML fragment or className
10948 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
10949 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
10951 * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
10952 * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
10953 * contains special characters that would not normally be allowed in a double-quoted attribute value,
10954 * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
10955 * malformed HTML being created. This behavior may change in a future release.</p>
10957 * <p><b><u>Insertion methods</u></b></p>
10958 * <p>Commonly used insertion methods:
10959 * <div class="mdetail-params"><ul>
10960 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
10961 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
10962 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
10963 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
10964 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
10965 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
10968 * <p><b><u>Example</u></b></p>
10969 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
10970 * element with id <tt>'my-div'</tt>:<br>
10972 var dh = Ext.core.DomHelper; // create shorthand alias
10973 // specification object
10978 // append children after creating
10979 children: [ // may also specify 'cn' instead of 'children'
10980 {tag: 'li', id: 'item0', html: 'List Item 0'},
10981 {tag: 'li', id: 'item1', html: 'List Item 1'},
10982 {tag: 'li', id: 'item2', html: 'List Item 2'}
10985 var list = dh.append(
10986 'my-div', // the context element 'my-div' can either be the id or the actual node
10987 spec // the specification object
10990 * <p>Element creation specification parameters in this class may also be passed as an Array of
10991 * specification objects. This can be used to insert multiple sibling nodes into an existing
10992 * container very efficiently. For example, to add more list items to the example above:<pre><code>
10993 dh.append('my-ul', [
10994 {tag: 'li', id: 'item3', html: 'List Item 3'},
10995 {tag: 'li', id: 'item4', html: 'List Item 4'}
10997 * </code></pre></p>
10999 * <p><b><u>Templating</u></b></p>
11000 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
11001 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
11002 * insert new elements. Revisiting the example above, we could utilize templating this time:
11005 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
11007 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
11009 for(var i = 0; i < 5, i++){
11010 tpl.append(list, [i]); // use template to append to the actual node
11012 * </code></pre></p>
11013 * <p>An example using a template:<pre><code>
11014 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
11016 var tpl = new Ext.core.DomHelper.createTemplate(html);
11017 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
11018 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
11019 * </code></pre></p>
11021 * <p>The same example using named parameters:<pre><code>
11022 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11024 var tpl = new Ext.core.DomHelper.createTemplate(html);
11025 tpl.append('blog-roll', {
11027 url: 'http://www.edspencer.net/',
11028 text: "Ed's Site"
11030 tpl.append('blog-roll', {
11032 url: 'http://www.dustindiaz.com/',
11033 text: "Dustin's Site"
11035 * </code></pre></p>
11037 * <p><b><u>Compiling Templates</u></b></p>
11038 * <p>Templates are applied using regular expressions. The performance is great, but if
11039 * you are adding a bunch of DOM elements using the same template, you can increase
11040 * performance even further by {@link Ext.Template#compile "compiling"} the template.
11041 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
11042 * broken up at the different variable points and a dynamic function is created and eval'ed.
11043 * The generated function performs string concatenation of these parts and the passed
11044 * variables instead of using regular expressions.
11046 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
11048 var tpl = new Ext.core.DomHelper.createTemplate(html);
11051 //... use template like normal
11052 * </code></pre></p>
11054 * <p><b><u>Performance Boost</u></b></p>
11055 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
11056 * of DOM can significantly boost performance.</p>
11057 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
11058 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
11059 * results in the creation of a text node. Usage:</p>
11061 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
11065 Ext.ns('Ext.core');
11066 Ext.core.DomHelper = function(){
11067 var tempTableEl = null,
11068 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
11069 tableRe = /^table|tbody|tr|td$/i,
11070 confRe = /tag|children|cn|html$/i,
11071 tableElRe = /td|tr|tbody/i,
11074 // kill repeat to save bytes
11075 afterbegin = 'afterbegin',
11076 afterend = 'afterend',
11077 beforebegin = 'beforebegin',
11078 beforeend = 'beforeend',
11081 tbs = ts+'<tbody>',
11082 tbe = '</tbody>'+te,
11083 trs = tbs + '<tr>',
11087 function doInsert(el, o, returnElement, pos, sibling, append){
11088 el = Ext.getDom(el);
11091 newNode = createDom(o, null);
11093 el.appendChild(newNode);
11095 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
11098 newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
11100 return returnElement ? Ext.get(newNode, true) : newNode;
11103 function createDom(o, parentNode){
11111 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
11112 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
11113 for (var i = 0, l = o.length; i < l; i++) {
11114 createDom(o[i], el);
11116 } else if (typeof o == 'string') { // Allow a string as a child spec.
11117 el = doc.createTextNode(o);
11119 el = doc.createElement( o.tag || 'div' );
11120 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
11122 if(!confRe.test(attr)){
11125 el.className = val;
11128 el.setAttribute(attr, val);
11135 Ext.core.DomHelper.applyStyles(el, o.style);
11137 if ((cn = o.children || o.cn)) {
11139 } else if (o.html) {
11140 el.innerHTML = o.html;
11144 parentNode.appendChild(el);
11149 // build as innerHTML where available
11150 function createHtml(o){
11158 if(typeof o == "string"){
11160 } else if (Ext.isArray(o)) {
11161 for (i=0; i < o.length; i++) {
11163 b += createHtml(o[i]);
11167 b += '<' + (o.tag = o.tag || 'div');
11170 if(!confRe.test(attr)){
11171 if (typeof val == "object") {
11172 b += ' ' + attr + '="';
11174 b += key + ':' + val[key] + ';';
11178 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
11182 // Now either just close the tag or try to add children and close the tag.
11183 if (emptyTags.test(o.tag)) {
11187 if ((cn = o.children || o.cn)) {
11188 b += createHtml(cn);
11192 b += '</' + o.tag + '>';
11198 function ieTable(depth, s, h, e){
11199 tempTableEl.innerHTML = [s, h, e].join('');
11203 while(++i < depth){
11204 el = el.firstChild;
11206 // If the result is multiple siblings, then encapsulate them into one fragment.
11207 ns = el.nextSibling;
11209 var df = document.createDocumentFragment();
11211 ns = el.nextSibling;
11212 df.appendChild(el);
11222 * Nasty code for IE's broken table implementation
11224 function insertIntoTable(tag, where, el, html) {
11228 tempTableEl = tempTableEl || document.createElement('div');
11230 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
11231 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
11234 before = where == beforebegin ? el :
11235 where == afterend ? el.nextSibling :
11236 where == afterbegin ? el.firstChild : null;
11238 if (where == beforebegin || where == afterend) {
11239 el = el.parentNode;
11242 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
11243 node = ieTable(4, trs, html, tre);
11244 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
11245 (tag == 'tr' && (where == beforebegin || where == afterend))) {
11246 node = ieTable(3, tbs, html, tbe);
11248 node = ieTable(2, ts, html, te);
11250 el.insertBefore(node, before);
11256 * Fix for IE9 createContextualFragment missing method
11258 function createContextualFragment(html){
11259 var div = document.createElement("div"),
11260 fragment = document.createDocumentFragment(),
11262 length, childNodes;
11264 div.innerHTML = html;
11265 childNodes = div.childNodes;
11266 length = childNodes.length;
11268 for (; i < length; i++) {
11269 fragment.appendChild(childNodes[i].cloneNode(true));
11277 * Returns the markup for the passed Element(s) config.
11278 * @param {Object} o The DOM object spec (and children)
11281 markup : function(o){
11282 return createHtml(o);
11286 * Applies a style specification to an element.
11287 * @param {String/HTMLElement} el The element to apply styles to
11288 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
11289 * a function which returns such a specification.
11291 applyStyles : function(el, styles){
11294 if (typeof styles == "function") {
11295 styles = styles.call();
11297 if (typeof styles == "string") {
11298 styles = Ext.core.Element.parseStyles(styles);
11300 if (typeof styles == "object") {
11301 el.setStyle(styles);
11307 * Inserts an HTML fragment into the DOM.
11308 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
11309 * @param {HTMLElement/TextNode} el The context element
11310 * @param {String} html The HTML fragment
11311 * @return {HTMLElement} The new node
11313 insertHtml : function(where, el, html){
11322 where = where.toLowerCase();
11323 // add these here because they are used in both branches of the condition.
11324 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
11325 hash[afterend] = ['AfterEnd', 'nextSibling'];
11327 // if IE and context element is an HTMLElement
11328 if (el.insertAdjacentHTML) {
11329 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
11333 // add these two to the hash.
11334 hash[afterbegin] = ['AfterBegin', 'firstChild'];
11335 hash[beforeend] = ['BeforeEnd', 'lastChild'];
11336 if ((hashVal = hash[where])) {
11337 el.insertAdjacentHTML(hashVal[0], html);
11338 return el[hashVal[1]];
11340 // if (not IE and context element is an HTMLElement) or TextNode
11342 // we cannot insert anything inside a textnode so...
11343 if (Ext.isTextNode(el)) {
11344 where = where === 'afterbegin' ? 'beforebegin' : where;
11345 where = where === 'beforeend' ? 'afterend' : where;
11347 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
11348 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
11351 range[setStart](el);
11352 frag = range.createContextualFragment(html);
11354 frag = createContextualFragment(html);
11356 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
11357 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
11359 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
11360 if (el.firstChild) {
11362 range[setStart](el[rangeEl]);
11363 frag = range.createContextualFragment(html);
11365 frag = createContextualFragment(html);
11368 if(where == afterbegin){
11369 el.insertBefore(frag, el.firstChild);
11371 el.appendChild(frag);
11374 el.innerHTML = html;
11376 return el[rangeEl];
11380 sourceClass: 'Ext.core.DomHelper',
11381 sourceMethod: 'insertHtml',
11382 htmlToInsert: html,
11384 msg: 'Illegal insertion point reached: "' + where + '"'
11389 * Creates new DOM element(s) and inserts them before el.
11390 * @param {Mixed} el The context element
11391 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11392 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11393 * @return {HTMLElement/Ext.core.Element} The new node
11395 insertBefore : function(el, o, returnElement){
11396 return doInsert(el, o, returnElement, beforebegin);
11400 * Creates new DOM element(s) and inserts them after el.
11401 * @param {Mixed} el The context element
11402 * @param {Object} o The DOM object spec (and children)
11403 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11404 * @return {HTMLElement/Ext.core.Element} The new node
11406 insertAfter : function(el, o, returnElement){
11407 return doInsert(el, o, returnElement, afterend, 'nextSibling');
11411 * Creates new DOM element(s) and inserts them as the first child of el.
11412 * @param {Mixed} el The context element
11413 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11414 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11415 * @return {HTMLElement/Ext.core.Element} The new node
11417 insertFirst : function(el, o, returnElement){
11418 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
11422 * Creates new DOM element(s) and appends them to el.
11423 * @param {Mixed} el The context element
11424 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11425 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11426 * @return {HTMLElement/Ext.core.Element} The new node
11428 append : function(el, o, returnElement){
11429 return doInsert(el, o, returnElement, beforeend, '', true);
11433 * Creates new DOM element(s) and overwrites the contents of el with them.
11434 * @param {Mixed} el The context element
11435 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11436 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
11437 * @return {HTMLElement/Ext.core.Element} The new node
11439 overwrite : function(el, o, returnElement){
11440 el = Ext.getDom(el);
11441 el.innerHTML = createHtml(o);
11442 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
11445 createHtml : createHtml,
11448 * Creates new DOM element(s) without inserting them to the document.
11449 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
11450 * @return {HTMLElement} The new uninserted node
11453 createDom: createDom,
11455 /** True to force the use of DOM instead of html fragments @type Boolean */
11459 * Creates a new Ext.Template from the DOM object spec.
11460 * @param {Object} o The DOM object spec (and children)
11461 * @return {Ext.Template} The new template
11463 createTemplate : function(o){
11464 var html = Ext.core.DomHelper.createHtml(o);
11465 return Ext.create('Ext.Template', html);
11472 * This is code is also distributed under MIT license for use
11473 * with jQuery and prototype JavaScript libraries.
11476 * @class Ext.DomQuery
11477 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
11479 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
11482 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
11484 <h4>Element Selectors:</h4>
11486 <li> <b>*</b> any element</li>
11487 <li> <b>E</b> an element with the tag E</li>
11488 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
11489 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
11490 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
11491 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
11493 <h4>Attribute Selectors:</h4>
11494 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
11496 <li> <b>E[foo]</b> has an attribute "foo"</li>
11497 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
11498 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
11499 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
11500 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
11501 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
11502 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
11504 <h4>Pseudo Classes:</h4>
11506 <li> <b>E:first-child</b> E is the first child of its parent</li>
11507 <li> <b>E:last-child</b> E is the last child of its parent</li>
11508 <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
11509 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
11510 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
11511 <li> <b>E:only-child</b> E is the only child of its parent</li>
11512 <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
11513 <li> <b>E:first</b> the first E in the resultset</li>
11514 <li> <b>E:last</b> the last E in the resultset</li>
11515 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
11516 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
11517 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
11518 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
11519 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
11520 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
11521 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
11522 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
11523 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
11524 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
11526 <h4>CSS Value Selectors:</h4>
11528 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
11529 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
11530 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
11531 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
11532 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
11533 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
11537 Ext.ns('Ext.core');
11539 Ext.core.DomQuery = Ext.DomQuery = function(){
11544 trimRe = /^\s+|\s+$/g,
11545 tplRe = /\{(\d+)\}/g,
11546 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
11547 tagTokenRe = /^(#)?([\w-\*]+)/,
11548 nthRe = /(\d*)n\+?(\d*)/,
11550 // This is for IE MSXML which does not support expandos.
11551 // IE runs the same speed using setAttribute, however FF slows way down
11552 // and Safari completely fails so they need to continue to use expandos.
11553 isIE = window.ActiveXObject ? true : false,
11556 // this eval is stop the compressor from
11557 // renaming the variable to something shorter
11558 eval("var batch = 30803;");
11560 // Retrieve the child node from a particular
11561 // parent at the specified index.
11562 function child(parent, index){
11564 n = parent.firstChild;
11566 if(n.nodeType == 1){
11576 // retrieve the next element node
11578 while((n = n.nextSibling) && n.nodeType != 1);
11582 // retrieve the previous element node
11584 while((n = n.previousSibling) && n.nodeType != 1);
11588 // Mark each child node with a nodeIndex skipping and
11589 // removing empty text nodes.
11590 function children(parent){
11591 var n = parent.firstChild,
11595 nextNode = n.nextSibling;
11596 // clean worthless empty nodes.
11597 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
11598 parent.removeChild(n);
11600 // add an expando nodeIndex
11601 n.nodeIndex = ++nodeIndex;
11609 // nodeSet - array of nodes
11611 function byClassName(nodeSet, cls){
11615 var result = [], ri = -1;
11616 for(var i = 0, ci; ci = nodeSet[i]; i++){
11617 if((' '+ci.className+' ').indexOf(cls) != -1){
11624 function attrValue(n, attr){
11625 // if its an array, use the first node.
11626 if(!n.tagName && typeof n.length != "undefined"){
11636 if(attr == "class" || attr == "className"){
11637 return n.className;
11639 return n.getAttribute(attr) || n[attr];
11645 // mode - false, /, >, +, ~
11646 // tagName - defaults to "*"
11647 function getNodes(ns, mode, tagName){
11648 var result = [], ri = -1, cs;
11652 tagName = tagName || "*";
11653 // convert to array
11654 if(typeof ns.getElementsByTagName != "undefined"){
11658 // no mode specified, grab all elements by tagName
11661 for(var i = 0, ni; ni = ns[i]; i++){
11662 cs = ni.getElementsByTagName(tagName);
11663 for(var j = 0, ci; ci = cs[j]; j++){
11667 // Direct Child mode (/ or >)
11668 // E > F or E/F all direct children elements of E that have the tag
11669 } else if(mode == "/" || mode == ">"){
11670 var utag = tagName.toUpperCase();
11671 for(var i = 0, ni, cn; ni = ns[i]; i++){
11672 cn = ni.childNodes;
11673 for(var j = 0, cj; cj = cn[j]; j++){
11674 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
11679 // Immediately Preceding mode (+)
11680 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
11681 }else if(mode == "+"){
11682 var utag = tagName.toUpperCase();
11683 for(var i = 0, n; n = ns[i]; i++){
11684 while((n = n.nextSibling) && n.nodeType != 1);
11685 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
11689 // Sibling mode (~)
11690 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
11691 }else if(mode == "~"){
11692 var utag = tagName.toUpperCase();
11693 for(var i = 0, n; n = ns[i]; i++){
11694 while((n = n.nextSibling)){
11695 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
11704 function concat(a, b){
11706 return a.concat(b);
11708 for(var i = 0, l = b.length; i < l; i++){
11709 a[a.length] = b[i];
11714 function byTag(cs, tagName){
11715 if(cs.tagName || cs == document){
11721 var result = [], ri = -1;
11722 tagName = tagName.toLowerCase();
11723 for(var i = 0, ci; ci = cs[i]; i++){
11724 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
11731 function byId(cs, id){
11732 if(cs.tagName || cs == document){
11738 var result = [], ri = -1;
11739 for(var i = 0, ci; ci = cs[i]; i++){
11740 if(ci && ci.id == id){
11748 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
11749 // custom can be "{"
11750 function byAttribute(cs, attr, value, op, custom){
11753 useGetStyle = custom == "{",
11754 fn = Ext.DomQuery.operators[op],
11759 for(var i = 0, ci; ci = cs[i]; i++){
11760 // skip non-element nodes.
11761 if(ci.nodeType != 1){
11764 // only need to do this for the first node
11766 xml = Ext.DomQuery.isXml(ci);
11770 // we only need to change the property names if we're dealing with html nodes, not XML
11773 a = Ext.DomQuery.getStyle(ci, attr);
11774 } else if (attr == "class" || attr == "className"){
11776 } else if (attr == "for"){
11778 } else if (attr == "href"){
11779 // getAttribute href bug
11780 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
11781 a = ci.getAttribute("href", 2);
11783 a = ci.getAttribute(attr);
11786 a = ci.getAttribute(attr);
11788 if((fn && fn(a, value)) || (!fn && a)){
11795 function byPseudo(cs, name, value){
11796 return Ext.DomQuery.pseudos[name](cs, value);
11799 function nodupIEXml(cs){
11802 cs[0].setAttribute("_nodup", d);
11804 for(var i = 1, len = cs.length; i < len; i++){
11806 if(!c.getAttribute("_nodup") != d){
11807 c.setAttribute("_nodup", d);
11811 for(var i = 0, len = cs.length; i < len; i++){
11812 cs[i].removeAttribute("_nodup");
11817 function nodup(cs){
11821 var len = cs.length, c, i, r = cs, cj, ri = -1;
11822 if(!len || typeof cs.nodeType != "undefined" || len == 1){
11825 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
11826 return nodupIEXml(cs);
11830 for(i = 1; c = cs[i]; i++){
11835 for(var j = 0; j < i; j++){
11838 for(j = i+1; cj = cs[j]; j++){
11839 if(cj._nodup != d){
11850 function quickDiffIEXml(c1, c2){
11853 for(var i = 0, len = c1.length; i < len; i++){
11854 c1[i].setAttribute("_qdiff", d);
11856 for(var i = 0, len = c2.length; i < len; i++){
11857 if(c2[i].getAttribute("_qdiff") != d){
11858 r[r.length] = c2[i];
11861 for(var i = 0, len = c1.length; i < len; i++){
11862 c1[i].removeAttribute("_qdiff");
11867 function quickDiff(c1, c2){
11868 var len1 = c1.length,
11874 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
11875 return quickDiffIEXml(c1, c2);
11877 for(var i = 0; i < len1; i++){
11880 for(var i = 0, len = c2.length; i < len; i++){
11881 if(c2[i]._qdiff != d){
11882 r[r.length] = c2[i];
11888 function quickId(ns, mode, root, id){
11890 var d = root.ownerDocument || root;
11891 return d.getElementById(id);
11893 ns = getNodes(ns, mode, "*");
11894 return byId(ns, id);
11898 getStyle : function(el, name){
11899 return Ext.fly(el).getStyle(name);
11902 * Compiles a selector/xpath query into a reusable function. The returned function
11903 * takes one parameter "root" (optional), which is the context node from where the query should start.
11904 * @param {String} selector The selector/xpath query
11905 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
11906 * @return {Function}
11908 compile : function(path, type){
11909 type = type || "select";
11911 // setup fn preamble
11912 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
11915 matchers = Ext.DomQuery.matchers,
11916 matchersLn = matchers.length,
11918 // accept leading mode switch
11919 lmode = path.match(modeRe);
11921 if(lmode && lmode[1]){
11922 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
11923 path = path.replace(lmode[1], "");
11926 // strip leading slashes
11927 while(path.substr(0, 1)=="/"){
11928 path = path.substr(1);
11931 while(path && lastPath != path){
11933 var tokenMatch = path.match(tagTokenRe);
11934 if(type == "select"){
11937 if(tokenMatch[1] == "#"){
11938 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
11940 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
11942 path = path.replace(tokenMatch[0], "");
11943 }else if(path.substr(0, 1) != '@'){
11944 fn[fn.length] = 'n = getNodes(n, mode, "*");';
11946 // type of "simple"
11949 if(tokenMatch[1] == "#"){
11950 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
11952 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
11954 path = path.replace(tokenMatch[0], "");
11957 while(!(modeMatch = path.match(modeRe))){
11958 var matched = false;
11959 for(var j = 0; j < matchersLn; j++){
11960 var t = matchers[j];
11961 var m = path.match(t.re);
11963 fn[fn.length] = t.select.replace(tplRe, function(x, i){
11966 path = path.replace(m[0], "");
11971 // prevent infinite loop on bad selector
11974 sourceClass: 'Ext.DomQuery',
11975 sourceMethod: 'compile',
11976 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
11981 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
11982 path = path.replace(modeMatch[1], "");
11986 fn[fn.length] = "return nodup(n);\n}";
11988 // eval fn and return it
11994 * Selects an array of DOM nodes using JavaScript-only implementation.
11996 * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
11998 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
11999 * @param {Node/String} root (optional) The start of the query (defaults to document).
12000 * @return {Array} An Array of DOM elements which match the selector. If there are
12001 * no matches, and empty Array is returned.
12003 jsSelect: function(path, root, type){
12004 // set root to doc if not specified.
12005 root = root || document;
12007 if(typeof root == "string"){
12008 root = document.getElementById(root);
12010 var paths = path.split(","),
12013 // loop over each selector
12014 for(var i = 0, len = paths.length; i < len; i++){
12015 var subPath = paths[i].replace(trimRe, "");
12016 // compile and place in cache
12017 if(!cache[subPath]){
12018 cache[subPath] = Ext.DomQuery.compile(subPath);
12019 if(!cache[subPath]){
12021 sourceClass: 'Ext.DomQuery',
12022 sourceMethod: 'jsSelect',
12023 msg: subPath + ' is not a valid selector'
12027 var result = cache[subPath](root);
12028 if(result && result != document){
12029 results = results.concat(result);
12033 // if there were multiple selectors, make sure dups
12035 if(paths.length > 1){
12036 return nodup(results);
12041 isXml: function(el) {
12042 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
12043 return docEl ? docEl.nodeName !== "HTML" : false;
12047 * Selects an array of DOM nodes by CSS/XPath selector.
12049 * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
12050 * {@link #jsSelect} to do the work.
12052 * Aliased as {@link Ext#query}.
12054 * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
12056 * @param {String} path The selector/xpath query
12057 * @param {Node} root (optional) The start of the query (defaults to document).
12058 * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
12059 * Empty array when no matches.
12062 select : document.querySelectorAll ? function(path, root, type) {
12063 root = root || document;
12064 if (!Ext.DomQuery.isXml(root)) {
12066 var cs = root.querySelectorAll(path);
12067 return Ext.Array.toArray(cs);
12071 return Ext.DomQuery.jsSelect.call(this, path, root, type);
12072 } : function(path, root, type) {
12073 return Ext.DomQuery.jsSelect.call(this, path, root, type);
12077 * Selects a single element.
12078 * @param {String} selector The selector/xpath query
12079 * @param {Node} root (optional) The start of the query (defaults to document).
12080 * @return {Element} The DOM element which matched the selector.
12082 selectNode : function(path, root){
12083 return Ext.DomQuery.select(path, root)[0];
12087 * Selects the value of a node, optionally replacing null with the defaultValue.
12088 * @param {String} selector The selector/xpath query
12089 * @param {Node} root (optional) The start of the query (defaults to document).
12090 * @param {String} defaultValue
12093 selectValue : function(path, root, defaultValue){
12094 path = path.replace(trimRe, "");
12095 if(!valueCache[path]){
12096 valueCache[path] = Ext.DomQuery.compile(path, "select");
12098 var n = valueCache[path](root), v;
12099 n = n[0] ? n[0] : n;
12101 // overcome a limitation of maximum textnode size
12102 // Rumored to potentially crash IE6 but has not been confirmed.
12103 // http://reference.sitepoint.com/javascript/Node/normalize
12104 // https://developer.mozilla.org/En/DOM/Node.normalize
12105 if (typeof n.normalize == 'function') n.normalize();
12107 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
12108 return ((v === null||v === undefined||v==='') ? defaultValue : v);
12112 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
12113 * @param {String} selector The selector/xpath query
12114 * @param {Node} root (optional) The start of the query (defaults to document).
12115 * @param {Number} defaultValue
12118 selectNumber : function(path, root, defaultValue){
12119 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
12120 return parseFloat(v);
12124 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
12125 * @param {String/HTMLElement/Array} el An element id, element or array of elements
12126 * @param {String} selector The simple selector to test
12127 * @return {Boolean}
12129 is : function(el, ss){
12130 if(typeof el == "string"){
12131 el = document.getElementById(el);
12133 var isArray = Ext.isArray(el),
12134 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
12135 return isArray ? (result.length == el.length) : (result.length > 0);
12139 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
12140 * @param {Array} el An array of elements to filter
12141 * @param {String} selector The simple selector to test
12142 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
12143 * the selector instead of the ones that match
12144 * @return {Array} An Array of DOM elements which match the selector. If there are
12145 * no matches, and empty Array is returned.
12147 filter : function(els, ss, nonMatches){
12148 ss = ss.replace(trimRe, "");
12149 if(!simpleCache[ss]){
12150 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
12152 var result = simpleCache[ss](els);
12153 return nonMatches ? quickDiff(result, els) : result;
12157 * Collection of matching regular expressions and code snippets.
12158 * Each capture group within () will be replace the {} in the select
12159 * statement as specified by their index.
12163 select: 'n = byClassName(n, " {1} ");'
12165 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
12166 select: 'n = byPseudo(n, "{1}", "{2}");'
12168 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
12169 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
12172 select: 'n = byId(n, "{1}");'
12175 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
12180 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
12181 * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, > <.
12184 "=" : function(a, v){
12187 "!=" : function(a, v){
12190 "^=" : function(a, v){
12191 return a && a.substr(0, v.length) == v;
12193 "$=" : function(a, v){
12194 return a && a.substr(a.length-v.length) == v;
12196 "*=" : function(a, v){
12197 return a && a.indexOf(v) !== -1;
12199 "%=" : function(a, v){
12200 return (a % v) == 0;
12202 "|=" : function(a, v){
12203 return a && (a == v || a.substr(0, v.length+1) == v+'-');
12205 "~=" : function(a, v){
12206 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
12211 Object hash of "pseudo class" filter functions which are used when filtering selections.
12212 Each function is passed two parameters:
12215 An Array of DOM elements to filter.
12218 The argument (if any) supplied in the selector.
12220 A filter function returns an Array of DOM elements which conform to the pseudo class.
12221 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
12222 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
12224 For example, to filter `a` elements to only return links to __external__ resources:
12226 Ext.DomQuery.pseudos.external = function(c, v){
12227 var r = [], ri = -1;
12228 for(var i = 0, ci; ci = c[i]; i++){
12229 // Include in result set only if it's a link to an external resource
12230 if(ci.hostname != location.hostname){
12237 Then external links could be gathered with the following statement:
12239 var externalLinks = Ext.select("a:external");
12244 "first-child" : function(c){
12245 var r = [], ri = -1, n;
12246 for(var i = 0, ci; ci = n = c[i]; i++){
12247 while((n = n.previousSibling) && n.nodeType != 1);
12255 "last-child" : function(c){
12256 var r = [], ri = -1, n;
12257 for(var i = 0, ci; ci = n = c[i]; i++){
12258 while((n = n.nextSibling) && n.nodeType != 1);
12266 "nth-child" : function(c, a) {
12267 var r = [], ri = -1,
12268 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
12269 f = (m[1] || 1) - 0, l = m[2] - 0;
12270 for(var i = 0, n; n = c[i]; i++){
12271 var pn = n.parentNode;
12272 if (batch != pn._batch) {
12274 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
12275 if(cn.nodeType == 1){
12276 cn.nodeIndex = ++j;
12282 if (l == 0 || n.nodeIndex == l){
12285 } else if ((n.nodeIndex + l) % f == 0){
12293 "only-child" : function(c){
12294 var r = [], ri = -1;;
12295 for(var i = 0, ci; ci = c[i]; i++){
12296 if(!prev(ci) && !next(ci)){
12303 "empty" : function(c){
12304 var r = [], ri = -1;
12305 for(var i = 0, ci; ci = c[i]; i++){
12306 var cns = ci.childNodes, j = 0, cn, empty = true;
12307 while(cn = cns[j]){
12309 if(cn.nodeType == 1 || cn.nodeType == 3){
12321 "contains" : function(c, v){
12322 var r = [], ri = -1;
12323 for(var i = 0, ci; ci = c[i]; i++){
12324 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
12331 "nodeValue" : function(c, v){
12332 var r = [], ri = -1;
12333 for(var i = 0, ci; ci = c[i]; i++){
12334 if(ci.firstChild && ci.firstChild.nodeValue == v){
12341 "checked" : function(c){
12342 var r = [], ri = -1;
12343 for(var i = 0, ci; ci = c[i]; i++){
12344 if(ci.checked == true){
12351 "not" : function(c, ss){
12352 return Ext.DomQuery.filter(c, ss, true);
12355 "any" : function(c, selectors){
12356 var ss = selectors.split('|'),
12357 r = [], ri = -1, s;
12358 for(var i = 0, ci; ci = c[i]; i++){
12359 for(var j = 0; s = ss[j]; j++){
12360 if(Ext.DomQuery.is(ci, s)){
12369 "odd" : function(c){
12370 return this["nth-child"](c, "odd");
12373 "even" : function(c){
12374 return this["nth-child"](c, "even");
12377 "nth" : function(c, a){
12378 return c[a-1] || [];
12381 "first" : function(c){
12385 "last" : function(c){
12386 return c[c.length-1] || [];
12389 "has" : function(c, ss){
12390 var s = Ext.DomQuery.select,
12392 for(var i = 0, ci; ci = c[i]; i++){
12393 if(s(ss, ci).length > 0){
12400 "next" : function(c, ss){
12401 var is = Ext.DomQuery.is,
12403 for(var i = 0, ci; ci = c[i]; i++){
12405 if(n && is(n, ss)){
12412 "prev" : function(c, ss){
12413 var is = Ext.DomQuery.is,
12415 for(var i = 0, ci; ci = c[i]; i++){
12417 if(n && is(n, ss)){
12428 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
12429 * @param {String} path The selector/xpath query
12430 * @param {Node} root (optional) The start of the query (defaults to document).
12435 Ext.query = Ext.DomQuery.select;
12438 * @class Ext.core.Element
12439 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
12440 * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
12441 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
12442 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
12443 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
12447 var el = Ext.get("my-div");
12449 // by DOM element reference
12450 var el = Ext.get(myDivElement);
12452 * <b>Animations</b><br />
12453 * <p>When an element is manipulated, by default there is no animation.</p>
12455 var el = Ext.get("my-div");
12460 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
12461 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
12463 // default animation
12464 el.setWidth(100, true);
12467 * <p>To configure the effects, an object literal with animation options to use as the Element animation
12468 * configuration object can also be specified. Note that the supported Element animation configuration
12469 * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported
12470 * Element animation configuration options are:</p>
12472 Option Default Description
12473 --------- -------- ---------------------------------------------
12474 {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
12475 {@link Ext.fx.Anim#easing easing} easeOut The easing method
12476 {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
12477 {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
12481 // Element animation options object
12483 {@link Ext.fx.Anim#duration duration}: 1,
12484 {@link Ext.fx.Anim#easing easing}: 'elasticIn',
12485 {@link Ext.fx.Anim#callback callback}: this.foo,
12486 {@link Ext.fx.Anim#scope scope}: this
12488 // animation with some options set
12489 el.setWidth(100, opt);
12491 * <p>The Element animation object being used for the animation will be set on the options
12492 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
12494 // using the "anim" property to get the Anim object
12495 if(opt.anim.isAnimated()){
12499 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
12500 * <p><b> Composite (Collections of) Elements</b></p>
12501 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
12502 * @constructor Create a new Element directly.
12503 * @param {String/HTMLElement} element
12504 * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
12507 var DOC = document,
12510 Ext.Element = Ext.core.Element = function(element, forceNew) {
12511 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
12520 if (!forceNew && id && EC[id]) {
12521 // element object already exists
12527 * @type HTMLElement
12532 * The DOM element ID
12535 this.id = id || Ext.id(dom);
12538 var DH = Ext.core.DomHelper,
12539 El = Ext.core.Element;
12544 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
12545 * @param {Object} o The object with the attributes
12546 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
12547 * @return {Ext.core.Element} this
12549 set: function(o, useSet) {
12553 useSet = (useSet !== false) && !!el.setAttribute;
12556 if (o.hasOwnProperty(attr)) {
12558 if (attr == 'style') {
12559 DH.applyStyles(el, val);
12560 } else if (attr == 'cls') {
12561 el.className = val;
12562 } else if (useSet) {
12563 el.setAttribute(attr, val);
12575 * Fires when a mouse click is detected within the element.
12576 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12577 * @param {HtmlElement} t The target of the event.
12578 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12581 * @event contextmenu
12582 * Fires when a right click is detected within the element.
12583 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12584 * @param {HtmlElement} t The target of the event.
12585 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12589 * Fires when a mouse double click is detected within the element.
12590 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12591 * @param {HtmlElement} t The target of the event.
12592 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12596 * Fires when a mousedown is detected within the element.
12597 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12598 * @param {HtmlElement} t The target of the event.
12599 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12603 * Fires when a mouseup is detected within the element.
12604 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12605 * @param {HtmlElement} t The target of the event.
12606 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12610 * Fires when a mouseover is detected within the element.
12611 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12612 * @param {HtmlElement} t The target of the event.
12613 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12617 * Fires when a mousemove is detected with the element.
12618 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12619 * @param {HtmlElement} t The target of the event.
12620 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12624 * Fires when a mouseout is detected with the element.
12625 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12626 * @param {HtmlElement} t The target of the event.
12627 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12630 * @event mouseenter
12631 * Fires when the mouse enters the element.
12632 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12633 * @param {HtmlElement} t The target of the event.
12634 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12637 * @event mouseleave
12638 * Fires when the mouse leaves the element.
12639 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12640 * @param {HtmlElement} t The target of the event.
12641 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12647 * Fires when a keypress is detected within the element.
12648 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12649 * @param {HtmlElement} t The target of the event.
12650 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12654 * Fires when a keydown is detected within the element.
12655 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12656 * @param {HtmlElement} t The target of the event.
12657 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12661 * Fires when a keyup is detected within the element.
12662 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12663 * @param {HtmlElement} t The target of the event.
12664 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12668 // HTML frame/object events
12671 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
12672 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12673 * @param {HtmlElement} t The target of the event.
12674 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12678 * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
12679 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12680 * @param {HtmlElement} t The target of the event.
12681 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12685 * Fires when an object/image is stopped from loading before completely loaded.
12686 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12687 * @param {HtmlElement} t The target of the event.
12688 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12692 * Fires when an object/image/frame cannot be loaded properly.
12693 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12694 * @param {HtmlElement} t The target of the event.
12695 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12699 * Fires when a document view is resized.
12700 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12701 * @param {HtmlElement} t The target of the event.
12702 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12706 * Fires when a document view is scrolled.
12707 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12708 * @param {HtmlElement} t The target of the event.
12709 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12715 * Fires when a user selects some text in a text field, including input and textarea.
12716 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12717 * @param {HtmlElement} t The target of the event.
12718 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12722 * Fires when a control loses the input focus and its value has been modified since gaining focus.
12723 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12724 * @param {HtmlElement} t The target of the event.
12725 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12729 * Fires when a form is submitted.
12730 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12731 * @param {HtmlElement} t The target of the event.
12732 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12736 * Fires when a form is reset.
12737 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12738 * @param {HtmlElement} t The target of the event.
12739 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12743 * Fires when an element receives focus either via the pointing device or by tab navigation.
12744 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12745 * @param {HtmlElement} t The target of the event.
12746 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12750 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
12751 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12752 * @param {HtmlElement} t The target of the event.
12753 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12756 // User Interface events
12758 * @event DOMFocusIn
12759 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
12760 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12761 * @param {HtmlElement} t The target of the event.
12762 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12765 * @event DOMFocusOut
12766 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
12767 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12768 * @param {HtmlElement} t The target of the event.
12769 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12772 * @event DOMActivate
12773 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
12774 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12775 * @param {HtmlElement} t The target of the event.
12776 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12779 // DOM Mutation events
12781 * @event DOMSubtreeModified
12782 * Where supported. Fires when the subtree is modified.
12783 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12784 * @param {HtmlElement} t The target of the event.
12785 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12788 * @event DOMNodeInserted
12789 * Where supported. Fires when a node has been added as a child of another node.
12790 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12791 * @param {HtmlElement} t The target of the event.
12792 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12795 * @event DOMNodeRemoved
12796 * Where supported. Fires when a descendant node of the element is removed.
12797 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12798 * @param {HtmlElement} t The target of the event.
12799 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12802 * @event DOMNodeRemovedFromDocument
12803 * Where supported. Fires when a node is being removed from a document.
12804 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12805 * @param {HtmlElement} t The target of the event.
12806 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12809 * @event DOMNodeInsertedIntoDocument
12810 * Where supported. Fires when a node is being inserted into a document.
12811 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12812 * @param {HtmlElement} t The target of the event.
12813 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12816 * @event DOMAttrModified
12817 * Where supported. Fires when an attribute has been modified.
12818 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12819 * @param {HtmlElement} t The target of the event.
12820 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12823 * @event DOMCharacterDataModified
12824 * Where supported. Fires when the character data has been modified.
12825 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
12826 * @param {HtmlElement} t The target of the event.
12827 * @param {Object} o The options configuration passed to the {@link #addListener} call.
12831 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
12837 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
12838 * @param {String} selector The simple selector to test
12839 * @return {Boolean} True if this element matches the selector, else false
12841 is: function(simpleSelector) {
12842 return Ext.DomQuery.is(this.dom, simpleSelector);
12846 * Tries to focus the element. Any exceptions are caught and ignored.
12847 * @param {Number} defer (optional) Milliseconds to defer the focus
12848 * @return {Ext.core.Element} this
12850 focus: function(defer,
12854 dom = dom || me.dom;
12856 if (Number(defer)) {
12857 Ext.defer(me.focus, defer, null, [null, dom]);
12866 * Tries to blur the element. Any exceptions are caught and ignored.
12867 * @return {Ext.core.Element} this
12877 * Returns the value of the "value" attribute
12878 * @param {Boolean} asNumber true to parse the value as a number
12879 * @return {String/Number}
12881 getValue: function(asNumber) {
12882 var val = this.dom.value;
12883 return asNumber ? parseInt(val, 10) : val;
12887 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
12888 * @param {String} eventName The name of event to handle.
12889 * @param {Function} fn The handler function the event invokes. This function is passed
12890 * the following parameters:<ul>
12891 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
12892 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
12893 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
12894 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
12896 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12897 * <b>If omitted, defaults to this Element.</b>.
12898 * @param {Object} options (optional) An object containing handler configuration properties.
12899 * This may contain any of the following properties:<ul>
12900 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
12901 * <b>If omitted, defaults to this Element.</b></div></li>
12902 * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
12903 * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
12904 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
12905 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
12906 * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
12907 * <li><b>target</b> Ext.core.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
12908 * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
12909 * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
12910 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
12911 * by the specified number of milliseconds. If the event fires again within that time, the original
12912 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
12915 * <b>Combining Options</b><br>
12916 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
12917 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
12918 * types of listeners:<br>
12920 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
12921 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
12923 el.on('click', this.onClick, this, {
12928 });</code></pre></p>
12930 * <b>Attaching multiple handlers in 1 call</b><br>
12931 * The method also allows for a single argument to be passed which is a config object containing properties
12932 * which specify multiple handlers.</p>
12942 fn: this.onMouseOver,
12946 fn: this.onMouseOut,
12951 * Or a shorthand syntax:<br>
12952 * Code:<pre><code></p>
12954 'click' : this.onClick,
12955 'mouseover' : this.onMouseOver,
12956 'mouseout' : this.onMouseOut,
12959 * </code></pre></p>
12960 * <p><b>delegate</b></p>
12961 * <p>This is a configuration option that you can pass along when registering a handler for
12962 * an event to assist with event delegation. Event delegation is a technique that is used to
12963 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
12964 * for a container element as opposed to each element within a container. By setting this
12965 * configuration option to a simple selector, the target element will be filtered to look for
12966 * a descendant of the target.
12967 * For example:<pre><code>
12968 // using this markup:
12970 <p id='p1'>paragraph one</p>
12971 <p id='p2' class='clickable'>paragraph two</p>
12972 <p id='p3'>paragraph three</p>
12974 // utilize event delegation to registering just one handler on the container element:
12975 el = Ext.get('elId');
12980 console.info(t.id); // 'p2'
12984 // filter the target element to be a descendant with the class 'clickable'
12985 delegate: '.clickable'
12988 * </code></pre></p>
12989 * @return {Ext.core.Element} this
12991 addListener: function(eventName, fn, scope, options) {
12992 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
12997 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
12998 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
12999 * listener, the same scope must be specified here.
13002 el.removeListener('click', this.handlerFn);
13004 el.un('click', this.handlerFn);
13006 * @param {String} eventName The name of the event from which to remove the handler.
13007 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
13008 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
13009 * then this must refer to the same object.
13010 * @return {Ext.core.Element} this
13012 removeListener: function(eventName, fn, scope) {
13013 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
13018 * Removes all previous added listeners from this element
13019 * @return {Ext.core.Element} this
13021 removeAllListeners: function() {
13022 Ext.EventManager.removeAll(this.dom);
13027 * Recursively removes all previous added listeners from this element and its children
13028 * @return {Ext.core.Element} this
13030 purgeAllListeners: function() {
13031 Ext.EventManager.purgeElement(this);
13036 * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
13037 * @param size {Mixed} The size to set
13038 * @param units {String} The units to append to a numeric size value
13040 addUnits: function(size, units) {
13042 // Most common case first: Size is set to a number
13043 if (Ext.isNumber(size)) {
13044 return size + (units || this.defaultUnit || 'px');
13047 // Size set to a value which means "auto"
13048 if (size === "" || size == "auto" || size === undefined || size === null) {
13052 // Otherwise, warn if it's not a valid CSS measurement
13053 if (!unitPattern.test(size)) {
13054 if (Ext.isDefined(Ext.global.console)) {
13055 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
13063 * Tests various css rules/browsers to determine if this element uses a border box
13064 * @return {Boolean}
13066 isBorderBox: function() {
13067 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
13071 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
13073 remove: function() {
13079 Ext.removeNode(dom);
13084 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
13085 * @param {Function} overFn The function to call when the mouse enters the Element.
13086 * @param {Function} outFn The function to call when the mouse leaves the Element.
13087 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
13088 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
13089 * @return {Ext.core.Element} this
13091 hover: function(overFn, outFn, scope, options) {
13093 me.on('mouseenter', overFn, scope || me.dom, options);
13094 me.on('mouseleave', outFn, scope || me.dom, options);
13099 * Returns true if this element is an ancestor of the passed element
13100 * @param {HTMLElement/String} el The element to check
13101 * @return {Boolean} True if this element is an ancestor of el, else false
13103 contains: function(el) {
13104 return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
13108 * Returns the value of a namespaced attribute from the element's underlying DOM node.
13109 * @param {String} namespace The namespace in which to look for the attribute
13110 * @param {String} name The attribute name
13111 * @return {String} The attribute value
13114 getAttributeNS: function(ns, name) {
13115 return this.getAttribute(name, ns);
13119 * Returns the value of an attribute from the element's underlying DOM node.
13120 * @param {String} name The attribute name
13121 * @param {String} namespace (optional) The namespace in which to look for the attribute
13122 * @return {String} The attribute value
13125 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
13126 function(name, ns) {
13130 type = typeof d[ns + ":" + name];
13131 if (type != 'undefined' && type != 'unknown') {
13132 return d[ns + ":" + name] || null;
13136 if (name === "for") {
13139 return d[name] || null;
13140 }: function(name, ns) {
13143 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
13145 return d.getAttribute(name) || d[name] || null;
13149 * Update the innerHTML of this element
13150 * @param {String} html The new HTML
13151 * @return {Ext.core.Element} this
13153 update: function(html) {
13155 this.dom.innerHTML = html;
13161 var ep = El.prototype;
13163 El.addMethods = function(o) {
13168 * Appends an event handler (shorthand for {@link #addListener}).
13169 * @param {String} eventName The name of event to handle.
13170 * @param {Function} fn The handler function the event invokes.
13171 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
13172 * @param {Object} options (optional) An object containing standard {@link #addListener} options
13173 * @member Ext.core.Element
13176 ep.on = ep.addListener;
13179 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
13180 * @param {String} eventName The name of the event from which to remove the handler.
13181 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
13182 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
13183 * then this must refer to the same object.
13184 * @return {Ext.core.Element} this
13185 * @member Ext.core.Element
13188 ep.un = ep.removeListener;
13191 * Removes all previous added listeners from this element
13192 * @return {Ext.core.Element} this
13193 * @member Ext.core.Element
13194 * @method clearListeners
13196 ep.clearListeners = ep.removeAllListeners;
13199 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
13200 * Alias to {@link #remove}.
13201 * @member Ext.core.Element
13204 ep.destroy = ep.remove;
13207 * true to automatically adjust width and height settings for box-model issues (default to true)
13209 ep.autoBoxAdjust = true;
13212 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
13216 * Retrieves Ext.core.Element objects.
13217 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
13218 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
13219 * its ID, use {@link Ext.ComponentManager#get}.</p>
13220 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
13221 * object was recreated with the same id via AJAX or DOM.</p>
13222 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
13223 * @return {Element} The Element object (or null if no matching element was found)
13225 * @member Ext.core.Element
13228 El.get = function(el) {
13235 if (typeof el == "string") {
13237 if (! (elm = DOC.getElementById(el))) {
13240 if (EC[el] && EC[el].el) {
13244 ex = El.addToCache(new El(elm));
13247 } else if (el.tagName) {
13249 if (! (id = el.id)) {
13252 if (EC[id] && EC[id].el) {
13256 ex = El.addToCache(new El(el));
13259 } else if (el instanceof El) {
13261 // refresh dom element in case no longer valid,
13262 // catch case where it hasn't been appended
13263 // If an el instance is passed, don't pass to getElementById without some kind of id
13264 if (Ext.isIE && (el.id == undefined || el.id == '')) {
13267 el.dom = DOC.getElementById(el.id) || el.dom;
13271 } else if (el.isComposite) {
13273 } else if (Ext.isArray(el)) {
13274 return El.select(el);
13275 } else if (el == DOC) {
13276 // create a bogus element object representing the document object
13278 var f = function() {};
13279 f.prototype = El.prototype;
13288 El.addToCache = function(el, id) {
13300 // private method for getting and setting element data
13301 El.data = function(el, key, value) {
13306 var c = EC[el.id].data;
13307 if (arguments.length == 2) {
13310 return (c[key] = value);
13315 // Garbage collection - uncache elements/purge listeners on orphaned elements
13316 // so we don't hold a reference and cause the browser to retain them
13317 function garbageCollect() {
13318 if (!Ext.enableGarbageCollector) {
13319 clearInterval(El.collectorThreadId);
13327 if (!EC.hasOwnProperty(eid)) {
13331 if (o.skipGarbageCollection) {
13336 // -------------------------------------------------------
13337 // Determining what is garbage:
13338 // -------------------------------------------------------
13340 // dom node is null, definitely garbage
13341 // -------------------------------------------------------
13343 // no parentNode == direct orphan, definitely garbage
13344 // -------------------------------------------------------
13345 // !d.offsetParent && !document.getElementById(eid)
13346 // display none elements have no offsetParent so we will
13347 // also try to look it up by it's id. However, check
13348 // offsetParent first so we don't do unneeded lookups.
13349 // This enables collection of elements that are not orphans
13350 // directly, but somewhere up the line they have an orphan
13352 // -------------------------------------------------------
13353 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
13354 if (d && Ext.enableListenerCollection) {
13355 Ext.EventManager.removeAll(d);
13360 // Cleanup IE Object leaks
13364 if (!EC.hasOwnProperty(eid)) {
13369 EC = Ext.cache = t;
13373 El.collectorThreadId = setInterval(garbageCollect, 30000);
13375 var flyFn = function() {};
13376 flyFn.prototype = El.prototype;
13379 El.Flyweight = function(dom) {
13383 El.Flyweight.prototype = new flyFn();
13384 El.Flyweight.prototype.isFlyweight = true;
13385 El._flyweights = {};
13388 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
13389 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
13390 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
13391 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
13392 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
13393 * @param {String/HTMLElement} el The dom node or id
13394 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
13395 * (e.g. internally Ext uses "_global")
13396 * @return {Element} The shared Element object (or null if no matching element was found)
13397 * @member Ext.core.Element
13400 El.fly = function(el, named) {
13402 named = named || '_global';
13403 el = Ext.getDom(el);
13405 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
13406 ret = El._flyweights[named];
13412 * Retrieves Ext.core.Element objects.
13413 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
13414 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
13415 * its ID, use {@link Ext.ComponentManager#get}.</p>
13416 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
13417 * object was recreated with the same id via AJAX or DOM.</p>
13418 * Shorthand of {@link Ext.core.Element#get}
13419 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
13420 * @return {Element} The Element object (or null if no matching element was found)
13427 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
13428 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
13429 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
13430 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
13431 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
13432 * @param {String/HTMLElement} el The dom node or id
13433 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
13434 * (e.g. internally Ext uses "_global")
13435 * @return {Element} The shared Element object (or null if no matching element was found)
13441 // speedy lookup for elements never to box adjust
13442 var noBoxAdjust = Ext.isStrict ? {
13449 if (Ext.isIE || Ext.isGecko) {
13450 noBoxAdjust['button'] = 1;
13455 * @class Ext.core.Element
13457 Ext.core.Element.addMethods({
13459 * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
13460 * @param {String} selector The simple selector to test
13461 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
13462 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
13463 * @return {HTMLElement} The matching DOM node (or null if no match was found)
13465 findParent : function(simpleSelector, maxDepth, returnEl) {
13471 maxDepth = maxDepth || 50;
13472 if (isNaN(maxDepth)) {
13473 stopEl = Ext.getDom(maxDepth);
13474 maxDepth = Number.MAX_VALUE;
13476 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
13477 if (Ext.DomQuery.is(p, simpleSelector)) {
13478 return returnEl ? Ext.get(p) : p;
13487 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
13488 * @param {String} selector The simple selector to test
13489 * @param {Number/Mixed} maxDepth (optional) The max depth to
13490 search as a number or element (defaults to 10 || document.body)
13491 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
13492 * @return {HTMLElement} The matching DOM node (or null if no match was found)
13494 findParentNode : function(simpleSelector, maxDepth, returnEl) {
13495 var p = Ext.fly(this.dom.parentNode, '_internal');
13496 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
13500 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
13501 * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
13502 * @param {String} selector The simple selector to test
13503 * @param {Number/Mixed} maxDepth (optional) The max depth to
13504 search as a number or element (defaults to 10 || document.body)
13505 * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
13507 up : function(simpleSelector, maxDepth) {
13508 return this.findParentNode(simpleSelector, maxDepth, true);
13512 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
13513 * @param {String} selector The CSS selector
13514 * @return {CompositeElement/CompositeElement} The composite element
13516 select : function(selector) {
13517 return Ext.core.Element.select(selector, false, this.dom);
13521 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
13522 * @param {String} selector The CSS selector
13523 * @return {Array} An array of the matched nodes
13525 query : function(selector) {
13526 return Ext.DomQuery.select(selector, this.dom);
13530 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
13531 * @param {String} selector The CSS selector
13532 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
13533 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
13535 down : function(selector, returnDom) {
13536 var n = Ext.DomQuery.selectNode(selector, this.dom);
13537 return returnDom ? n : Ext.get(n);
13541 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
13542 * @param {String} selector The CSS selector
13543 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
13544 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
13546 child : function(selector, returnDom) {
13550 id = Ext.get(me).id;
13552 id = id.replace(/[\.:]/g, "\\$0");
13553 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
13554 return returnDom ? node : Ext.get(node);
13558 * Gets the parent node for this element, optionally chaining up trying to match a selector
13559 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
13560 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13561 * @return {Ext.core.Element/HTMLElement} The parent node or null
13563 parent : function(selector, returnDom) {
13564 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
13568 * Gets the next sibling, skipping text nodes
13569 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13570 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13571 * @return {Ext.core.Element/HTMLElement} The next sibling or null
13573 next : function(selector, returnDom) {
13574 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
13578 * Gets the previous sibling, skipping text nodes
13579 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13580 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13581 * @return {Ext.core.Element/HTMLElement} The previous sibling or null
13583 prev : function(selector, returnDom) {
13584 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
13589 * Gets the first child, skipping text nodes
13590 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
13591 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13592 * @return {Ext.core.Element/HTMLElement} The first child or null
13594 first : function(selector, returnDom) {
13595 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
13599 * Gets the last child, skipping text nodes
13600 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
13601 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
13602 * @return {Ext.core.Element/HTMLElement} The last child or null
13604 last : function(selector, returnDom) {
13605 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
13608 matchNode : function(dir, start, selector, returnDom) {
13613 var n = this.dom[start];
13615 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
13616 return !returnDom ? Ext.get(n) : n;
13625 * @class Ext.core.Element
13627 Ext.core.Element.addMethods({
13629 * Appends the passed element(s) to this element
13630 * @param {String/HTMLElement/Array/Element/CompositeElement} el
13631 * @return {Ext.core.Element} this
13633 appendChild : function(el) {
13634 return Ext.get(el).appendTo(this);
13638 * Appends this element to the passed element
13639 * @param {Mixed} el The new parent element
13640 * @return {Ext.core.Element} this
13642 appendTo : function(el) {
13643 Ext.getDom(el).appendChild(this.dom);
13648 * Inserts this element before the passed element in the DOM
13649 * @param {Mixed} el The element before which this element will be inserted
13650 * @return {Ext.core.Element} this
13652 insertBefore : function(el) {
13653 el = Ext.getDom(el);
13654 el.parentNode.insertBefore(this.dom, el);
13659 * Inserts this element after the passed element in the DOM
13660 * @param {Mixed} el The element to insert after
13661 * @return {Ext.core.Element} this
13663 insertAfter : function(el) {
13664 el = Ext.getDom(el);
13665 el.parentNode.insertBefore(this.dom, el.nextSibling);
13670 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
13671 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
13672 * @return {Ext.core.Element} The new child
13674 insertFirst : function(el, returnDom) {
13676 if (el.nodeType || el.dom || typeof el == 'string') { // element
13677 el = Ext.getDom(el);
13678 this.dom.insertBefore(el, this.dom.firstChild);
13679 return !returnDom ? Ext.get(el) : el;
13681 else { // dh config
13682 return this.createChild(el, this.dom.firstChild, returnDom);
13687 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
13688 * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
13689 * @param {String} where (optional) 'before' or 'after' defaults to before
13690 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
13691 * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
13693 insertSibling: function(el, where, returnDom){
13695 isAfter = (where || 'before').toLowerCase() == 'after',
13698 if(Ext.isArray(el)){
13700 Ext.each(el, function(e) {
13701 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
13711 if(el.nodeType || el.dom){
13712 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
13717 if (isAfter && !me.dom.nextSibling) {
13718 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
13720 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
13727 * Replaces the passed element with this element
13728 * @param {Mixed} el The element to replace
13729 * @return {Ext.core.Element} this
13731 replace : function(el) {
13733 this.insertBefore(el);
13739 * Replaces this element with the passed element
13740 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
13741 * @return {Ext.core.Element} this
13743 replaceWith: function(el){
13746 if(el.nodeType || el.dom || typeof el == 'string'){
13748 me.dom.parentNode.insertBefore(el, me.dom);
13750 el = Ext.core.DomHelper.insertBefore(me.dom, el);
13753 delete Ext.cache[me.id];
13754 Ext.removeNode(me.dom);
13755 me.id = Ext.id(me.dom = el);
13756 Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);
13761 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
13762 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
13763 * automatically generated with the specified attributes.
13764 * @param {HTMLElement} insertBefore (optional) a child element of this element
13765 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
13766 * @return {Ext.core.Element} The new child element
13768 createChild : function(config, insertBefore, returnDom) {
13769 config = config || {tag:'div'};
13770 if (insertBefore) {
13771 return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
13774 return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
13779 * Creates and wraps this element with another element
13780 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
13781 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
13782 * @return {HTMLElement/Element} The newly created wrapper element
13784 wrap : function(config, returnDom) {
13785 var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
13786 d = newEl.dom || newEl;
13788 d.appendChild(this.dom);
13793 * Inserts an html fragment into this element
13794 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
13795 * @param {String} html The HTML fragment
13796 * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
13797 * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
13799 insertHtml : function(where, html, returnEl) {
13800 var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
13801 return returnEl ? Ext.get(el) : el;
13806 * @class Ext.core.Element
13809 Ext.core.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
13810 // local style camelizing for speed
13811 var supports = Ext.supports,
13812 view = document.defaultView,
13813 opacityRe = /alpha\(opacity=(.*)\)/i,
13814 trimRe = /^\s+|\s+$/g,
13817 adjustDirect2DTableRe = /table-row|table-.*-group/,
13818 INTERNAL = '_internal',
13819 PADDING = 'padding',
13825 BOTTOM = '-bottom',
13829 ISCLIPPED = 'isClipped',
13830 OVERFLOW = 'overflow',
13831 OVERFLOWX = 'overflow-x',
13832 OVERFLOWY = 'overflow-y',
13833 ORIGINALCLIP = 'originalClip',
13834 // special markup used throughout Ext when box wrapping elements
13835 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
13836 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
13837 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
13838 data = Ext.core.Element.data;
13840 Ext.override(Ext.core.Element, {
13843 * TODO: Look at this
13845 // private ==> used by Fx
13846 adjustWidth : function(width) {
13848 isNum = (typeof width == 'number');
13850 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13851 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
13853 return (isNum && width < 0) ? 0 : width;
13856 // private ==> used by Fx
13857 adjustHeight : function(height) {
13859 isNum = (typeof height == "number");
13861 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
13862 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
13864 return (isNum && height < 0) ? 0 : height;
13869 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
13870 * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
13871 * @return {Ext.core.Element} this
13873 addCls : function(className){
13876 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
13878 if (className === undefined) {
13881 // Separate case is for speed
13882 if (Object.prototype.toString.call(className) !== '[object Array]') {
13883 if (typeof className === 'string') {
13884 className = className.replace(trimRe, '').split(spacesRe);
13885 if (className.length === 1) {
13886 className = className[0];
13887 if (!me.hasCls(className)) {
13888 me.dom.className += space + className;
13891 this.addCls(className);
13895 for (i = 0, len = className.length; i < len; i++) {
13897 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
13902 me.dom.className += space + cls.join(" ");
13909 * Removes one or more CSS classes from the element.
13910 * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
13911 * @return {Ext.core.Element} this
13913 removeCls : function(className){
13915 i, idx, len, cls, elClasses;
13916 if (className === undefined) {
13919 if (Object.prototype.toString.call(className) !== '[object Array]') {
13920 className = className.replace(trimRe, '').split(spacesRe);
13922 if (me.dom && me.dom.className) {
13923 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
13924 for (i = 0, len = className.length; i < len; i++) {
13925 cls = className[i];
13926 if (typeof cls == 'string') {
13927 cls = cls.replace(trimRe, '');
13928 idx = Ext.Array.indexOf(elClasses, cls);
13930 Ext.Array.erase(elClasses, idx, 1);
13934 me.dom.className = elClasses.join(" ");
13940 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
13941 * @param {String/Array} className The CSS class to add, or an array of classes
13942 * @return {Ext.core.Element} this
13944 radioCls : function(className){
13945 var cn = this.dom.parentNode.childNodes,
13947 className = Ext.isArray(className) ? className : [className];
13948 for (i = 0, len = cn.length; i < len; i++) {
13950 if (v && v.nodeType == 1) {
13951 Ext.fly(v, '_internal').removeCls(className);
13954 return this.addCls(className);
13958 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
13959 * @param {String} className The CSS class to toggle
13960 * @return {Ext.core.Element} this
13963 toggleCls : Ext.supports.ClassList ?
13964 function(className) {
13965 this.dom.classList.toggle(Ext.String.trim(className));
13968 function(className) {
13969 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
13973 * Checks if the specified CSS class exists on this element's DOM node.
13974 * @param {String} className The CSS class to check for
13975 * @return {Boolean} True if the class exists, else false
13978 hasCls : Ext.supports.ClassList ?
13979 function(className) {
13983 className = className.split(spacesRe);
13984 var ln = className.length,
13986 for (; i < ln; i++) {
13987 if (className[i] && this.dom.classList.contains(className[i])) {
13993 function(className){
13994 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
13998 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
13999 * @param {String} oldClassName The CSS class to replace
14000 * @param {String} newClassName The replacement CSS class
14001 * @return {Ext.core.Element} this
14003 replaceCls : function(oldClassName, newClassName){
14004 return this.removeCls(oldClassName).addCls(newClassName);
14007 isStyle : function(style, val) {
14008 return this.getStyle(style) == val;
14012 * Normalizes currentStyle and computedStyle.
14013 * @param {String} property The style property whose value is returned.
14014 * @return {String} The current value of the style property for this element.
14017 getStyle : function(){
14018 return view && view.getComputedStyle ?
14021 v, cs, out, display, cleaner;
14023 if(el == document){
14026 prop = Ext.core.Element.normalize(prop);
14027 out = (v = el.style[prop]) ? v :
14028 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
14030 // Ignore cases when the margin is correctly reported as 0, the bug only shows
14032 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
14033 cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
14034 display = this.getStyle('display');
14035 el.style.display = 'inline-block';
14036 out = view.getComputedStyle(el, '').marginRight;
14037 el.style.display = display;
14041 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
14042 out = 'transparent';
14050 if (el == document) {
14054 if (prop == 'opacity') {
14055 if (el.style.filter.match) {
14056 m = el.style.filter.match(opacityRe);
14058 var fv = parseFloat(m[1]);
14060 return fv ? fv / 100 : 0;
14066 prop = Ext.core.Element.normalize(prop);
14067 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
14072 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
14073 * are convert to standard 6 digit hex color.
14074 * @param {String} attr The css attribute
14075 * @param {String} defaultValue The default value to use when a valid color isn't found
14076 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
14079 getColor : function(attr, defaultValue, prefix){
14080 var v = this.getStyle(attr),
14081 color = prefix || prefix === '' ? prefix : '#',
14084 if(!v || (/transparent|inherit/.test(v))) {
14085 return defaultValue;
14088 Ext.each(v.slice(4, v.length -1).split(','), function(s){
14089 h = parseInt(s, 10);
14090 color += (h < 16 ? '0' : '') + h.toString(16);
14093 v = v.replace('#', '');
14094 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
14096 return(color.length > 5 ? color.toLowerCase() : defaultValue);
14100 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
14101 * @param {String/Object} property The style property to be set, or an object of multiple styles.
14102 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
14103 * @return {Ext.core.Element} this
14105 setStyle : function(prop, value){
14112 if (typeof prop === 'string') {
14117 for (style in prop) {
14118 if (prop.hasOwnProperty(style)) {
14119 value = Ext.value(prop[style], '');
14120 if (style == 'opacity') {
14121 me.setOpacity(value);
14124 me.dom.style[Ext.core.Element.normalize(style)] = value;
14132 * Set the opacity of the element
14133 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
14134 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
14135 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
14136 * @return {Ext.core.Element} this
14138 setOpacity: function(opacity, animate) {
14148 style = me.dom.style;
14150 if (!animate || !me.anim) {
14151 if (!Ext.supports.Opacity) {
14152 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
14153 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
14156 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
14159 style.opacity = opacity;
14163 if (!Ext.isObject(animate)) {
14169 me.animate(Ext.applyIf({
14181 * Clears any opacity settings from this element. Required in some cases for IE.
14182 * @return {Ext.core.Element} this
14184 clearOpacity : function(){
14185 var style = this.dom.style;
14186 if(!Ext.supports.Opacity){
14187 if(!Ext.isEmpty(style.filter)){
14188 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
14191 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
14198 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
14199 * @return {Number} 0 or 1
14201 adjustDirect2DDimension: function(dimension) {
14204 display = me.getStyle('display'),
14205 inlineDisplay = dom.style['display'],
14206 inlinePosition = dom.style['position'],
14207 originIndex = dimension === 'width' ? 0 : 1,
14210 if (display === 'inline') {
14211 dom.style['display'] = 'inline-block';
14214 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
14216 // floating will contain digits that appears after the decimal point
14217 // if height or width are set to auto we fallback to msTransformOrigin calculation
14218 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
14220 dom.style['position'] = inlinePosition;
14222 if (display === 'inline') {
14223 dom.style['display'] = inlineDisplay;
14230 * Returns the offset height of the element
14231 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
14232 * @return {Number} The element's height
14234 getHeight: function(contentHeight, preciseHeight) {
14237 hidden = Ext.isIE && me.isStyle('display', 'none'),
14238 height, overflow, style, floating;
14240 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14241 // We will put the overflow back to it's original value when we are done measuring.
14242 if (Ext.isIEQuirks) {
14244 overflow = style.overflow;
14245 me.setStyle({ overflow: 'hidden'});
14248 height = dom.offsetHeight;
14250 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
14252 // IE9 Direct2D dimension rounding bug
14253 if (!hidden && Ext.supports.Direct2DBug) {
14254 floating = me.adjustDirect2DDimension('height');
14255 if (preciseHeight) {
14256 height += floating;
14258 else if (floating > 0 && floating < 0.5) {
14263 if (contentHeight) {
14264 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
14267 if (Ext.isIEQuirks) {
14268 me.setStyle({ overflow: overflow});
14278 * Returns the offset width of the element
14279 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
14280 * @return {Number} The element's width
14282 getWidth: function(contentWidth, preciseWidth) {
14285 hidden = Ext.isIE && me.isStyle('display', 'none'),
14286 rect, width, overflow, style, floating, parentPosition;
14288 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14289 // We will put the overflow back to it's original value when we are done measuring.
14290 if (Ext.isIEQuirks) {
14292 overflow = style.overflow;
14293 me.setStyle({overflow: 'hidden'});
14296 // Fix Opera 10.5x width calculation issues
14297 if (Ext.isOpera10_5) {
14298 if (dom.parentNode.currentStyle.position === 'relative') {
14299 parentPosition = dom.parentNode.style.position;
14300 dom.parentNode.style.position = 'static';
14301 width = dom.offsetWidth;
14302 dom.parentNode.style.position = parentPosition;
14304 width = Math.max(width || 0, dom.offsetWidth);
14306 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
14307 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
14308 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
14309 // subpixel measurements so we can force them to always be rounded up. See
14310 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
14311 } else if (Ext.supports.BoundingClientRect) {
14312 rect = dom.getBoundingClientRect();
14313 width = rect.right - rect.left;
14314 width = preciseWidth ? width : Math.ceil(width);
14316 width = dom.offsetWidth;
14319 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
14321 // IE9 Direct2D dimension rounding bug
14322 if (!hidden && Ext.supports.Direct2DBug) {
14323 floating = me.adjustDirect2DDimension('width');
14324 if (preciseWidth) {
14327 else if (floating > 0 && floating < 0.5) {
14332 if (contentWidth) {
14333 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
14336 if (Ext.isIEQuirks) {
14337 me.setStyle({ overflow: overflow});
14347 * Set the width of this Element.
14348 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
14349 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14350 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14352 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14353 * @return {Ext.core.Element} this
14355 setWidth : function(width, animate){
14357 width = me.adjustWidth(width);
14358 if (!animate || !me.anim) {
14359 me.dom.style.width = me.addUnits(width);
14362 if (!Ext.isObject(animate)) {
14365 me.animate(Ext.applyIf({
14375 * Set the height of this Element.
14377 // change the height to 200px and animate with default configuration
14378 Ext.fly('elementId').setHeight(200, true);
14380 // change the height to 150px and animate with a custom configuration
14381 Ext.fly('elId').setHeight(150, {
14382 duration : .5, // animation will have a duration of .5 seconds
14383 // will change the content to "finished"
14384 callback: function(){ this.{@link #update}("finished"); }
14387 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
14388 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
14389 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14391 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14392 * @return {Ext.core.Element} this
14394 setHeight : function(height, animate){
14396 height = me.adjustHeight(height);
14397 if (!animate || !me.anim) {
14398 me.dom.style.height = me.addUnits(height);
14401 if (!Ext.isObject(animate)) {
14404 me.animate(Ext.applyIf({
14414 * Gets the width of the border(s) for the specified side(s)
14415 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14416 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
14417 * @return {Number} The width of the sides passed added together
14419 getBorderWidth : function(side){
14420 return this.addStyles(side, borders);
14424 * Gets the width of the padding(s) for the specified side(s)
14425 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
14426 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
14427 * @return {Number} The padding of the sides passed added together
14429 getPadding : function(side){
14430 return this.addStyles(side, paddings);
14434 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
14435 * @return {Ext.core.Element} this
14441 if(!data(dom, ISCLIPPED)){
14442 data(dom, ISCLIPPED, true);
14443 data(dom, ORIGINALCLIP, {
14444 o: me.getStyle(OVERFLOW),
14445 x: me.getStyle(OVERFLOWX),
14446 y: me.getStyle(OVERFLOWY)
14448 me.setStyle(OVERFLOW, HIDDEN);
14449 me.setStyle(OVERFLOWX, HIDDEN);
14450 me.setStyle(OVERFLOWY, HIDDEN);
14456 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
14457 * @return {Ext.core.Element} this
14459 unclip : function(){
14464 if(data(dom, ISCLIPPED)){
14465 data(dom, ISCLIPPED, false);
14466 clip = data(dom, ORIGINALCLIP);
14468 me.setStyle(OVERFLOW, o.o);
14471 me.setStyle(OVERFLOWX, o.x);
14474 me.setStyle(OVERFLOWY, o.y);
14481 addStyles : function(sides, styles){
14483 sidesArr = sides.match(wordsRe),
14485 len = sidesArr.length,
14487 for (; i < len; i++) {
14488 side = sidesArr[i];
14489 size = side && parseInt(this.getStyle(styles[side]), 10);
14491 totalSize += MATH.abs(size);
14500 * More flexible version of {@link #setStyle} for setting style properties.
14501 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
14502 * a function which returns such a specification.
14503 * @return {Ext.core.Element} this
14505 applyStyles : function(style){
14506 Ext.core.DomHelper.applyStyles(this.dom, style);
14511 * Returns an object with properties matching the styles requested.
14512 * For example, el.getStyles('color', 'font-size', 'width') might return
14513 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
14514 * @param {String} style1 A style name
14515 * @param {String} style2 A style name
14516 * @param {String} etc.
14517 * @return {Object} The style object
14519 getStyles : function(){
14521 len = arguments.length,
14524 for(; i < len; ++i) {
14525 style = arguments[i];
14526 styles[style] = this.getStyle(style);
14532 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
14533 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
14534 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
14535 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
14536 * is of this form:</p>
14538 Ext.core.Element.boxMarkup =
14539 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
14540 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
14541 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
14543 * <p>Example usage:</p>
14546 Ext.get("foo").boxWrap();
14548 // You can also add a custom class and use CSS inheritance rules to customize the box look.
14549 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
14550 // for how to create a custom box wrap style.
14551 Ext.get("foo").boxWrap().addCls("x-box-blue");
14553 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
14554 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
14555 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
14556 * also supply all of the necessary rules.
14557 * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
14559 boxWrap : function(cls){
14560 cls = cls || Ext.baseCSSPrefix + 'box';
14561 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
14562 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
14567 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
14568 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
14569 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14570 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
14571 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
14573 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
14574 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
14575 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
14577 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
14578 * @return {Ext.core.Element} this
14580 setSize : function(width, height, animate){
14582 if (Ext.isObject(width)) { // in case of object from getSize()
14584 height = width.height;
14585 width = width.width;
14587 width = me.adjustWidth(width);
14588 height = me.adjustHeight(height);
14589 if(!animate || !me.anim){
14590 me.dom.style.width = me.addUnits(width);
14591 me.dom.style.height = me.addUnits(height);
14594 if (animate === true) {
14597 me.animate(Ext.applyIf({
14608 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
14609 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
14610 * if a height has not been set using CSS.
14613 getComputedHeight : function(){
14615 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
14617 h = parseFloat(me.getStyle('height')) || 0;
14618 if(!me.isBorderBox()){
14619 h += me.getFrameWidth('tb');
14626 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
14627 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
14628 * if a width has not been set using CSS.
14631 getComputedWidth : function(){
14633 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
14636 w = parseFloat(me.getStyle('width')) || 0;
14637 if(!me.isBorderBox()){
14638 w += me.getFrameWidth('lr');
14645 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
14646 for more information about the sides.
14647 * @param {String} sides
14650 getFrameWidth : function(sides, onlyContentBox){
14651 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
14655 * Sets up event handlers to add and remove a css class when the mouse is over this element
14656 * @param {String} className
14657 * @return {Ext.core.Element} this
14659 addClsOnOver : function(className){
14660 var dom = this.dom;
14663 Ext.fly(dom, INTERNAL).addCls(className);
14666 Ext.fly(dom, INTERNAL).removeCls(className);
14673 * Sets up event handlers to add and remove a css class when this element has the focus
14674 * @param {String} className
14675 * @return {Ext.core.Element} this
14677 addClsOnFocus : function(className){
14680 me.on("focus", function(){
14681 Ext.fly(dom, INTERNAL).addCls(className);
14683 me.on("blur", function(){
14684 Ext.fly(dom, INTERNAL).removeCls(className);
14690 * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
14691 * @param {String} className
14692 * @return {Ext.core.Element} this
14694 addClsOnClick : function(className){
14695 var dom = this.dom;
14696 this.on("mousedown", function(){
14697 Ext.fly(dom, INTERNAL).addCls(className);
14698 var d = Ext.getDoc(),
14700 Ext.fly(dom, INTERNAL).removeCls(className);
14701 d.removeListener("mouseup", fn);
14703 d.on("mouseup", fn);
14709 * <p>Returns the dimensions of the element available to lay content out in.<p>
14710 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
14711 * example:<pre><code>
14712 var vpSize = Ext.getBody().getViewSize();
14714 // all Windows created afterwards will have a default value of 90% height and 95% width
14715 Ext.Window.override({
14716 width: vpSize.width * 0.9,
14717 height: vpSize.height * 0.95
14719 // To handle window resizing you would have to hook onto onWindowResize.
14722 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
14723 * To obtain the size including scrollbars, use getStyleSize
14725 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14728 getViewSize : function(){
14731 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
14732 style, overflow, ret;
14734 // If the body, use static methods
14737 width : Ext.core.Element.getViewWidth(),
14738 height : Ext.core.Element.getViewHeight()
14741 // Else use clientHeight/clientWidth
14744 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
14745 // We will put the overflow back to it's original value when we are done measuring.
14746 if (Ext.isIE6 || Ext.isIEQuirks) {
14748 overflow = style.overflow;
14749 me.setStyle({ overflow: 'hidden'});
14752 width : dom.clientWidth,
14753 height : dom.clientHeight
14755 if (Ext.isIE6 || Ext.isIEQuirks) {
14756 me.setStyle({ overflow: overflow });
14763 * <p>Returns the dimensions of the element available to lay content out in.<p>
14765 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
14766 * To obtain the size excluding scrollbars, use getViewSize
14768 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
14771 getStyleSize : function(){
14775 isDoc = (d == doc || d == doc.body),
14779 // If the body, use static methods
14782 width : Ext.core.Element.getViewWidth(),
14783 height : Ext.core.Element.getViewHeight()
14786 // Use Styles if they are set
14787 if(s.width && s.width != 'auto'){
14788 w = parseFloat(s.width);
14789 if(me.isBorderBox()){
14790 w -= me.getFrameWidth('lr');
14793 // Use Styles if they are set
14794 if(s.height && s.height != 'auto'){
14795 h = parseFloat(s.height);
14796 if(me.isBorderBox()){
14797 h -= me.getFrameWidth('tb');
14800 // Use getWidth/getHeight if style not set.
14801 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
14805 * Returns the size of the element.
14806 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
14807 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
14809 getSize : function(contentSize){
14810 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
14814 * Forces the browser to repaint this element
14815 * @return {Ext.core.Element} this
14817 repaint : function(){
14818 var dom = this.dom;
14819 this.addCls(Ext.baseCSSPrefix + 'repaint');
14820 setTimeout(function(){
14821 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
14827 * Disables text selection for this element (normalized across browsers)
14828 * @return {Ext.core.Element} this
14830 unselectable : function(){
14832 me.dom.unselectable = "on";
14834 me.swallowEvent("selectstart", true);
14835 me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
14836 me.addCls(Ext.baseCSSPrefix + 'unselectable');
14842 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
14843 * then it returns the calculated width of the sides (see getPadding)
14844 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
14845 * @return {Object/Number}
14847 getMargin : function(side){
14849 hash = {t:"top", l:"left", r:"right", b: "bottom"},
14854 for (key in me.margins){
14855 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
14859 return me.addStyles.call(me, side, me.margins);
14865 * @class Ext.core.Element
14868 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
14872 Ext.core.Element.VISIBILITY = 1;
14874 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
14878 Ext.core.Element.DISPLAY = 2;
14881 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
14886 Ext.core.Element.OFFSETS = 3;
14889 Ext.core.Element.ASCLASS = 4;
14892 * Defaults to 'x-hide-nosize'
14896 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
14898 Ext.core.Element.addMethods(function(){
14899 var El = Ext.core.Element,
14900 OPACITY = "opacity",
14901 VISIBILITY = "visibility",
14902 DISPLAY = "display",
14904 OFFSETS = "offsets",
14905 ASCLASS = "asclass",
14908 ORIGINALDISPLAY = 'originalDisplay',
14909 VISMODE = 'visibilityMode',
14910 ISVISIBLE = 'isVisible',
14912 getDisplay = function(dom){
14913 var d = data(dom, ORIGINALDISPLAY);
14914 if(d === undefined){
14915 data(dom, ORIGINALDISPLAY, d = '');
14919 getVisMode = function(dom){
14920 var m = data(dom, VISMODE);
14921 if(m === undefined){
14922 data(dom, VISMODE, m = 1);
14929 * The element's default display mode (defaults to "")
14932 originalDisplay : "",
14933 visibilityMode : 1,
14936 * Sets the element's visibility mode. When setVisible() is called it
14937 * will use this to determine whether to set the visibility or the display property.
14938 * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
14939 * @return {Ext.core.Element} this
14941 setVisibilityMode : function(visMode){
14942 data(this.dom, VISMODE, visMode);
14947 * Checks whether the element is currently visible using both visibility and display properties.
14948 * @return {Boolean} True if the element is currently visible, else false
14950 isVisible : function() {
14953 visible = data(dom, ISVISIBLE);
14955 if(typeof visible == 'boolean'){ //return the cached value if registered
14958 //Determine the current state based on display states
14959 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
14960 !me.isStyle(DISPLAY, NONE) &&
14961 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
14963 data(dom, ISVISIBLE, visible);
14968 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
14969 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
14970 * @param {Boolean} visible Whether the element is visible
14971 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
14972 * @return {Ext.core.Element} this
14974 setVisible : function(visible, animate){
14975 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
14977 visMode = getVisMode(dom);
14980 // hideMode string override
14981 if (typeof animate == 'string'){
14984 visMode = El.DISPLAY;
14987 visMode = El.VISIBILITY;
14990 visMode = El.OFFSETS;
14994 visMode = El.ASCLASS;
14997 me.setVisibilityMode(visMode);
15001 if (!animate || !me.anim) {
15002 if(visMode == El.ASCLASS ){
15004 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
15006 } else if (visMode == El.DISPLAY){
15008 return me.setDisplayed(visible);
15010 } else if (visMode == El.OFFSETS){
15013 // Remember position for restoring, if we are not already hidden by offsets.
15014 if (!me.hideModeStyles) {
15015 me.hideModeStyles = {
15016 position: me.getStyle('position'),
15017 top: me.getStyle('top'),
15018 left: me.getStyle('left')
15021 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
15024 // Only "restore" as position if we have actually been hidden using offsets.
15025 // Calling setVisible(true) on a positioned element should not reposition it.
15026 else if (me.hideModeStyles) {
15027 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
15028 delete me.hideModeStyles;
15033 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
15034 dom.style.visibility = visible ? '' : HIDDEN;
15037 // closure for composites
15039 me.setOpacity(0.01);
15040 me.setVisible(true);
15042 if (!Ext.isObject(animate)) {
15048 me.animate(Ext.applyIf({
15049 callback: function() {
15050 visible || me.setVisible(false).setOpacity(1);
15053 opacity: (visible) ? 1 : 0
15057 data(dom, ISVISIBLE, visible); //set logical visibility state
15064 * Determine if the Element has a relevant height and width available based
15065 * upon current logical visibility state
15067 hasMetrics : function(){
15068 var dom = this.dom;
15069 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
15073 * Toggles the element's visibility or display, depending on visibility mode.
15074 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
15075 * @return {Ext.core.Element} this
15077 toggle : function(animate){
15079 me.setVisible(!me.isVisible(), me.anim(animate));
15084 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
15085 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
15086 * @return {Ext.core.Element} this
15088 setDisplayed : function(value) {
15089 if(typeof value == "boolean"){
15090 value = value ? getDisplay(this.dom) : NONE;
15092 this.setStyle(DISPLAY, value);
15097 fixDisplay : function(){
15099 if (me.isStyle(DISPLAY, NONE)) {
15100 me.setStyle(VISIBILITY, HIDDEN);
15101 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
15102 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
15103 me.setStyle(DISPLAY, "block");
15109 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15110 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15111 * @return {Ext.core.Element} this
15113 hide : function(animate){
15114 // hideMode override
15115 if (typeof animate == 'string'){
15116 this.setVisible(false, animate);
15119 this.setVisible(false, this.anim(animate));
15124 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
15125 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
15126 * @return {Ext.core.Element} this
15128 show : function(animate){
15129 // hideMode override
15130 if (typeof animate == 'string'){
15131 this.setVisible(true, animate);
15134 this.setVisible(true, this.anim(animate));
15140 * @class Ext.core.Element
15142 Ext.applyIf(Ext.core.Element.prototype, {
15143 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15144 animate: function(config) {
15147 me = Ext.get(me.dom);
15149 if (Ext.fx.Manager.hasFxBlock(me.id)) {
15152 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
15156 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
15157 anim: function(config) {
15158 if (!Ext.isObject(config)) {
15159 return (config) ? {} : false;
15163 duration = config.duration || Ext.fx.Anim.prototype.duration,
15164 easing = config.easing || 'ease',
15167 if (config.stopAnimation) {
15168 me.stopAnimation();
15171 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
15173 // Clear any 'paused' defaults.
15174 Ext.fx.Manager.setFxDefaults(me.id, {
15180 remove: config.remove,
15181 alternate: config.alternate || false,
15182 duration: duration,
15184 callback: config.callback,
15185 listeners: config.listeners,
15186 iterations: config.iterations || 1,
15187 scope: config.scope,
15188 block: config.block,
15189 concurrent: config.concurrent,
15190 delay: config.delay || 0,
15192 keyframes: config.keyframes,
15193 from: config.from || {},
15194 to: Ext.apply({}, config)
15196 Ext.apply(animConfig.to, config.to);
15198 // Anim API properties - backward compat
15199 delete animConfig.to.to;
15200 delete animConfig.to.from;
15201 delete animConfig.to.remove;
15202 delete animConfig.to.alternate;
15203 delete animConfig.to.keyframes;
15204 delete animConfig.to.iterations;
15205 delete animConfig.to.listeners;
15206 delete animConfig.to.target;
15207 delete animConfig.to.paused;
15208 delete animConfig.to.callback;
15209 delete animConfig.to.scope;
15210 delete animConfig.to.duration;
15211 delete animConfig.to.easing;
15212 delete animConfig.to.concurrent;
15213 delete animConfig.to.block;
15214 delete animConfig.to.stopAnimation;
15215 delete animConfig.to.delay;
15220 * Slides the element into view. An anchor point can be optionally passed to set the point of
15221 * origin for the slide effect. This function automatically handles wrapping the element with
15222 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
15225 // default: slide the element in from the top
15228 // custom: slide the element in from the right with a 2-second duration
15229 el.slideIn('r', { duration: 2 });
15231 // common config options shown with default values
15237 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
15238 * @param {Object} options (optional) Object literal with any of the Fx config options
15239 * @return {Ext.core.Element} The Element
15241 slideIn: function(anchor, obj, slideOut) {
15243 elStyle = me.dom.style,
15244 beforeAnim, wrapAnim;
15246 anchor = anchor || "t";
15249 beforeAnim = function() {
15250 var animScope = this,
15251 listeners = obj.listeners,
15252 box, position, restoreSize, wrap, anim;
15259 if ((anchor == 't' || anchor == 'b') && box.height == 0) {
15260 box.height = me.dom.scrollHeight;
15262 else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
15263 box.width = me.dom.scrollWidth;
15266 position = me.getPositioning();
15267 me.setSize(box.width, box.height);
15271 visibility: slideOut ? 'visible' : 'hidden'
15274 wrap.setPositioning(position);
15275 if (wrap.isStyle('position', 'static')) {
15276 wrap.position('relative');
15278 me.clearPositioning('auto');
15281 // This element is temporarily positioned absolute within its wrapper.
15282 // Restore to its default, CSS-inherited visibility setting.
15283 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
15286 position: 'absolute'
15289 wrap.setSize(box.width, box.height);
15296 width: box.width + 'px',
15300 width: box.width + 'px',
15301 height: box.height + 'px'
15304 elStyle.bottom = '0px';
15310 height: box.height + 'px'
15313 width: box.width + 'px',
15314 height: box.height + 'px'
15317 elStyle.right = '0px';
15322 x: box.x + box.width,
15324 height: box.height + 'px'
15328 width: box.width + 'px',
15329 height: box.height + 'px'
15336 y: box.y + box.height,
15337 width: box.width + 'px',
15342 width: box.width + 'px',
15343 height: box.height + 'px'
15356 width: box.width + 'px',
15357 height: box.height + 'px'
15360 elStyle.bottom = '0px';
15361 elStyle.right = '0px';
15366 x: box.x + box.width,
15372 width: box.width + 'px',
15373 height: box.height + 'px'
15376 elStyle.right = '0px';
15381 x: box.x + box.width,
15382 y: box.y + box.height,
15389 width: box.width + 'px',
15390 height: box.height + 'px'
15397 y: box.y + box.height,
15403 width: box.width + 'px',
15404 height: box.height + 'px'
15407 elStyle.bottom = '0px';
15412 wrapAnim = Ext.apply({}, obj);
15413 delete wrapAnim.listeners;
15414 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
15417 easing: 'ease-out',
15418 from: slideOut ? anim.to : anim.from,
15419 to: slideOut ? anim.from : anim.to
15422 // In the absence of a callback, this listener MUST be added first
15423 wrapAnim.on('afteranimate', function() {
15425 me.setPositioning(position);
15426 if (obj.useDisplay) {
15427 me.setDisplayed(false);
15433 me.clearPositioning();
15434 me.setPositioning(position);
15437 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
15440 me.setSize(box.width, box.height);
15443 // Add configured listeners after
15445 wrapAnim.on(listeners);
15450 duration: obj.duration ? obj.duration * 2 : 1000,
15457 if (wrapAnim && wrapAnim.running) {
15469 * Slides the element out of view. An anchor point can be optionally passed to set the end point
15470 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
15471 * 'hidden') but block elements will still take up space in the document. The element must be removed
15472 * from the DOM using the 'remove' config option if desired. This function automatically handles
15473 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
15476 // default: slide the element out to the top
15479 // custom: slide the element out to the right with a 2-second duration
15480 el.slideOut('r', { duration: 2 });
15482 // common config options shown with default values
15490 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
15491 * @param {Object} options (optional) Object literal with any of the Fx config options
15492 * @return {Ext.core.Element} The Element
15494 slideOut: function(anchor, o) {
15495 return this.slideIn(anchor, o, true);
15499 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
15500 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
15506 // common config options shown with default values
15513 * @param {Object} options (optional) Object literal with any of the Fx config options
15514 * @return {Ext.core.Element} The Element
15517 puff: function(obj) {
15520 obj = Ext.applyIf(obj || {}, {
15521 easing: 'ease-out',
15526 beforeAnim = function() {
15530 var box = me.getBox(),
15531 fontSize = me.getStyle('fontSize'),
15532 position = me.getPositioning();
15534 width: box.width * 2,
15535 height: box.height * 2,
15536 x: box.x - (box.width / 2),
15537 y: box.y - (box.height /2),
15541 this.on('afteranimate',function() {
15543 if (obj.useDisplay) {
15544 me.setDisplayed(false);
15549 me.setPositioning(position);
15550 me.setStyle({fontSize: fontSize});
15556 duration: obj.duration,
15557 easing: obj.easing,
15568 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
15569 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
15570 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
15576 // all config options shown with default values
15584 * @param {Object} options (optional) Object literal with any of the Fx config options
15585 * @return {Ext.core.Element} The Element
15587 switchOff: function(obj) {
15591 obj = Ext.applyIf(obj || {}, {
15598 beforeAnim = function() {
15599 var animScope = this,
15600 size = me.getSize(),
15602 keyframe, position;
15605 position = me.getPositioning();
15607 keyframe = Ext.create('Ext.fx.Animator', {
15609 duration: obj.duration,
15610 easing: obj.easing,
15617 y: xy[1] + size.height / 2
15621 x: xy[0] + size.width / 2
15625 keyframe.on('afteranimate', function() {
15626 if (obj.useDisplay) {
15627 me.setDisplayed(false);
15632 me.setPositioning(position);
15638 duration: (obj.duration * 2),
15649 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
15652 // default: a single light blue ripple
15655 // custom: 3 red ripples lasting 3 seconds total
15656 el.frame("#ff0000", 3, { duration: 3 });
15658 // common config options shown with default values
15659 el.frame("#C3DAF9", 1, {
15660 duration: 1 //duration of each individual ripple.
15661 // Note: Easing is not configurable and will be ignored if included
15664 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
15665 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
15666 * @param {Object} options (optional) Object literal with any of the Fx config options
15667 * @return {Ext.core.Element} The Element
15669 frame : function(color, count, obj){
15673 color = color || '#C3DAF9';
15674 count = count || 1;
15677 beforeAnim = function() {
15679 var animScope = this,
15681 proxy = Ext.getBody().createChild({
15683 position : 'absolute',
15684 'pointer-events': 'none',
15686 border : '0px solid ' + color
15690 proxyAnim = Ext.create('Ext.fx.Anim', {
15692 duration: obj.duration || 1000,
15699 height: box.height,
15707 height: box.height + 40,
15708 width: box.width + 40
15711 proxyAnim.on('afteranimate', function() {
15718 duration: (obj.duration * 2) || 2000,
15729 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
15730 * ending point of the effect.
15733 // default: slide the element downward while fading out
15736 // custom: slide the element out to the right with a 2-second duration
15737 el.ghost('r', { duration: 2 });
15739 // common config options shown with default values
15745 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
15746 * @param {Object} options (optional) Object literal with any of the Fx config options
15747 * @return {Ext.core.Element} The Element
15749 ghost: function(anchor, obj) {
15753 anchor = anchor || "b";
15754 beforeAnim = function() {
15755 var width = me.getWidth(),
15756 height = me.getHeight(),
15758 position = me.getPositioning(),
15764 to.y = xy[1] - height;
15767 to.x = xy[0] - width;
15770 to.x = xy[0] + width;
15773 to.y = xy[1] + height;
15776 to.x = xy[0] - width;
15777 to.y = xy[1] - height;
15780 to.x = xy[0] - width;
15781 to.y = xy[1] + height;
15784 to.x = xy[0] + width;
15785 to.y = xy[1] + height;
15788 to.x = xy[0] + width;
15789 to.y = xy[1] - height;
15793 this.on('afteranimate', function () {
15797 me.setPositioning(position);
15802 me.animate(Ext.applyIf(obj || {}, {
15804 easing: 'ease-out',
15815 * Highlights the Element by setting a color (applies to the background-color by default, but can be
15816 * changed using the "attr" config option) and then fading back to the original color. If no original
15817 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
15820 // default: highlight background to yellow
15823 // custom: highlight foreground text to blue for 2 seconds
15824 el.highlight("0000ff", { attr: 'color', duration: 2 });
15826 // common config options shown with default values
15827 el.highlight("ffff9c", {
15828 attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
15829 endColor: (current color) or "ffffff",
15834 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
15835 * @param {Object} options (optional) Object literal with any of the Fx config options
15836 * @return {Ext.core.Element} The Element
15838 highlight: function(color, o) {
15842 restore, to, attr, lns, event, fn;
15845 lns = o.listeners || {};
15846 attr = o.attr || 'backgroundColor';
15847 from[attr] = color || 'ffff9c';
15851 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
15857 // Don't apply directly on lns, since we reference it in our own callbacks below
15858 o.listeners = Ext.apply(Ext.apply({}, lns), {
15859 beforeanimate: function() {
15860 restore = dom.style[attr];
15864 event = lns.beforeanimate;
15866 fn = event.fn || event;
15867 return fn.apply(event.scope || lns.scope || window, arguments);
15870 afteranimate: function() {
15872 dom.style[attr] = restore;
15875 event = lns.afteranimate;
15877 fn = event.fn || event;
15878 fn.apply(event.scope || lns.scope || window, arguments);
15883 me.animate(Ext.apply({}, o, {
15894 * Creates a pause before any subsequent queued effects begin. If there are
15895 * no effects queued after the pause it will have no effect.
15900 * @param {Number} seconds The length of time to pause (in seconds)
15901 * @return {Ext.Element} The Element
15903 pause: function(ms) {
15905 Ext.fx.Manager.setFxDefaults(me.id, {
15912 * Fade an element in (from transparent to opaque). The ending opacity can be specified
15913 * using the <tt>{@link #endOpacity}</tt> config option.
15916 // default: fade in from opacity 0 to 100%
15919 // custom: fade in from opacity 0 to 75% over 2 seconds
15920 el.fadeIn({ endOpacity: .75, duration: 2});
15922 // common config options shown with default values
15924 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
15929 * @param {Object} options (optional) Object literal with any of the Fx config options
15930 * @return {Ext.Element} The Element
15932 fadeIn: function(o) {
15933 this.animate(Ext.apply({}, o, {
15940 * Fade an element out (from opaque to transparent). The ending opacity can be specified
15941 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
15942 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
15945 // default: fade out from the element's current opacity to 0
15948 // custom: fade out from the element's current opacity to 25% over 2 seconds
15949 el.fadeOut({ endOpacity: .25, duration: 2});
15951 // common config options shown with default values
15953 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
15960 * @param {Object} options (optional) Object literal with any of the Fx config options
15961 * @return {Ext.Element} The Element
15963 fadeOut: function(o) {
15964 this.animate(Ext.apply({}, o, {
15972 * Animates the transition of an element's dimensions from a starting height/width
15973 * to an ending height/width. This method is a convenience implementation of {@link #shift}.
15976 // change height and width to 100x100 pixels
15977 el.scale(100, 100);
15979 // common config options shown with default values. The height and width will default to
15980 // the element's existing values if passed as null.
15982 [element's width],
15983 [element's height], {
15989 * @param {Number} width The new width (pass undefined to keep the original width)
15990 * @param {Number} height The new height (pass undefined to keep the original height)
15991 * @param {Object} options (optional) Object literal with any of the Fx config options
15992 * @return {Ext.Element} The Element
15994 scale: function(w, h, o) {
15995 this.animate(Ext.apply({}, o, {
16004 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
16005 * Any of these properties not specified in the config object will not be changed. This effect
16006 * requires that at least one new dimension, position or opacity setting must be passed in on
16007 * the config object in order for the function to have any effect.
16010 // slide the element horizontally to x position 200 while changing the height and opacity
16011 el.shift({ x: 200, height: 50, opacity: .8 });
16013 // common config options shown with default values.
16015 width: [element's width],
16016 height: [element's height],
16017 x: [element's x position],
16018 y: [element's y position],
16019 opacity: [element's opacity],
16024 * @param {Object} options Object literal with any of the Fx config options
16025 * @return {Ext.Element} The Element
16027 shift: function(config) {
16028 this.animate(config);
16034 * @class Ext.core.Element
16036 Ext.applyIf(Ext.core.Element, {
16037 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
16038 camelRe: /(-[a-z])/gi,
16039 opacityRe: /alpha\(opacity=(.*)\)/i,
16040 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
16042 defaultUnit : "px",
16043 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
16044 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
16045 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
16047 // Reference the prototype's version of the method. Signatures are identical.
16048 addUnits : Ext.core.Element.prototype.addUnits,
16051 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16052 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16054 * @param {Number|String} box The encoded margins
16055 * @return {Object} An object with margin sizes for top, right, bottom and left
16057 parseBox : function(box) {
16058 if (Ext.isObject(box)) {
16061 right: box.right || 0,
16062 bottom: box.bottom || 0,
16063 left: box.left || 0
16066 if (typeof box != 'string') {
16067 box = box.toString();
16069 var parts = box.split(' '),
16073 parts[1] = parts[2] = parts[3] = parts[0];
16075 else if (ln == 2) {
16076 parts[2] = parts[0];
16077 parts[3] = parts[1];
16079 else if (ln == 3) {
16080 parts[3] = parts[1];
16084 top :parseFloat(parts[0]) || 0,
16085 right :parseFloat(parts[1]) || 0,
16086 bottom:parseFloat(parts[2]) || 0,
16087 left :parseFloat(parts[3]) || 0
16094 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
16095 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
16097 * @param {Number|String} box The encoded margins
16098 * @param {String} units The type of units to add
16099 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
16101 unitizeBox : function(box, units) {
16102 var A = this.addUnits,
16103 B = this.parseBox(box);
16105 return A(B.top, units) + ' ' +
16106 A(B.right, units) + ' ' +
16107 A(B.bottom, units) + ' ' +
16113 camelReplaceFn : function(m, a) {
16114 return a.charAt(1).toUpperCase();
16118 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
16121 * <li>border-width -> borderWidth</li>
16122 * <li>padding-top -> paddingTop</li>
16125 * @param {String} prop The property to normalize
16126 * @return {String} The normalized string
16128 normalize : function(prop) {
16129 if (prop == 'float') {
16130 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
16132 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
16136 * Retrieves the document height
16138 * @return {Number} documentHeight
16140 getDocumentHeight: function() {
16141 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
16145 * Retrieves the document width
16147 * @return {Number} documentWidth
16149 getDocumentWidth: function() {
16150 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
16154 * Retrieves the viewport height of the window.
16156 * @return {Number} viewportHeight
16158 getViewportHeight: function(){
16159 return window.innerHeight;
16163 * Retrieves the viewport width of the window.
16165 * @return {Number} viewportWidth
16167 getViewportWidth : function() {
16168 return window.innerWidth;
16172 * Retrieves the viewport size of the window.
16174 * @return {Object} object containing width and height properties
16176 getViewSize : function() {
16178 width: window.innerWidth,
16179 height: window.innerHeight
16184 * Retrieves the current orientation of the window. This is calculated by
16185 * determing if the height is greater than the width.
16187 * @return {String} Orientation of window: 'portrait' or 'landscape'
16189 getOrientation : function() {
16190 if (Ext.supports.OrientationChange) {
16191 return (window.orientation == 0) ? 'portrait' : 'landscape';
16194 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
16198 * Returns the top Element that is located at the passed coordinates
16200 * @param {Number} x The x coordinate
16201 * @param {Number} x The y coordinate
16202 * @return {String} The found Element
16204 fromPoint: function(x, y) {
16205 return Ext.get(document.elementFromPoint(x, y));
16209 * Converts a CSS string into an object with a property for each style.
16211 * The sample code below would return an object with 2 properties, one
16212 * for background-color and one for color.</p>
16214 var css = 'background-color: red;color: blue; ';
16215 console.log(Ext.core.Element.parseStyles(css));
16218 * @param {String} styles A CSS string
16219 * @return {Object} styles
16221 parseStyles: function(styles){
16223 cssRe = this.cssRe,
16227 // Since we're using the g flag on the regex, we need to set the lastIndex.
16228 // This automatically happens on some implementations, but not others, see:
16229 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
16230 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
16231 cssRe.lastIndex = 0;
16232 while ((matches = cssRe.exec(styles))) {
16233 out[matches[1]] = matches[2];
16241 * @class Ext.CompositeElementLite
16242 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
16243 * members, or to perform collective actions upon the whole set.</p>
16244 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
16245 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
16246 * Example:<pre><code>
16247 var els = Ext.select("#some-el div.some-class");
16248 // or select directly from an existing element
16249 var el = Ext.get('some-el');
16250 el.select('div.some-class');
16252 els.setWidth(100); // all elements become 100 width
16253 els.hide(true); // all elements fade out and hide
16255 els.setWidth(100).hide(true);
16258 Ext.CompositeElementLite = function(els, root){
16260 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
16261 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
16262 * to augment the capabilities of the CompositeElementLite class may use it when adding
16263 * methods to the class.</p>
16264 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
16265 * following siblings of selected elements, the code would be</p><code><pre>
16266 Ext.override(Ext.CompositeElementLite, {
16267 nextAll: function() {
16268 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
16270 // Loop through all elements in this Composite, accumulating
16271 // an Array of all siblings.
16272 for (i = 0; i < l; i++) {
16273 for (n = els[i].nextSibling; n; n = n.nextSibling) {
16278 // Add all found siblings to this Composite
16279 return this.add(r);
16283 * @property elements
16285 this.elements = [];
16286 this.add(els, root);
16287 this.el = new Ext.core.Element.Flyweight();
16290 Ext.CompositeElementLite.prototype = {
16294 getElement : function(el){
16295 // Set the shared flyweight dom property to the current element
16303 transformElement : function(el){
16304 return Ext.getDom(el);
16308 * Returns the number of elements in this Composite.
16311 getCount : function(){
16312 return this.elements.length;
16315 * Adds elements to this Composite object.
16316 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
16317 * @return {CompositeElement} This Composite object.
16319 add : function(els, root){
16321 elements = me.elements;
16325 if(typeof els == "string"){
16326 els = Ext.core.Element.selectorFunction(els, root);
16327 }else if(els.isComposite){
16328 els = els.elements;
16329 }else if(!Ext.isIterable(els)){
16333 for(var i = 0, len = els.length; i < len; ++i){
16334 elements.push(me.transformElement(els[i]));
16339 invoke : function(fn, args){
16346 for(i = 0; i < len; i++) {
16349 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
16355 * Returns a flyweight Element of the dom element object at the specified index
16356 * @param {Number} index
16357 * @return {Ext.core.Element}
16359 item : function(index){
16361 el = me.elements[index],
16365 out = me.getElement(el);
16370 // fixes scope with flyweight
16371 addListener : function(eventName, handler, scope, opt){
16372 var els = this.elements,
16376 for(i = 0; i<len; i++) {
16379 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
16385 * <p>Calls the passed function for each element in this composite.</p>
16386 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
16387 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
16388 * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
16389 * a reference to the dom node, use el.dom.</b></div></li>
16390 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
16391 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
16393 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
16394 * @return {CompositeElement} this
16396 each : function(fn, scope){
16402 for(i = 0; i<len; i++) {
16405 e = this.getElement(e);
16406 if(fn.call(scope || e, e, me, i) === false){
16415 * Clears this Composite and adds the elements passed.
16416 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
16417 * @return {CompositeElement} this
16419 fill : function(els){
16427 * Filters this composite to only elements that match the passed selector.
16428 * @param {String/Function} selector A string CSS selector or a comparison function.
16429 * The comparison function will be called with the following arguments:<ul>
16430 * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
16431 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
16433 * @return {CompositeElement} this
16435 filter : function(selector){
16438 fn = Ext.isFunction(selector) ? selector
16440 return el.is(selector);
16443 me.each(function(el, self, i) {
16444 if (fn(el, i) !== false) {
16445 els[els.length] = me.transformElement(el);
16454 * Find the index of the passed element within the composite collection.
16455 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
16456 * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
16458 indexOf : function(el){
16459 return Ext.Array.indexOf(this.elements, this.transformElement(el));
16463 * Replaces the specified element with the passed element.
16464 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
16466 * @param {Mixed} replacement The id of an element or the Element itself.
16467 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
16468 * @return {CompositeElement} this
16470 replaceElement : function(el, replacement, domReplace){
16471 var index = !isNaN(el) ? el : this.indexOf(el),
16474 replacement = Ext.getDom(replacement);
16476 d = this.elements[index];
16477 d.parentNode.insertBefore(replacement, d);
16480 Ext.Array.splice(this.elements, index, 1, replacement);
16486 * Removes all elements.
16488 clear : function(){
16489 this.elements = [];
16493 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
16497 * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
16498 * This is called twice - once immediately below, and once again after additional Ext.core.Element
16499 * are added in Ext JS
16501 Ext.CompositeElementLite.importElementMethods = function() {
16503 ElProto = Ext.core.Element.prototype,
16504 CelProto = Ext.CompositeElementLite.prototype;
16506 for (fnName in ElProto) {
16507 if (typeof ElProto[fnName] == 'function'){
16508 (function(fnName) {
16509 CelProto[fnName] = CelProto[fnName] || function() {
16510 return this.invoke(fnName, arguments);
16512 }).call(CelProto, fnName);
16518 Ext.CompositeElementLite.importElementMethods();
16521 Ext.core.Element.selectorFunction = Ext.DomQuery.select;
16525 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
16526 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16527 * {@link Ext.CompositeElementLite CompositeElementLite} object.
16528 * @param {String/Array} selector The CSS selector or an array of elements
16529 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16530 * @return {CompositeElementLite/CompositeElement}
16531 * @member Ext.core.Element
16534 Ext.core.Element.select = function(selector, root){
16536 if(typeof selector == "string"){
16537 els = Ext.core.Element.selectorFunction(selector, root);
16538 }else if(selector.length !== undefined){
16542 sourceClass: "Ext.core.Element",
16543 sourceMethod: "select",
16544 selector: selector,
16546 msg: "Invalid selector specified: " + selector
16549 return new Ext.CompositeElementLite(els);
16552 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
16553 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
16554 * {@link Ext.CompositeElementLite CompositeElementLite} object.
16555 * @param {String/Array} selector The CSS selector or an array of elements
16556 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
16557 * @return {CompositeElementLite/CompositeElement}
16561 Ext.select = Ext.core.Element.select;
16564 * @class Ext.util.DelayedTask
16566 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
16567 * performing setTimeout where a new timeout cancels the old timeout. When called, the
16568 * task will wait the specified time period before executing. If durng that time period,
16569 * the task is called again, the original call will be cancelled. This continues so that
16570 * the function is only called a single time for each iteration.
16572 * This method is especially useful for things like detecting whether a user has finished
16573 * typing in a text field. An example would be performing validation on a keypress. You can
16574 * use this class to buffer the keypress events for a certain number of milliseconds, and
16575 * perform only if they stop for that amount of time.
16579 * var task = new Ext.util.DelayedTask(function(){
16580 * alert(Ext.getDom('myInputField').value.length);
16583 * // Wait 500ms before calling our function. If the user presses another key
16584 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
16585 * Ext.get('myInputField').on('keypress', function(){
16586 * task.{@link #delay}(500);
16589 * Note that we are using a DelayedTask here to illustrate a point. The configuration
16590 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
16591 * also setup a delayed task for you to buffer events.
16593 * @constructor The parameters to this constructor serve as defaults and are not required.
16594 * @param {Function} fn (optional) The default function to call.
16595 * @param {Object} scope (optional) The default scope (The <code><b>this</b></code> reference) in which the
16596 * function is called. If not specified, <code>this</code> will refer to the browser window.
16597 * @param {Array} args (optional) The default Array of arguments.
16599 Ext.util.DelayedTask = function(fn, scope, args) {
16602 call = function() {
16605 fn.apply(scope, args || []);
16609 * Cancels any pending timeout and queues a new one
16610 * @param {Number} delay The milliseconds to delay
16611 * @param {Function} newFn (optional) Overrides function passed to constructor
16612 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
16613 * is specified, <code>this</code> will refer to the browser window.
16614 * @param {Array} newArgs (optional) Overrides args passed to constructor
16616 this.delay = function(delay, newFn, newScope, newArgs) {
16619 scope = newScope || scope;
16620 args = newArgs || args;
16621 id = setInterval(call, delay);
16625 * Cancel the last queued timeout
16627 this.cancel = function(){
16634 Ext.require('Ext.util.DelayedTask', function() {
16636 Ext.util.Event = Ext.extend(Object, (function() {
16637 function createBuffered(handler, listener, o, scope) {
16638 listener.task = new Ext.util.DelayedTask();
16639 return function() {
16640 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
16644 function createDelayed(handler, listener, o, scope) {
16645 return function() {
16646 var task = new Ext.util.DelayedTask();
16647 if (!listener.tasks) {
16648 listener.tasks = [];
16650 listener.tasks.push(task);
16651 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
16655 function createSingle(handler, listener, o, scope) {
16656 return function() {
16657 listener.ev.removeListener(listener.fn, scope);
16658 return handler.apply(scope, arguments);
16665 constructor: function(observable, name) {
16667 this.observable = observable;
16668 this.listeners = [];
16671 addListener: function(fn, scope, options) {
16674 scope = scope || me.observable;
16678 sourceClass: Ext.getClassName(this.observable),
16679 sourceMethod: "addListener",
16680 msg: "The specified callback function is undefined"
16684 if (!me.isListening(fn, scope)) {
16685 listener = me.createListener(fn, scope, options);
16687 // if we are currently firing this event, don't disturb the listener loop
16688 me.listeners = me.listeners.slice(0);
16690 me.listeners.push(listener);
16694 createListener: function(fn, scope, o) {
16696 scope = scope || this.observable;
16706 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
16707 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
16709 handler = createSingle(handler, listener, o, scope);
16712 handler = createDelayed(handler, listener, o, scope);
16715 handler = createBuffered(handler, listener, o, scope);
16718 listener.fireFn = handler;
16722 findListener: function(fn, scope) {
16723 var listeners = this.listeners,
16724 i = listeners.length,
16729 listener = listeners[i];
16731 s = listener.scope;
16732 if (listener.fn == fn && (s == scope || s == this.observable)) {
16741 isListening: function(fn, scope) {
16742 return this.findListener(fn, scope) !== -1;
16745 removeListener: function(fn, scope) {
16750 index = me.findListener(fn, scope);
16752 listener = me.listeners[index];
16755 me.listeners = me.listeners.slice(0);
16758 // cancel and remove a buffered handler that hasn't fired yet
16759 if (listener.task) {
16760 listener.task.cancel();
16761 delete listener.task;
16764 // cancel and remove all delayed handlers that haven't fired yet
16765 k = listener.tasks && listener.tasks.length;
16768 listener.tasks[k].cancel();
16770 delete listener.tasks;
16773 // remove this listener from the listeners array
16774 Ext.Array.erase(me.listeners, index, 1);
16781 // Iterate to stop any buffered/delayed events
16782 clearListeners: function() {
16783 var listeners = this.listeners,
16784 i = listeners.length;
16787 this.removeListener(listeners[i].fn, listeners[i].scope);
16793 listeners = me.listeners,
16794 count = listeners.length,
16801 for (i = 0; i < count; i++) {
16802 listener = listeners[i];
16803 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
16805 args.push(listener.o);
16807 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
16808 return (me.firing = false);
16820 * @class Ext.EventManager
16821 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
16822 * several useful events directly.
16823 * See {@link Ext.EventObject} for more details on normalized event objects.
16826 Ext.EventManager = {
16828 // --------------------- onReady ---------------------
16831 * Check if we have bound our global onReady listener
16834 hasBoundOnReady: false,
16837 * Check if fireDocReady has been called
16840 hasFiredReady: false,
16843 * Timer for the document ready event in old IE versions
16846 readyTimeout: null,
16849 * Checks if we have bound an onreadystatechange event
16852 hasOnReadyStateChange: false,
16855 * Holds references to any onReady functions
16858 readyEvent: new Ext.util.Event(),
16861 * Check the ready state for old IE versions
16863 * @return {Boolean} True if the document is ready
16865 checkReadyState: function(){
16866 var me = Ext.EventManager;
16868 if(window.attachEvent){
16869 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
16870 if (window != top) {
16874 document.documentElement.doScroll('left');
16881 if (document.readyState == 'complete') {
16885 me.readyTimeout = setTimeout(arguments.callee, 2);
16890 * Binds the appropriate browser event for checking if the DOM has loaded.
16893 bindReadyEvent: function(){
16894 var me = Ext.EventManager;
16895 if (me.hasBoundOnReady) {
16899 if (document.addEventListener) {
16900 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
16901 // fallback, load will ~always~ fire
16902 window.addEventListener('load', me.fireDocReady, false);
16904 // check if the document is ready, this will also kick off the scroll checking timer
16905 if (!me.checkReadyState()) {
16906 document.attachEvent('onreadystatechange', me.checkReadyState);
16907 me.hasOnReadyStateChange = true;
16909 // fallback, onload will ~always~ fire
16910 window.attachEvent('onload', me.fireDocReady, false);
16912 me.hasBoundOnReady = true;
16916 * We know the document is loaded, so trigger any onReady events.
16919 fireDocReady: function(){
16920 var me = Ext.EventManager;
16922 // only unbind these events once
16923 if (!me.hasFiredReady) {
16924 me.hasFiredReady = true;
16926 if (document.addEventListener) {
16927 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
16928 window.removeEventListener('load', me.fireDocReady, false);
16930 if (me.readyTimeout !== null) {
16931 clearTimeout(me.readyTimeout);
16933 if (me.hasOnReadyStateChange) {
16934 document.detachEvent('onreadystatechange', me.checkReadyState);
16936 window.detachEvent('onload', me.fireDocReady);
16938 Ext.supports.init();
16940 if (!Ext.isReady) {
16941 Ext.isReady = true;
16942 me.onWindowUnload();
16943 me.readyEvent.fire();
16948 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
16949 * accessed shorthanded as Ext.onReady().
16950 * @param {Function} fn The method the event invokes.
16951 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
16952 * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
16954 onDocumentReady: function(fn, scope, options){
16955 options = options || {};
16956 var me = Ext.EventManager,
16957 readyEvent = me.readyEvent;
16959 // force single to be true so our event is only ever fired once.
16960 options.single = true;
16962 // Document already loaded, let's just fire it
16964 readyEvent.addListener(fn, scope, options);
16967 options.delay = options.delay || 1;
16968 readyEvent.addListener(fn, scope, options);
16969 me.bindReadyEvent();
16974 // --------------------- event binding ---------------------
16977 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
16980 stoppedMouseDownEvent: new Ext.util.Event(),
16983 * Options to parse for the 4th argument to addListener.
16986 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
16989 * Get the id of the element. If one has not been assigned, automatically assign it.
16990 * @param {Mixed} element The element to get the id for.
16991 * @return {String} id
16993 getId : function(element) {
16994 var skipGarbageCollection = false,
16997 element = Ext.getDom(element);
16999 if (element === document || element === window) {
17000 id = element === document ? Ext.documentId : Ext.windowId;
17003 id = Ext.id(element);
17005 // skip garbage collection for special elements (window, document, iframes)
17006 if (element && (element.getElementById || element.navigator)) {
17007 skipGarbageCollection = true;
17010 if (!Ext.cache[id]){
17011 Ext.core.Element.addToCache(new Ext.core.Element(element), id);
17012 if (skipGarbageCollection) {
17013 Ext.cache[id].skipGarbageCollection = true;
17020 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
17022 * @param {Object} element The element the event is for
17023 * @param {Object} event The event configuration
17024 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
17026 prepareListenerConfig: function(element, config, isRemove){
17028 propRe = me.propRe,
17031 // loop over all the keys in the object
17032 for (key in config) {
17033 if (config.hasOwnProperty(key)) {
17034 // if the key is something else then an event option
17035 if (!propRe.test(key)) {
17036 value = config[key];
17037 // if the value is a function it must be something like click: function(){}, scope: this
17038 // which means that there might be multiple event listeners with shared options
17039 if (Ext.isFunction(value)) {
17041 args = [element, key, value, config.scope, config];
17043 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
17044 args = [element, key, value.fn, value.scope, value];
17047 if (isRemove === true) {
17048 me.removeListener.apply(this, args);
17050 me.addListener.apply(me, args);
17058 * Normalize cross browser event differences
17060 * @param {Object} eventName The event name
17061 * @param {Object} fn The function to execute
17062 * @return {Object} The new event name/function
17064 normalizeEvent: function(eventName, fn){
17065 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
17067 fn = Ext.Function.createInterceptor(fn, this.contains, this);
17069 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
17070 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
17071 eventName = 'DOMMouseScroll';
17074 eventName: eventName,
17080 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
17082 * @param {Object} event
17084 contains: function(event){
17085 var parent = event.browserEvent.currentTarget,
17086 child = this.getRelatedTarget(event);
17088 if (parent && parent.firstChild) {
17090 if (child === parent) {
17093 child = child.parentNode;
17094 if (child && (child.nodeType != 1)) {
17103 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
17104 * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
17105 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
17106 * @param {String} eventName The name of the event to listen for.
17107 * @param {Function} handler The handler function the event invokes. This function is passed
17108 * the following parameters:<ul>
17109 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
17110 * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
17111 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
17112 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
17114 * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
17115 * @param {Object} options (optional) An object containing handler configuration properties.
17116 * This may contain any of the following properties:<ul>
17117 * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
17118 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
17119 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
17120 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
17121 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
17122 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
17123 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
17124 * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
17125 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
17126 * by the specified number of milliseconds. If the event fires again within that time, the original
17127 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
17128 * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
17130 * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
17132 addListener: function(element, eventName, fn, scope, options){
17133 // Check if we've been passed a "config style" event.
17134 if (typeof eventName !== 'string') {
17135 this.prepareListenerConfig(element, eventName);
17139 var dom = Ext.getDom(element),
17145 sourceClass: 'Ext.EventManager',
17146 sourceMethod: 'addListener',
17147 targetElement: element,
17148 eventName: eventName,
17149 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
17154 sourceClass: 'Ext.EventManager',
17155 sourceMethod: 'addListener',
17156 targetElement: element,
17157 eventName: eventName,
17158 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
17162 // create the wrapper function
17163 options = options || {};
17165 bind = this.normalizeEvent(eventName, fn);
17166 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
17169 if (dom.attachEvent) {
17170 dom.attachEvent('on' + bind.eventName, wrap);
17172 dom.addEventListener(bind.eventName, wrap, options.capture || false);
17175 if (dom == document && eventName == 'mousedown') {
17176 this.stoppedMouseDownEvent.addListener(wrap);
17179 // add all required data into the event cache
17180 this.getEventListenerCache(dom, eventName).push({
17188 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
17189 * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
17190 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
17191 * @param {String} eventName The name of the event.
17192 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
17193 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
17194 * then this must refer to the same object.
17196 removeListener : function(element, eventName, fn, scope) {
17197 // handle our listener config object syntax
17198 if (typeof eventName !== 'string') {
17199 this.prepareListenerConfig(element, eventName, true);
17203 var dom = Ext.getDom(element),
17204 cache = this.getEventListenerCache(dom, eventName),
17205 bindName = this.normalizeEvent(eventName).eventName,
17206 i = cache.length, j,
17207 listener, wrap, tasks;
17211 listener = cache[i];
17213 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
17214 wrap = listener.wrap;
17216 // clear buffered calls
17218 clearTimeout(wrap.task);
17222 // clear delayed calls
17223 j = wrap.tasks && wrap.tasks.length;
17226 clearTimeout(wrap.tasks[j]);
17231 if (dom.detachEvent) {
17232 dom.detachEvent('on' + bindName, wrap);
17234 dom.removeEventListener(bindName, wrap, false);
17237 if (wrap && dom == document && eventName == 'mousedown') {
17238 this.stoppedMouseDownEvent.removeListener(wrap);
17241 // remove listener from cache
17242 Ext.Array.erase(cache, i, 1);
17248 * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners}
17249 * directly on an Element in favor of calling this version.
17250 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17252 removeAll : function(element){
17253 var dom = Ext.getDom(element),
17258 cache = this.getElementEventCache(dom);
17260 for (ev in cache) {
17261 if (cache.hasOwnProperty(ev)) {
17262 this.removeListener(dom, ev);
17265 Ext.cache[dom.id].events = {};
17269 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
17270 * directly on an Element in favor of calling this version.
17271 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
17272 * @param {String} eventName (optional) The name of the event.
17274 purgeElement : function(element, eventName) {
17275 var dom = Ext.getDom(element),
17279 this.removeListener(dom, eventName);
17282 this.removeAll(dom);
17285 if(dom && dom.childNodes) {
17286 for(len = element.childNodes.length; i < len; i++) {
17287 this.purgeElement(element.childNodes[i], eventName);
17293 * Create the wrapper function for the event
17295 * @param {HTMLElement} dom The dom element
17296 * @param {String} ename The event name
17297 * @param {Function} fn The function to execute
17298 * @param {Object} scope The scope to execute callback in
17299 * @param {Object} options The options
17300 * @return {Function} the wrapper function
17302 createListenerWrap : function(dom, ename, fn, scope, options) {
17303 options = options || {};
17307 return function wrap(e, args) {
17308 // Compile the implementation upon first firing
17310 f = ['if(!Ext) {return;}'];
17312 if(options.buffer || options.delay || options.freezeEvent) {
17313 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
17315 f.push('e = Ext.EventObject.setEvent(e);');
17318 if (options.delegate) {
17319 f.push('var t = e.getTarget("' + options.delegate + '", this);');
17320 f.push('if(!t) {return;}');
17322 f.push('var t = e.target;');
17325 if (options.target) {
17326 f.push('if(e.target !== options.target) {return;}');
17329 if(options.stopEvent) {
17330 f.push('e.stopEvent();');
17332 if(options.preventDefault) {
17333 f.push('e.preventDefault();');
17335 if(options.stopPropagation) {
17336 f.push('e.stopPropagation();');
17340 if(options.normalized === false) {
17341 f.push('e = e.browserEvent;');
17344 if(options.buffer) {
17345 f.push('(wrap.task && clearTimeout(wrap.task));');
17346 f.push('wrap.task = setTimeout(function(){');
17349 if(options.delay) {
17350 f.push('wrap.tasks = wrap.tasks || [];');
17351 f.push('wrap.tasks.push(setTimeout(function(){');
17354 // finally call the actual handler fn
17355 f.push('fn.call(scope || dom, e, t, options);');
17357 if(options.single) {
17358 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
17361 if(options.delay) {
17362 f.push('}, ' + options.delay + '));');
17365 if(options.buffer) {
17366 f.push('}, ' + options.buffer + ');');
17369 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
17372 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
17377 * Get the event cache for a particular element for a particular event
17379 * @param {HTMLElement} element The element
17380 * @param {Object} eventName The event name
17381 * @return {Array} The events for the element
17383 getEventListenerCache : function(element, eventName) {
17388 var eventCache = this.getElementEventCache(element);
17389 return eventCache[eventName] || (eventCache[eventName] = []);
17393 * Gets the event cache for the object
17395 * @param {HTMLElement} element The element
17396 * @return {Object} The event cache for the object
17398 getElementEventCache : function(element) {
17402 var elementCache = Ext.cache[this.getId(element)];
17403 return elementCache.events || (elementCache.events = {});
17406 // --------------------- utility methods ---------------------
17407 mouseLeaveRe: /(mouseout|mouseleave)/,
17408 mouseEnterRe: /(mouseover|mouseenter)/,
17411 * Stop the event (preventDefault and stopPropagation)
17412 * @param {Event} The event to stop
17414 stopEvent: function(event) {
17415 this.stopPropagation(event);
17416 this.preventDefault(event);
17420 * Cancels bubbling of the event.
17421 * @param {Event} The event to stop bubbling.
17423 stopPropagation: function(event) {
17424 event = event.browserEvent || event;
17425 if (event.stopPropagation) {
17426 event.stopPropagation();
17428 event.cancelBubble = true;
17433 * Prevents the browsers default handling of the event.
17434 * @param {Event} The event to prevent the default
17436 preventDefault: function(event) {
17437 event = event.browserEvent || event;
17438 if (event.preventDefault) {
17439 event.preventDefault();
17441 event.returnValue = false;
17442 // Some keys events require setting the keyCode to -1 to be prevented
17444 // all ctrl + X and F1 -> F12
17445 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
17446 event.keyCode = -1;
17449 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
17455 * Gets the related target from the event.
17456 * @param {Object} event The event
17457 * @return {HTMLElement} The related target.
17459 getRelatedTarget: function(event) {
17460 event = event.browserEvent || event;
17461 var target = event.relatedTarget;
17463 if (this.mouseLeaveRe.test(event.type)) {
17464 target = event.toElement;
17465 } else if (this.mouseEnterRe.test(event.type)) {
17466 target = event.fromElement;
17469 return this.resolveTextNode(target);
17473 * Gets the x coordinate from the event
17474 * @param {Object} event The event
17475 * @return {Number} The x coordinate
17477 getPageX: function(event) {
17478 return this.getXY(event)[0];
17482 * Gets the y coordinate from the event
17483 * @param {Object} event The event
17484 * @return {Number} The y coordinate
17486 getPageY: function(event) {
17487 return this.getXY(event)[1];
17491 * Gets the x & ycoordinate from the event
17492 * @param {Object} event The event
17493 * @return {Array} The x/y coordinate
17495 getPageXY: function(event) {
17496 event = event.browserEvent || event;
17497 var x = event.pageX,
17499 doc = document.documentElement,
17500 body = document.body;
17502 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
17503 if (!x && x !== 0) {
17504 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
17505 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
17511 * Gets the target of the event.
17512 * @param {Object} event The event
17513 * @return {HTMLElement} target
17515 getTarget: function(event) {
17516 event = event.browserEvent || event;
17517 return this.resolveTextNode(event.target || event.srcElement);
17521 * Resolve any text nodes accounting for browser differences.
17523 * @param {HTMLElement} node The node
17524 * @return {HTMLElement} The resolved node
17526 // technically no need to browser sniff this, however it makes no sense to check this every time, for every event, whether the string is equal.
17527 resolveTextNode: Ext.isGecko ?
17532 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
17533 var s = HTMLElement.prototype.toString.call(node);
17534 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
17537 return node.nodeType == 3 ? node.parentNode: node;
17538 }: function(node) {
17539 return node && node.nodeType == 3 ? node.parentNode: node;
17542 // --------------------- custom event binding ---------------------
17544 // Keep track of the current width/height
17549 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
17550 * passes new viewport width and height to handlers.
17551 * @param {Function} fn The handler function the window resize event invokes.
17552 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
17553 * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener}
17555 onWindowResize: function(fn, scope, options){
17556 var resize = this.resizeEvent;
17558 this.resizeEvent = resize = new Ext.util.Event();
17559 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
17561 resize.addListener(fn, scope, options);
17565 * Fire the resize event.
17568 fireResize: function(){
17570 w = Ext.core.Element.getViewWidth(),
17571 h = Ext.core.Element.getViewHeight();
17573 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
17574 if(me.curHeight != h || me.curWidth != w){
17577 me.resizeEvent.fire(w, h);
17582 * Removes the passed window resize listener.
17583 * @param {Function} fn The method the event invokes
17584 * @param {Object} scope The scope of handler
17586 removeResizeListener: function(fn, scope){
17587 if (this.resizeEvent) {
17588 this.resizeEvent.removeListener(fn, scope);
17592 onWindowUnload: function() {
17593 var unload = this.unloadEvent;
17595 this.unloadEvent = unload = new Ext.util.Event();
17596 this.addListener(window, 'unload', this.fireUnload, this);
17601 * Fires the unload event for items bound with onWindowUnload
17604 fireUnload: function() {
17605 // wrap in a try catch, could have some problems during unload
17607 this.removeUnloadListener();
17608 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
17609 if (Ext.isGecko3) {
17610 var gridviews = Ext.ComponentQuery.query('gridview'),
17612 ln = gridviews.length;
17613 for (; i < ln; i++) {
17614 gridviews[i].scrollToTop();
17617 // Purge all elements in the cache
17620 for (el in cache) {
17621 if (cache.hasOwnProperty(el)) {
17622 Ext.EventManager.removeAll(el);
17630 * Removes the passed window unload listener.
17631 * @param {Function} fn The method the event invokes
17632 * @param {Object} scope The scope of handler
17634 removeUnloadListener: function(){
17635 if (this.unloadEvent) {
17636 this.removeListener(window, 'unload', this.fireUnload);
17641 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
17642 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
17643 * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
17646 useKeyDown: Ext.isWebKit ?
17647 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
17648 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
17651 * Indicates which event to use for getting key presses.
17652 * @return {String} The appropriate event name.
17654 getKeyEvent: function(){
17655 return this.useKeyDown ? 'keydown' : 'keypress';
17660 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
17664 Ext.onReady = function(fn, scope, options) {
17665 Ext.Loader.onReady(fn, scope, true, options);
17669 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
17671 * @method onDocumentReady
17673 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
17676 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
17677 * @member Ext.EventManager
17680 Ext.EventManager.on = Ext.EventManager.addListener;
17683 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
17684 * @member Ext.EventManager
17687 Ext.EventManager.un = Ext.EventManager.removeListener;
17690 var initExtCss = function() {
17691 // find the body element
17692 var bd = document.body || document.getElementsByTagName('body')[0],
17693 baseCSSPrefix = Ext.baseCSSPrefix,
17694 cls = [baseCSSPrefix + 'body'],
17702 html = bd.parentNode;
17704 //Let's keep this human readable!
17706 cls.push(baseCSSPrefix + 'ie');
17709 cls.push(baseCSSPrefix + 'ie6');
17712 cls.push(baseCSSPrefix + 'ie7');
17715 cls.push(baseCSSPrefix + 'ie8');
17718 cls.push(baseCSSPrefix + 'ie9');
17721 cls.push(baseCSSPrefix + 'gecko');
17723 if (Ext.isGecko3) {
17724 cls.push(baseCSSPrefix + 'gecko3');
17726 if (Ext.isGecko4) {
17727 cls.push(baseCSSPrefix + 'gecko4');
17730 cls.push(baseCSSPrefix + 'opera');
17732 if (Ext.isWebKit) {
17733 cls.push(baseCSSPrefix + 'webkit');
17735 if (Ext.isSafari) {
17736 cls.push(baseCSSPrefix + 'safari');
17738 if (Ext.isSafari2) {
17739 cls.push(baseCSSPrefix + 'safari2');
17741 if (Ext.isSafari3) {
17742 cls.push(baseCSSPrefix + 'safari3');
17744 if (Ext.isSafari4) {
17745 cls.push(baseCSSPrefix + 'safari4');
17747 if (Ext.isChrome) {
17748 cls.push(baseCSSPrefix + 'chrome');
17751 cls.push(baseCSSPrefix + 'mac');
17754 cls.push(baseCSSPrefix + 'linux');
17756 if (!Ext.supports.CSS3BorderRadius) {
17757 cls.push(baseCSSPrefix + 'nbr');
17759 if (!Ext.supports.CSS3LinearGradient) {
17760 cls.push(baseCSSPrefix + 'nlg');
17762 if (!Ext.scopeResetCSS) {
17763 cls.push(baseCSSPrefix + 'reset');
17766 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
17768 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
17769 Ext.isBorderBox = false;
17772 Ext.isBorderBox = true;
17775 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
17776 if (!Ext.isStrict) {
17777 htmlCls.push(baseCSSPrefix + 'quirks');
17778 if (Ext.isIE && !Ext.isStrict) {
17779 Ext.isIEQuirks = true;
17782 Ext.fly(html, '_internal').addCls(htmlCls);
17785 Ext.fly(bd, '_internal').addCls(cls);
17789 Ext.onReady(initExtCss);
17793 * @class Ext.EventObject
17795 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
17796 wraps the browser's native event-object normalizing cross-browser differences,
17797 such as which mouse button is clicked, keys pressed, mechanisms to stop
17798 event-propagation along with a method to prevent default actions from taking place.
17802 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
17803 e.preventDefault();
17804 var target = e.getTarget(); // same as t (the target HTMLElement)
17808 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element}
17809 myDiv.on( // 'on' is shorthand for addListener
17810 "click", // perform an action on click of myDiv
17811 handleClick // reference to the action handler
17814 // other methods to do the same:
17815 Ext.EventManager.on("myDiv", 'click', handleClick);
17816 Ext.EventManager.addListener("myDiv", 'click', handleClick);
17821 Ext.define('Ext.EventObjectImpl', {
17822 uses: ['Ext.util.Point'],
17824 /** Key constant @type Number */
17826 /** Key constant @type Number */
17828 /** Key constant @type Number */
17830 /** Key constant @type Number */
17832 /** Key constant @type Number */
17834 /** Key constant @type Number */
17836 /** Key constant @type Number */
17838 /** Key constant @type Number */
17840 /** Key constant @type Number */
17842 /** Key constant @type Number */
17844 /** Key constant @type Number */
17846 /** Key constant @type Number */
17848 /** Key constant @type Number */
17850 /** Key constant @type Number */
17852 /** Key constant @type Number */
17854 /** Key constant @type Number */
17856 /** Key constant @type Number */
17858 /** Key constant @type Number */
17860 /** Key constant @type Number */
17862 /** Key constant @type Number */
17864 /** Key constant @type Number */
17866 /** Key constant @type Number */
17868 /** Key constant @type Number */
17870 /** Key constant @type Number */
17872 /** Key constant @type Number */
17874 /** Key constant @type Number */
17876 /** Key constant @type Number */
17878 /** Key constant @type Number */
17880 /** Key constant @type Number */
17882 /** Key constant @type Number */
17884 /** Key constant @type Number */
17886 /** Key constant @type Number */
17888 /** Key constant @type Number */
17890 /** Key constant @type Number */
17892 /** Key constant @type Number */
17894 /** Key constant @type Number */
17896 /** Key constant @type Number */
17898 /** Key constant @type Number */
17900 /** Key constant @type Number */
17902 /** Key constant @type Number */
17904 /** Key constant @type Number */
17906 /** Key constant @type Number */
17908 /** Key constant @type Number */
17910 /** Key constant @type Number */
17912 /** Key constant @type Number */
17914 /** Key constant @type Number */
17916 /** Key constant @type Number */
17918 /** Key constant @type Number */
17920 /** Key constant @type Number */
17922 /** Key constant @type Number */
17924 /** Key constant @type Number */
17926 /** Key constant @type Number */
17928 /** Key constant @type Number */
17930 /** Key constant @type Number */
17932 /** Key constant @type Number */
17934 /** Key constant @type Number */
17936 /** Key constant @type Number */
17938 /** Key constant @type Number */
17940 /** Key constant @type Number */
17942 /** Key constant @type Number */
17944 /** Key constant @type Number */
17946 /** Key constant @type Number */
17948 /** Key constant @type Number */
17950 /** Key constant @type Number */
17952 /** Key constant @type Number */
17954 /** Key constant @type Number */
17956 /** Key constant @type Number */
17958 /** Key constant @type Number */
17960 /** Key constant @type Number */
17962 /** Key constant @type Number */
17964 /** Key constant @type Number */
17966 /** Key constant @type Number */
17968 /** Key constant @type Number */
17970 /** Key constant @type Number */
17972 /** Key constant @type Number */
17974 /** Key constant @type Number */
17976 /** Key constant @type Number */
17978 /** Key constant @type Number */
17980 /** Key constant @type Number */
17982 /** Key constant @type Number */
17984 /** Key constant @type Number */
17986 /** Key constant @type Number */
17988 /** Key constant @type Number */
17990 /** Key constant @type Number */
17992 /** Key constant @type Number */
17994 /** Key constant @type Number */
17996 /** Key constant @type Number */
17999 * The mouse wheel delta scaling factor. This value depends on browser version and OS and
18000 * attempts to produce a similar scrolling experience across all platforms and browsers.
18002 * To change this value:
18004 * Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
18009 WHEEL_SCALE: (function () {
18013 // Firefox uses 3 on all platforms
18015 } else if (Ext.isMac) {
18016 // Continuous scrolling devices have momentum and produce much more scroll than
18017 // discrete devices on the same OS and browser. To make things exciting, Safari
18018 // (and not Chrome) changed from small values to 120 (like IE).
18020 if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
18021 // Safari changed the scrolling factor to match IE (for details see
18022 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
18023 // change was introduced was 532.0
18024 // Detailed discussion:
18025 // https://bugs.webkit.org/show_bug.cgi?id=29601
18026 // http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
18029 // MS optical wheel mouse produces multiples of 12 which is close enough
18030 // to help tame the speed of the continuous mice...
18034 // Momentum scrolling produces very fast scrolling, so increase the scale factor
18035 // to help produce similar results cross platform. This could be even larger and
18036 // it would help those mice, but other mice would become almost unusable as a
18037 // result (since we cannot tell which device type is in use).
18040 // IE, Opera and other Windows browsers use 120.
18048 * Simple click regex
18051 clickRe: /(dbl)?click/,
18052 // safari keypress events for special keys return bad keycodes
18056 63235: 39, // right
18059 63276: 33, // page up
18060 63277: 34, // page down
18061 63272: 46, // delete
18065 // normalize button clicks, don't see any way to feature detect this.
18066 btnMap: Ext.isIE ? {
18076 constructor: function(event, freezeEvent){
18078 this.setEvent(event.browserEvent || event, freezeEvent);
18082 setEvent: function(event, freezeEvent){
18083 var me = this, button, options;
18085 if (event == me || (event && event.browserEvent)) { // already wrapped
18088 me.browserEvent = event;
18090 // normalize buttons
18091 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
18092 if (me.clickRe.test(event.type) && button == -1) {
18098 shiftKey: event.shiftKey,
18099 // mac metaKey behaves like ctrlKey
18100 ctrlKey: event.ctrlKey || event.metaKey || false,
18101 altKey: event.altKey,
18102 // in getKey these will be normalized for the mac
18103 keyCode: event.keyCode,
18104 charCode: event.charCode,
18105 // cache the targets for the delayed and or buffered events
18106 target: Ext.EventManager.getTarget(event),
18107 relatedTarget: Ext.EventManager.getRelatedTarget(event),
18108 currentTarget: event.currentTarget,
18109 xy: (freezeEvent ? me.getXY() : null)
18123 Ext.apply(me, options);
18128 * Stop the event (preventDefault and stopPropagation)
18130 stopEvent: function(){
18131 this.stopPropagation();
18132 this.preventDefault();
18136 * Prevents the browsers default handling of the event.
18138 preventDefault: function(){
18139 if (this.browserEvent) {
18140 Ext.EventManager.preventDefault(this.browserEvent);
18145 * Cancels bubbling of the event.
18147 stopPropagation: function(){
18148 var browserEvent = this.browserEvent;
18150 if (browserEvent) {
18151 if (browserEvent.type == 'mousedown') {
18152 Ext.EventManager.stoppedMouseDownEvent.fire(this);
18154 Ext.EventManager.stopPropagation(browserEvent);
18159 * Gets the character code for the event.
18162 getCharCode: function(){
18163 return this.charCode || this.keyCode;
18167 * Returns a normalized keyCode for the event.
18168 * @return {Number} The key code
18170 getKey: function(){
18171 return this.normalizeKey(this.keyCode || this.charCode);
18175 * Normalize key codes across browsers
18177 * @param {Number} key The key code
18178 * @return {Number} The normalized code
18180 normalizeKey: function(key){
18181 // can't feature detect this
18182 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
18186 * Gets the x coordinate of the event.
18188 * @deprecated 4.0 Replaced by {@link #getX}
18190 getPageX: function(){
18191 return this.getX();
18195 * Gets the y coordinate of the event.
18197 * @deprecated 4.0 Replaced by {@link #getY}
18199 getPageY: function(){
18200 return this.getY();
18204 * Gets the x coordinate of the event.
18208 return this.getXY()[0];
18212 * Gets the y coordinate of the event.
18216 return this.getXY()[1];
18220 * Gets the page coordinates of the event.
18221 * @return {Array} The xy values like [x, y]
18223 getXY: function() {
18226 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
18232 * Gets the target for the event.
18233 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18234 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18235 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
18236 * @return {HTMLelement}
18238 getTarget : function(selector, maxDepth, returnEl){
18240 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
18242 return returnEl ? Ext.get(this.target) : this.target;
18246 * Gets the related target.
18247 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
18248 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
18249 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
18250 * @return {HTMLElement}
18252 getRelatedTarget : function(selector, maxDepth, returnEl){
18254 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
18256 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
18260 * Correctly scales a given wheel delta.
18261 * @param {Number} delta The delta value.
18263 correctWheelDelta : function (delta) {
18264 var scale = this.WHEEL_SCALE,
18265 ret = Math.round(delta / scale + 0.5);
18267 if (!ret && delta) {
18268 ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
18275 * Returns the mouse wheel deltas for this event.
18276 * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
18278 getWheelDeltas : function () {
18280 event = me.browserEvent,
18281 dx = 0, dy = 0; // the deltas
18283 if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
18284 dx = event.wheelDeltaX;
18285 dy = event.wheelDeltaY;
18286 } else if (event.wheelDelta) { // old WebKit and IE
18287 dy = event.wheelDelta;
18288 } else if (event.detail) { // Gecko
18289 dy = -event.detail; // gecko is backwards
18291 // Gecko sometimes returns really big values if the user changes settings to
18292 // scroll a whole page per scroll
18295 } else if (dy < -100) {
18299 // Firefox 3.1 adds an axis field to the event to indicate direction of
18300 // scroll. See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
18301 if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
18308 x: me.correctWheelDelta(dx),
18309 y: me.correctWheelDelta(dy)
18314 * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
18315 * {@link #getWheelDeltas} instead.
18316 * @return {Number} The mouse wheel y-delta
18318 getWheelDelta : function(){
18319 var deltas = this.getWheelDeltas();
18325 * Returns true if the target of this event is a child of el. Unless the allowEl parameter is set, it will return false if if the target is el.
18326 * Example usage:<pre><code>
18327 // Handle click on any child of an element
18328 Ext.getBody().on('click', function(e){
18329 if(e.within('some-el')){
18330 alert('Clicked on a child of some-el!');
18334 // Handle click directly on an element, ignoring clicks on child nodes
18335 Ext.getBody().on('click', function(e,t){
18336 if((t.id == 'some-el') && !e.within(t, true)){
18337 alert('Clicked directly on some-el!');
18341 * @param {Mixed} el The id, DOM element or Ext.core.Element to check
18342 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
18343 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
18344 * @return {Boolean}
18346 within : function(el, related, allowEl){
18348 var t = related ? this.getRelatedTarget() : this.getTarget(),
18352 result = Ext.fly(el).contains(t);
18353 if (!result && allowEl) {
18354 result = t == Ext.getDom(el);
18363 * Checks if the key pressed was a "navigation" key
18364 * @return {Boolean} True if the press is a navigation keypress
18366 isNavKeyPress : function(){
18368 k = this.normalizeKey(me.keyCode);
18370 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
18377 * Checks if the key pressed was a "special" key
18378 * @return {Boolean} True if the press is a special keypress
18380 isSpecialKey : function(){
18381 var k = this.normalizeKey(this.keyCode);
18382 return (this.type == 'keypress' && this.ctrlKey) ||
18383 this.isNavKeyPress() ||
18384 (k == this.BACKSPACE) || // Backspace
18385 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
18386 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
18390 * Returns a point object that consists of the object coordinates.
18391 * @return {Ext.util.Point} point
18393 getPoint : function(){
18394 var xy = this.getXY();
18395 return Ext.create('Ext.util.Point', xy[0], xy[1]);
18399 * Returns true if the control, meta, shift or alt key was pressed during this event.
18400 * @return {Boolean}
18402 hasModifier : function(){
18403 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
18407 * Injects a DOM event using the data in this object and (optionally) a new target.
18408 * This is a low-level technique and not likely to be used by application code. The
18409 * currently supported event types are:
18410 * <p><b>HTMLEvents</b></p>
18421 * <p><b>MouseEvents</b></p>
18424 * <li>dblclick</li>
18425 * <li>mousedown</li>
18427 * <li>mouseover</li>
18428 * <li>mousemove</li>
18429 * <li>mouseout</li>
18431 * <p><b>UIEvents</b></p>
18434 * <li>focusout</li>
18435 * <li>activate</li>
18439 * @param {Element/HTMLElement} target If specified, the target for the event. This
18440 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
18441 * is used to determine the target.
18443 injectEvent: function () {
18445 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
18447 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
18449 // IE9 has createEvent, but this code causes major problems with htmleditor (it
18450 // blocks all mouse events and maybe more). TODO
18452 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
18454 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18455 var event = doc.createEvent('HTMLEvents');
18457 event.initEvent(type, bubbles, cancelable);
18461 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18462 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18463 button, relatedTarget) {
18464 var event = doc.createEvent('MouseEvents'),
18465 view = doc.defaultView || window;
18467 if (event.initMouseEvent) {
18468 event.initMouseEvent(type, bubbles, cancelable, view, detail,
18469 clientX, clientY, clientX, clientY, ctrlKey, altKey,
18470 shiftKey, metaKey, button, relatedTarget);
18471 } else { // old Safari
18472 event = doc.createEvent('UIEvents');
18473 event.initEvent(type, bubbles, cancelable);
18475 event.detail = detail;
18476 event.screenX = clientX;
18477 event.screenY = clientY;
18478 event.clientX = clientX;
18479 event.clientY = clientY;
18480 event.ctrlKey = ctrlKey;
18481 event.altKey = altKey;
18482 event.metaKey = metaKey;
18483 event.shiftKey = shiftKey;
18484 event.button = button;
18485 event.relatedTarget = relatedTarget;
18491 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18492 var event = doc.createEvent('UIEvents'),
18493 view = doc.defaultView || window;
18495 event.initUIEvent(type, bubbles, cancelable, view, detail);
18499 fireEvent: function (target, type, event) {
18500 target.dispatchEvent(event);
18503 fixTarget: function (target) {
18504 // Safari3 doesn't have window.dispatchEvent()
18505 if (target == window && !target.dispatchEvent) {
18512 } else if (document.createEventObject) { // else if (IE)
18513 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
18516 createHtmlEvent: function (doc, type, bubbles, cancelable) {
18517 var event = doc.createEventObject();
18518 event.bubbles = bubbles;
18519 event.cancelable = cancelable;
18523 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
18524 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
18525 button, relatedTarget) {
18526 var event = doc.createEventObject();
18527 event.bubbles = bubbles;
18528 event.cancelable = cancelable;
18529 event.detail = detail;
18530 event.screenX = clientX;
18531 event.screenY = clientY;
18532 event.clientX = clientX;
18533 event.clientY = clientY;
18534 event.ctrlKey = ctrlKey;
18535 event.altKey = altKey;
18536 event.shiftKey = shiftKey;
18537 event.metaKey = metaKey;
18538 event.button = crazyIEButtons[button] || button;
18539 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
18543 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
18544 var event = doc.createEventObject();
18545 event.bubbles = bubbles;
18546 event.cancelable = cancelable;
18550 fireEvent: function (target, type, event) {
18551 target.fireEvent('on' + type, event);
18554 fixTarget: function (target) {
18555 if (target == document) {
18556 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
18557 // IE6,IE7 cannot properly call document.fireEvent()
18558 return document.documentElement;
18570 load: [false, false],
18571 unload: [false, false],
18572 select: [true, false],
18573 change: [true, false],
18574 submit: [true, true],
18575 reset: [true, false],
18576 resize: [true, false],
18577 scroll: [true, false]
18579 function (name, value) {
18580 var bubbles = value[0], cancelable = value[1];
18581 dispatchers[name] = function (targetEl, srcEvent) {
18582 var e = API.createHtmlEvent(name, bubbles, cancelable);
18583 API.fireEvent(targetEl, name, e);
18590 function createMouseEventDispatcher (type, detail) {
18591 var cancelable = (type != 'mousemove');
18592 return function (targetEl, srcEvent) {
18593 var xy = srcEvent.getXY(),
18594 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
18595 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
18596 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
18597 srcEvent.relatedTarget);
18598 API.fireEvent(targetEl, type, e);
18602 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
18603 function (eventName) {
18604 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
18611 focusin: [true, false],
18612 focusout: [true, false],
18613 activate: [true, true],
18614 focus: [false, false],
18615 blur: [false, false]
18617 function (name, value) {
18618 var bubbles = value[0], cancelable = value[1];
18619 dispatchers[name] = function (targetEl, srcEvent) {
18620 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
18621 API.fireEvent(targetEl, name, e);
18627 // not even sure what ancient browsers fall into this category...
18629 dispatchers = {}; // never mind all those we just built :P
18632 fixTarget: function (t) {
18638 function cannotInject (target, srcEvent) {
18639 // TODO log something
18642 return function (target) {
18644 dispatcher = dispatchers[me.type] || cannotInject,
18645 t = target ? (target.dom || target) : me.getTarget();
18647 t = API.fixTarget(t);
18650 }() // call to produce method
18654 Ext.EventObject = new Ext.EventObjectImpl();
18660 * @class Ext.core.Element
18663 var doc = document,
18664 activeElement = null,
18665 isCSS1 = doc.compatMode == "CSS1Compat",
18666 ELEMENT = Ext.core.Element,
18667 fly = function(el){
18669 _fly = new Ext.core.Element.Flyweight();
18675 // If the browser does not support document.activeElement we need some assistance.
18676 // This covers old Safari 3.2 (4.0 added activeElement along with just about all
18677 // other browsers). We need this support to handle issues with old Safari.
18678 if (!('activeElement' in doc) && doc.addEventListener) {
18679 doc.addEventListener('focus',
18681 if (ev && ev.target) {
18682 activeElement = (ev.target == doc) ? null : ev.target;
18688 * Helper function to create the function that will restore the selection.
18690 function makeSelectionRestoreFn (activeEl, start, end) {
18691 return function () {
18692 activeEl.selectionStart = start;
18693 activeEl.selectionEnd = end;
18697 Ext.apply(ELEMENT, {
18698 isAncestor : function(p, c) {
18705 return p.contains(c);
18706 } else if (p.compareDocumentPosition) {
18707 return !!(p.compareDocumentPosition(c) & 16);
18709 while ((c = c.parentNode)) {
18710 ret = c == p || ret;
18718 * Returns the active element in the DOM. If the browser supports activeElement
18719 * on the document, this is returned. If not, the focus is tracked and the active
18720 * element is maintained internally.
18721 * @return {HTMLElement} The active (focused) element in the document.
18723 getActiveElement: function () {
18724 return doc.activeElement || activeElement;
18728 * Creates a function to call to clean up problems with the work-around for the
18729 * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
18730 * the element before calling getComputedStyle and then to restore its original
18731 * display value. The problem with this is that it corrupts the selection of an
18732 * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
18733 * To cleanup after this, we need to capture the selection of any such element and
18734 * then restore it after we have restored the display style.
18736 * @param target {Element} The top-most element being adjusted.
18739 getRightMarginFixCleaner: function (target) {
18740 var supports = Ext.supports,
18741 hasInputBug = supports.DisplayChangeInputSelectionBug,
18742 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
18744 if (hasInputBug || hasTextAreaBug) {
18745 var activeEl = doc.activeElement || activeElement, // save a call
18746 tag = activeEl && activeEl.tagName,
18750 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
18751 (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
18752 if (ELEMENT.isAncestor(target, activeEl)) {
18753 start = activeEl.selectionStart;
18754 end = activeEl.selectionEnd;
18756 if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
18757 // We don't create the raw closure here inline because that
18758 // will be costly even if we don't want to return it (nested
18759 // function decls and exprs are often instantiated on entry
18760 // regardless of whether execution ever reaches them):
18761 return makeSelectionRestoreFn(activeEl, start, end);
18767 return Ext.emptyFn; // avoid special cases, just return a nop
18770 getViewWidth : function(full) {
18771 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
18774 getViewHeight : function(full) {
18775 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
18778 getDocumentHeight: function() {
18779 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
18782 getDocumentWidth: function() {
18783 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
18786 getViewportHeight: function(){
18788 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
18792 getViewportWidth : function() {
18793 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
18794 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
18797 getY : function(el) {
18798 return ELEMENT.getXY(el)[1];
18801 getX : function(el) {
18802 return ELEMENT.getXY(el)[0];
18805 getXY : function(el) {
18816 bd = (doc.body || doc.documentElement),
18819 el = Ext.getDom(el);
18822 hasAbsolute = fly(el).isStyle("position", "absolute");
18824 if (el.getBoundingClientRect) {
18825 b = el.getBoundingClientRect();
18826 scroll = fly(document).getScroll();
18827 ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
18836 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
18839 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
18840 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
18842 if (p != el && !pe.isStyle('overflow','visible')) {
18847 p = p.offsetParent;
18850 if (Ext.isSafari && hasAbsolute) {
18851 x -= bd.offsetLeft;
18855 if (Ext.isGecko && !hasAbsolute) {
18857 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
18858 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
18862 while (p && p != bd) {
18863 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
18875 setXY : function(el, xy) {
18876 (el = Ext.fly(el, '_setXY')).position();
18878 var pts = el.translatePoints(xy),
18879 style = el.dom.style,
18883 if (!isNaN(pts[pos])) {
18884 style[pos] = pts[pos] + "px";
18889 setX : function(el, x) {
18890 ELEMENT.setXY(el, [x, false]);
18893 setY : function(el, y) {
18894 ELEMENT.setXY(el, [false, y]);
18898 * Serializes a DOM form into a url encoded string
18899 * @param {Object} form The form
18900 * @return {String} The url encoded form
18902 serializeForm: function(form) {
18903 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
18905 encoder = encodeURIComponent,
18911 Ext.each(fElements, function(element){
18912 name = element.name;
18913 type = element.type;
18915 if (!element.disabled && name) {
18916 if (/select-(one|multiple)/i.test(type)) {
18917 Ext.each(element.options, function(opt){
18918 if (opt.selected) {
18919 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
18920 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
18923 } else if (!(/file|undefined|reset|button/i.test(type))) {
18924 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
18925 data += encoder(name) + '=' + encoder(element.value) + '&';
18926 hasSubmit = /submit/i.test(type);
18931 return data.substr(0, data.length - 1);
18937 * @class Ext.core.Element
18940 Ext.core.Element.addMethods({
18943 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
18944 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
18945 * back in, the function is not called.
18946 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
18947 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
18948 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
18949 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
18950 // Hide the menu if the mouse moves out for 250ms or more
18951 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
18954 // Remove mouseleave monitor on menu destroy
18955 this.menuEl.un(this.mouseLeaveMonitor);
18958 monitorMouseLeave: function(delay, handler, scope) {
18962 mouseleave: function(e) {
18963 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
18965 mouseenter: function() {
18966 clearTimeout(timer);
18976 * Stops the specified event(s) from bubbling and optionally prevents the default action
18977 * @param {String/Array} eventName an event / array of events to stop from bubbling
18978 * @param {Boolean} preventDefault (optional) true to prevent the default action too
18979 * @return {Ext.core.Element} this
18981 swallowEvent : function(eventName, preventDefault) {
18984 e.stopPropagation();
18985 if (preventDefault) {
18986 e.preventDefault();
18990 if (Ext.isArray(eventName)) {
18991 Ext.each(eventName, function(e) {
18996 me.on(eventName, fn);
19001 * Create an event handler on this element such that when the event fires and is handled by this element,
19002 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
19003 * @param {String} eventName The type of event to relay
19004 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
19005 * for firing the relayed event
19007 relayEvent : function(eventName, observable) {
19008 this.on(eventName, function(e) {
19009 observable.fireEvent(eventName, e);
19014 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
19015 * @param {Boolean} forceReclean (optional) By default the element
19016 * keeps track if it has been cleaned already so
19017 * you can call this over and over. However, if you update the element and
19018 * need to force a reclean, you can pass true.
19020 clean : function(forceReclean) {
19023 n = dom.firstChild,
19027 if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
19032 nx = n.nextSibling;
19033 if (n.nodeType == 3) {
19034 // Remove empty/whitespace text nodes
19035 if (!(/\S/.test(n.nodeValue))) {
19036 dom.removeChild(n);
19037 // Combine adjacent text nodes
19038 } else if (nx && nx.nodeType == 3) {
19039 n.appendData(Ext.String.trim(nx.data));
19040 dom.removeChild(nx);
19041 nx = n.nextSibling;
19042 n.nodeIndex = ++ni;
19045 // Recursively clean
19046 Ext.fly(n).clean();
19047 n.nodeIndex = ++ni;
19052 Ext.core.Element.data(dom, 'isCleaned', true);
19057 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
19058 * parameter as {@link Ext.ElementLoader#load}
19059 * @return {Ext.core.Element} this
19061 load : function(options) {
19062 this.getLoader().load(options);
19067 * Gets this element's {@link Ext.ElementLoader ElementLoader}
19068 * @return {Ext.ElementLoader} The loader
19070 getLoader : function() {
19071 var dom = this.dom,
19072 data = Ext.core.Element.data,
19073 loader = data(dom, 'loader');
19076 loader = Ext.create('Ext.ElementLoader', {
19079 data(dom, 'loader', loader);
19085 * Update the innerHTML of this element, optionally searching for and processing scripts
19086 * @param {String} html The new HTML
19087 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
19088 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
19089 * @return {Ext.core.Element} this
19091 update : function(html, loadScripts, callback) {
19103 if (loadScripts !== true) {
19104 dom.innerHTML = html;
19105 Ext.callback(callback, me);
19110 html += '<span id="' + id + '"></span>';
19112 interval = setInterval(function(){
19113 if (!document.getElementById(id)) {
19116 clearInterval(interval);
19117 var DOC = document,
19118 hd = DOC.getElementsByTagName("head")[0],
19119 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
19120 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
19121 typeRe = /\stype=([\'\"])(.*?)\1/i,
19129 while ((match = re.exec(html))) {
19131 srcMatch = attrs ? attrs.match(srcRe) : false;
19132 if (srcMatch && srcMatch[2]) {
19133 s = DOC.createElement("script");
19134 s.src = srcMatch[2];
19135 typeMatch = attrs.match(typeRe);
19136 if (typeMatch && typeMatch[2]) {
19137 s.type = typeMatch[2];
19140 } else if (match[2] && match[2].length > 0) {
19141 if (window.execScript) {
19142 window.execScript(match[2]);
19144 window.eval(match[2]);
19149 el = DOC.getElementById(id);
19151 Ext.removeNode(el);
19153 Ext.callback(callback, me);
19155 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
19159 // inherit docs, overridden so we can add removeAnchor
19160 removeAllListeners : function() {
19161 this.removeAnchor();
19162 Ext.EventManager.removeAll(this.dom);
19167 * Creates a proxy element of this element
19168 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
19169 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
19170 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
19171 * @return {Ext.core.Element} The new proxy element
19173 createProxy : function(config, renderTo, matchBox) {
19174 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
19177 proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
19178 Ext.core.DomHelper.insertBefore(me.dom, config, true);
19180 proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
19182 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
19183 proxy.setBox(me.getBox());
19188 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
19191 * @class Ext.core.Element
19193 Ext.core.Element.addMethods({
19195 * Gets the x,y coordinates specified by the anchor position on the element.
19196 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
19197 * for details on supported anchor positions.
19198 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
19199 * of page coordinates
19200 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
19201 * {width: (target width), height: (target height)} (defaults to the element's current size)
19202 * @return {Array} [x, y] An array containing the element's x and y coordinates
19204 getAnchorXY : function(anchor, local, s){
19205 //Passing a different size is useful for pre-calculating anchors,
19206 //especially for anchored animations that change the el size.
19207 anchor = (anchor || "tl").toLowerCase();
19211 vp = me.dom == document.body || me.dom == document,
19212 w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
19213 h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
19217 scroll = me.getScroll(),
19218 extraX = vp ? scroll.left : !local ? o[0] : 0,
19219 extraY = vp ? scroll.top : !local ? o[1] : 0,
19221 c : [r(w * 0.5), r(h * 0.5)],
19222 t : [r(w * 0.5), 0],
19223 l : [0, r(h * 0.5)],
19224 r : [w, r(h * 0.5)],
19225 b : [r(w * 0.5), h],
19233 return [xy[0] + extraX, xy[1] + extraY];
19237 * Anchors an element to another element and realigns it when the window is resized.
19238 * @param {Mixed} element The element to align to.
19239 * @param {String} position The position to align to.
19240 * @param {Array} offsets (optional) Offset the positioning by [x, y]
19241 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
19242 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
19243 * is a number, it is used as the buffer delay (defaults to 50ms).
19244 * @param {Function} callback The function to call after the animation finishes
19245 * @return {Ext.core.Element} this
19247 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
19250 scroll = !Ext.isEmpty(monitorScroll),
19251 action = function(){
19252 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
19253 Ext.callback(callback, Ext.fly(dom));
19255 anchor = this.getAnchor();
19257 // previous listener anchor, remove it
19258 this.removeAnchor();
19259 Ext.apply(anchor, {
19264 Ext.EventManager.onWindowResize(action, null);
19267 Ext.EventManager.on(window, 'scroll', action, null,
19268 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
19270 action.call(me); // align immediately
19275 * Remove any anchor to this element. See {@link #anchorTo}.
19276 * @return {Ext.core.Element} this
19278 removeAnchor : function(){
19280 anchor = this.getAnchor();
19282 if(anchor && anchor.fn){
19283 Ext.EventManager.removeResizeListener(anchor.fn);
19285 Ext.EventManager.un(window, 'scroll', anchor.fn);
19293 getAnchor : function(){
19294 var data = Ext.core.Element.data,
19299 var anchor = data(dom, '_anchor');
19302 anchor = data(dom, '_anchor', {});
19307 getAlignVector: function(el, spec, offset) {
19309 side = {t:"top", l:"left", r:"right", b: "bottom"},
19310 thisRegion = me.getRegion(),
19314 if(!el || !el.dom){
19316 sourceClass: 'Ext.core.Element',
19317 sourceMethod: 'getAlignVector',
19318 msg: 'Attempted to align an element that doesn\'t exist'
19322 elRegion = el.getRegion();
19326 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
19327 * supported position values.
19328 * @param {Mixed} element The element to align to.
19329 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
19330 * @param {Array} offsets (optional) Offset the positioning by [x, y]
19331 * @return {Array} [x, y]
19333 getAlignToXY : function(el, p, o){
19336 if(!el || !el.dom){
19338 sourceClass: 'Ext.core.Element',
19339 sourceMethod: 'getAlignToXY',
19340 msg: 'Attempted to align an element that doesn\'t exist'
19345 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
19353 //constrain the aligned el to viewport if necessary
19357 dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
19358 dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
19366 docElement = doc.documentElement,
19367 docBody = doc.body,
19368 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
19369 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
19370 c = false, //constrain to viewport
19373 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
19377 sourceClass: 'Ext.core.Element',
19378 sourceMethod: 'getAlignToXY',
19382 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
19390 //Subtract the aligned el's internal xy from the target's offset xy
19391 //plus custom offset to get the aligned el's new offset xy
19392 a1 = me.getAnchorXY(p1, true);
19393 a2 = el.getAnchorXY(p2, false);
19395 x = a2[0] - a1[0] + o[0];
19396 y = a2[1] - a1[1] + o[1];
19400 h = me.getHeight();
19401 r = el.getRegion();
19402 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
19403 //perpendicular to the vp border, allow the aligned el to slide on that border,
19404 //otherwise swap the aligned el to the opposite border of the target.
19405 p1y = p1.charAt(0);
19406 p1x = p1.charAt(p1.length-1);
19407 p2y = p2.charAt(0);
19408 p2x = p2.charAt(p2.length-1);
19409 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
19410 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
19413 if (x + w > dw + scrollX) {
19414 x = swapX ? r.left-w : dw+scrollX-w;
19417 x = swapX ? r.right : scrollX;
19419 if (y + h > dh + scrollY) {
19420 y = swapY ? r.top-h : dh+scrollY-h;
19423 y = swapY ? r.bottom : scrollY;
19430 * Aligns this element with another element relative to the specified anchor points. If the other element is the
19431 * document it aligns it to the viewport.
19432 * The position parameter is optional, and can be specified in any one of the following formats:
19434 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
19435 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
19436 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
19437 * deprecated in favor of the newer two anchor syntax below</i>.</li>
19438 * <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
19439 * element's anchor point, and the second value is used as the target's anchor point.</li>
19441 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
19442 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
19443 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
19444 * that specified in order to enforce the viewport constraints.
19445 * Following are all of the supported anchor positions:
19448 ----- -----------------------------
19449 tl The top left corner (default)
19450 t The center of the top edge
19451 tr The top right corner
19452 l The center of the left edge
19453 c In the center of the element
19454 r The center of the right edge
19455 bl The bottom left corner
19456 b The center of the bottom edge
19457 br The bottom right corner
19461 // align el to other-el using the default positioning ("tl-bl", non-constrained)
19462 el.alignTo("other-el");
19464 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
19465 el.alignTo("other-el", "tr?");
19467 // align the bottom right corner of el with the center left edge of other-el
19468 el.alignTo("other-el", "br-l?");
19470 // align the center of el with the bottom left corner of other-el and
19471 // adjust the x position by -6 pixels (and the y position by 0)
19472 el.alignTo("other-el", "c-bl", [-6, 0]);
19474 * @param {Mixed} element The element to align to.
19475 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
19476 * @param {Array} offsets (optional) Offset the positioning by [x, y]
19477 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19478 * @return {Ext.core.Element} this
19480 alignTo : function(element, position, offsets, animate){
19482 return me.setXY(me.getAlignToXY(element, position, offsets),
19483 me.anim && !!animate ? me.anim(animate) : false);
19486 // private ==> used outside of core
19487 adjustForConstraints : function(xy, parent) {
19488 var vector = this.getConstrainVector(parent, xy);
19490 xy[0] += vector[0];
19491 xy[1] += vector[1];
19497 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
19498 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
19499 * <p>Priority is given to constraining the top and left within the constraint.</p>
19500 * <p>The constraint may either be an existing element into which this element is to be constrained, or
19501 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
19502 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
19503 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
19504 * of using this Element's current position;
19505 * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
19506 * vector by which this element must be translated. Otherwise, <code>false</code>.
19508 getConstrainVector: function(constrainTo, proposedPosition) {
19509 if (!(constrainTo instanceof Ext.util.Region)) {
19510 constrainTo = Ext.get(constrainTo).getViewRegion();
19512 var thisRegion = this.getRegion(),
19514 shadowSize = this.shadow && this.shadow.offset,
19515 overflowed = false;
19517 // Shift this region to occupy the proposed position
19518 if (proposedPosition) {
19519 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
19522 // Reduce the constrain region to allow for shadow
19523 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
19525 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
19528 // Constrain the X coordinate by however much this Element overflows
19529 if (thisRegion.right > constrainTo.right) {
19531 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
19533 if (thisRegion.left + vector[0] < constrainTo.left) {
19535 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
19538 // Constrain the Y coordinate by however much this Element overflows
19539 if (thisRegion.bottom > constrainTo.bottom) {
19541 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
19543 if (thisRegion.top + vector[1] < constrainTo.top) {
19545 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
19547 return overflowed ? vector : false;
19551 * Calculates the x, y to center this element on the screen
19552 * @return {Array} The x, y values [x, y]
19554 getCenterXY : function(){
19555 return this.getAlignToXY(document, 'c-c');
19559 * Centers the Element in either the viewport, or another Element.
19560 * @param {Mixed} centerIn (optional) The element in which to center the element.
19562 center : function(centerIn){
19563 return this.alignTo(centerIn || document, 'c-c');
19568 * @class Ext.core.Element
19572 var ELEMENT = Ext.core.Element,
19577 POSITION = "position",
19579 RELATIVE = "relative",
19581 ZINDEX = "z-index";
19583 Ext.override(Ext.core.Element, {
19585 * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19586 * @return {Number} The X position of the element
19589 return ELEMENT.getX(this.dom);
19593 * Gets the current Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19594 * @return {Number} The Y position of the element
19597 return ELEMENT.getY(this.dom);
19601 * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19602 * @return {Array} The XY position of the element
19604 getXY : function(){
19605 return ELEMENT.getXY(this.dom);
19609 * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.
19610 * @param {Mixed} element The element to get the offsets from.
19611 * @return {Array} The XY page offsets (e.g. [100, -200])
19613 getOffsetsTo : function(el){
19614 var o = this.getXY(),
19615 e = Ext.fly(el, '_internal').getXY();
19616 return [o[0]-e[0],o[1]-e[1]];
19620 * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19621 * @param {Number} The X position of the element
19622 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19623 * @return {Ext.core.Element} this
19625 setX : function(x, animate){
19626 return this.setXY([x, this.getY()], animate);
19630 * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19631 * @param {Number} The Y position of the element
19632 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19633 * @return {Ext.core.Element} this
19635 setY : function(y, animate){
19636 return this.setXY([this.getX(), y], animate);
19640 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
19641 * @param {String} left The left CSS property value
19642 * @return {Ext.core.Element} this
19644 setLeft : function(left){
19645 this.setStyle(LEFT, this.addUnits(left));
19650 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
19651 * @param {String} top The top CSS property value
19652 * @return {Ext.core.Element} this
19654 setTop : function(top){
19655 this.setStyle(TOP, this.addUnits(top));
19660 * Sets the element's CSS right style.
19661 * @param {String} right The right CSS property value
19662 * @return {Ext.core.Element} this
19664 setRight : function(right){
19665 this.setStyle(RIGHT, this.addUnits(right));
19670 * Sets the element's CSS bottom style.
19671 * @param {String} bottom The bottom CSS property value
19672 * @return {Ext.core.Element} this
19674 setBottom : function(bottom){
19675 this.setStyle(BOTTOM, this.addUnits(bottom));
19680 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19681 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19682 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
19683 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19684 * @return {Ext.core.Element} this
19686 setXY: function(pos, animate) {
19688 if (!animate || !me.anim) {
19689 ELEMENT.setXY(me.dom, pos);
19692 if (!Ext.isObject(animate)) {
19695 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
19701 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19702 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19703 * @param {Number} x X value for new position (coordinates are page-based)
19704 * @param {Number} y Y value for new position (coordinates are page-based)
19705 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19706 * @return {Ext.core.Element} this
19708 setLocation : function(x, y, animate){
19709 return this.setXY([x, y], animate);
19713 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
19714 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
19715 * @param {Number} x X value for new position (coordinates are page-based)
19716 * @param {Number} y Y value for new position (coordinates are page-based)
19717 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
19718 * @return {Ext.core.Element} this
19720 moveTo : function(x, y, animate){
19721 return this.setXY([x, y], animate);
19725 * Gets the left X coordinate
19726 * @param {Boolean} local True to get the local css position instead of page coordinate
19729 getLeft : function(local){
19730 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
19734 * Gets the right X coordinate of the element (element X position + element width)
19735 * @param {Boolean} local True to get the local css position instead of page coordinate
19738 getRight : function(local){
19740 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
19744 * Gets the top Y coordinate
19745 * @param {Boolean} local True to get the local css position instead of page coordinate
19748 getTop : function(local) {
19749 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
19753 * Gets the bottom Y coordinate of the element (element Y position + element height)
19754 * @param {Boolean} local True to get the local css position instead of page coordinate
19757 getBottom : function(local){
19759 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
19763 * Initializes positioning on this element. If a desired position is not passed, it will make the
19764 * the element positioned relative IF it is not already positioned.
19765 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
19766 * @param {Number} zIndex (optional) The zIndex to apply
19767 * @param {Number} x (optional) Set the page X position
19768 * @param {Number} y (optional) Set the page Y position
19770 position : function(pos, zIndex, x, y) {
19773 if (!pos && me.isStyle(POSITION, STATIC)){
19774 me.setStyle(POSITION, RELATIVE);
19776 me.setStyle(POSITION, pos);
19779 me.setStyle(ZINDEX, zIndex);
19782 me.setXY([x || false, y || false]);
19787 * Clear positioning back to the default when the document was loaded
19788 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
19789 * @return {Ext.core.Element} this
19791 clearPositioning : function(value){
19792 value = value || '';
19805 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
19806 * snapshot before performing an update and then restoring the element.
19809 getPositioning : function(){
19810 var l = this.getStyle(LEFT);
19811 var t = this.getStyle(TOP);
19813 "position" : this.getStyle(POSITION),
19815 "right" : l ? "" : this.getStyle(RIGHT),
19817 "bottom" : t ? "" : this.getStyle(BOTTOM),
19818 "z-index" : this.getStyle(ZINDEX)
19823 * Set positioning with an object returned by getPositioning().
19824 * @param {Object} posCfg
19825 * @return {Ext.core.Element} this
19827 setPositioning : function(pc){
19829 style = me.dom.style;
19833 if(pc.right == AUTO){
19836 if(pc.bottom == AUTO){
19844 * Translates the passed page coordinates into left/top css values for this element
19845 * @param {Number/Array} x The page x or an array containing [x, y]
19846 * @param {Number} y (optional) The page y, required if x is not an array
19847 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
19849 translatePoints: function(x, y) {
19850 if (Ext.isArray(x)) {
19855 relative = me.isStyle(POSITION, RELATIVE),
19857 left = parseInt(me.getStyle(LEFT), 10),
19858 top = parseInt(me.getStyle(TOP), 10);
19860 if (!Ext.isNumber(left)) {
19861 left = relative ? 0 : me.dom.offsetLeft;
19863 if (!Ext.isNumber(top)) {
19864 top = relative ? 0 : me.dom.offsetTop;
19866 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
19867 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
19875 * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
19876 * @param {Object} box The box to fill {x, y, width, height}
19877 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
19878 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19879 * @return {Ext.core.Element} this
19881 setBox: function(box, adjust, animate) {
19885 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
19886 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
19887 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
19889 me.setBounds(box.x, box.y, w, h, animate);
19894 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
19895 * set another Element's size/location to match this element.
19896 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
19897 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
19898 * @return {Object} box An object in the format<pre><code>
19900 x: <Element's X position>,
19901 y: <Element's Y position>,
19902 width: <Element's width>,
19903 height: <Element's height>,
19904 bottom: <Element's lower bound>,
19905 right: <Element's rightmost bound>
19908 * The returned object may also be addressed as an Array where index 0 contains the X position
19909 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
19911 getBox: function(contentBox, local) {
19916 getBorderWidth = me.getBorderWidth,
19917 getPadding = me.getPadding,
19918 l, r, t, b, w, h, bx;
19922 left = parseInt(me.getStyle("left"), 10) || 0;
19923 top = parseInt(me.getStyle("top"), 10) || 0;
19927 h = me.getHeight();
19938 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
19939 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
19940 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
19941 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
19947 width: w - (l + r),
19948 height: h - (t + b)
19951 bx.right = bx.x + bx.width;
19952 bx.bottom = bx.y + bx.height;
19957 * Move this element relative to its current position.
19958 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
19959 * @param {Number} distance How far to move the element in pixels
19960 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
19961 * @return {Ext.core.Element} this
19963 move: function(direction, distance, animate) {
19968 left = [x - distance, y],
19969 right = [x + distance, y],
19970 top = [x, y - distance],
19971 bottom = [x, y + distance],
19985 direction = direction.toLowerCase();
19986 me.moveTo(hash[direction][0], hash[direction][1], animate);
19990 * Quick set left and top adding default units
19991 * @param {String} left The left CSS property value
19992 * @param {String} top The top CSS property value
19993 * @return {Ext.core.Element} this
19995 setLeftTop: function(left, top) {
19997 style = me.dom.style;
19998 style.left = me.addUnits(left);
19999 style.top = me.addUnits(top);
20004 * Returns the region of this element.
20005 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
20006 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
20008 getRegion: function() {
20009 return this.getPageBox(true);
20013 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
20014 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
20016 getViewRegion: function() {
20018 isBody = me.dom === document.body,
20019 scroll, pos, top, left, width, height;
20021 // For the body we want to do some special logic
20023 scroll = me.getScroll();
20024 left = scroll.left;
20026 width = Ext.core.Element.getViewportWidth();
20027 height = Ext.core.Element.getViewportHeight();
20031 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
20032 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
20033 width = me.getWidth(true);
20034 height = me.getHeight(true);
20037 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
20041 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
20042 * set another Element's size/location to match this element.
20043 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
20044 * @return {Object} box An object in the format<pre><code>
20046 x: <Element's X position>,
20047 y: <Element's Y position>,
20048 width: <Element's width>,
20049 height: <Element's height>,
20050 bottom: <Element's lower bound>,
20051 right: <Element's rightmost bound>
20054 * The returned object may also be addressed as an Array where index 0 contains the X position
20055 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
20057 getPageBox : function(getRegion) {
20060 isDoc = el === document.body,
20061 w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth,
20062 h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
20070 return Ext.create('Ext.util.Region', t, r, b, l);
20085 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
20086 * @param {Number} x X value for new position (coordinates are page-based)
20087 * @param {Number} y Y value for new position (coordinates are page-based)
20088 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
20089 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20090 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
20092 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
20093 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
20094 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
20096 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20097 * @return {Ext.core.Element} this
20099 setBounds: function(x, y, width, height, animate) {
20101 if (!animate || !me.anim) {
20102 me.setSize(width, height);
20103 me.setLocation(x, y);
20105 if (!Ext.isObject(animate)) {
20108 me.animate(Ext.applyIf({
20112 width: me.adjustWidth(width),
20113 height: me.adjustHeight(height)
20121 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
20122 * @param {Ext.util.Region} region The region to fill
20123 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20124 * @return {Ext.core.Element} this
20126 setRegion: function(region, animate) {
20127 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
20133 * @class Ext.core.Element
20135 Ext.override(Ext.core.Element, {
20137 * Returns true if this element is scrollable.
20138 * @return {Boolean}
20140 isScrollable : function(){
20141 var dom = this.dom;
20142 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
20146 * Returns the current scroll position of the element.
20147 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
20149 getScroll : function() {
20153 docElement = doc.documentElement,
20158 if (d == doc || d == body) {
20159 if (Ext.isIE && Ext.isStrict) {
20160 l = docElement.scrollLeft;
20161 t = docElement.scrollTop;
20163 l = window.pageXOffset;
20164 t = window.pageYOffset;
20167 left: l || (body ? body.scrollLeft : 0),
20168 top : t || (body ? body.scrollTop : 0)
20172 left: d.scrollLeft,
20181 * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
20182 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
20183 * @param {Number} value The new scroll value
20184 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20185 * @return {Element} this
20187 scrollTo : function(side, value, animate) {
20188 //check if we're scrolling top or left
20189 var top = /top/i.test(side),
20194 if (!animate || !me.anim) {
20195 // just setting the value, so grab the direction
20196 prop = 'scroll' + (top ? 'Top' : 'Left');
20200 if (!Ext.isObject(animate)) {
20203 obj['scroll' + (top ? 'Top' : 'Left')] = value;
20204 me.animate(Ext.applyIf({
20212 * Scrolls this element into view within the passed container.
20213 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
20214 * string (id), dom node, or Ext.core.Element.
20215 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
20216 * @return {Ext.core.Element} this
20218 scrollIntoView : function(container, hscroll) {
20219 container = Ext.getDom(container) || Ext.getBody().dom;
20221 offsets = this.getOffsetsTo(container),
20223 left = offsets[0] + container.scrollLeft,
20224 top = offsets[1] + container.scrollTop,
20225 bottom = top + el.offsetHeight,
20226 right = left + el.offsetWidth,
20228 ctClientHeight = container.clientHeight,
20229 ctScrollTop = parseInt(container.scrollTop, 10),
20230 ctScrollLeft = parseInt(container.scrollLeft, 10),
20231 ctBottom = ctScrollTop + ctClientHeight,
20232 ctRight = ctScrollLeft + container.clientWidth;
20234 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
20235 container.scrollTop = top;
20236 } else if (bottom > ctBottom) {
20237 container.scrollTop = bottom - ctClientHeight;
20239 // corrects IE, other browsers will ignore
20240 container.scrollTop = container.scrollTop;
20242 if (hscroll !== false) {
20243 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
20244 container.scrollLeft = left;
20246 else if (right > ctRight) {
20247 container.scrollLeft = right - container.clientWidth;
20249 container.scrollLeft = container.scrollLeft;
20255 scrollChildIntoView : function(child, hscroll) {
20256 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
20260 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
20261 * within this element's scrollable range.
20262 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
20263 * @param {Number} distance How far to scroll the element in pixels
20264 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
20265 * @return {Boolean} Returns true if a scroll was triggered or false if the element
20266 * was scrolled as far as it could go.
20268 scroll : function(direction, distance, animate) {
20269 if (!this.isScrollable()) {
20273 l = el.scrollLeft, t = el.scrollTop,
20274 w = el.scrollWidth, h = el.scrollHeight,
20275 cw = el.clientWidth, ch = el.clientHeight,
20276 scrolled = false, v,
20278 l: Math.min(l + distance, w-cw),
20279 r: v = Math.max(l - distance, 0),
20280 t: Math.max(t - distance, 0),
20281 b: Math.min(t + distance, h-ch)
20286 direction = direction.substr(0, 1);
20287 if ((v = hash[direction]) > -1) {
20289 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
20295 * @class Ext.core.Element
20297 Ext.core.Element.addMethods(
20299 var VISIBILITY = "visibility",
20300 DISPLAY = "display",
20303 XMASKED = Ext.baseCSSPrefix + "masked",
20304 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
20305 data = Ext.core.Element.data;
20309 * Checks whether the element is currently visible using both visibility and display properties.
20310 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
20311 * @return {Boolean} True if the element is currently visible, else false
20313 isVisible : function(deep) {
20314 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
20315 p = this.dom.parentNode;
20317 if (deep !== true || !vis) {
20321 while (p && !(/^body/i.test(p.tagName))) {
20322 if (!Ext.fly(p, '_isVisible').isVisible()) {
20331 * Returns true if display is not "none"
20332 * @return {Boolean}
20334 isDisplayed : function() {
20335 return !this.isStyle(DISPLAY, NONE);
20339 * Convenience method for setVisibilityMode(Element.DISPLAY)
20340 * @param {String} display (optional) What to set display to when visible
20341 * @return {Ext.core.Element} this
20343 enableDisplayMode : function(display) {
20344 this.setVisibilityMode(Ext.core.Element.DISPLAY);
20346 if (!Ext.isEmpty(display)) {
20347 data(this.dom, 'originalDisplay', display);
20354 * Puts a mask over this element to disable user interaction. Requires core.css.
20355 * This method can only be applied to elements which accept child nodes.
20356 * @param {String} msg (optional) A message to display in the mask
20357 * @param {String} msgCls (optional) A css class to apply to the msg element
20358 * @return {Element} The mask element
20360 mask : function(msg, msgCls) {
20363 setExpression = dom.style.setExpression,
20364 dh = Ext.core.DomHelper,
20365 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
20369 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
20370 me.addCls(XMASKEDRELATIVE);
20372 el = data(dom, 'maskMsg');
20376 el = data(dom, 'mask');
20381 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
20382 data(dom, 'mask', mask);
20384 me.addCls(XMASKED);
20385 mask.setDisplayed(true);
20387 if (typeof msg == 'string') {
20388 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
20389 data(dom, 'maskMsg', mm);
20390 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
20391 mm.dom.firstChild.innerHTML = msg;
20392 mm.setDisplayed(true);
20395 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
20396 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
20397 // In normal use cases an element will be masked for a limited period of time.
20398 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
20399 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
20400 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
20401 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
20404 // Some versions and modes of IE subtract top+bottom padding when calculating height.
20405 // Different versions from those which make the same error for width!
20406 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
20407 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
20409 // ie will not expand full height automatically
20410 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
20411 mask.setSize(undefined, me.getHeight());
20417 * Removes a previously applied mask.
20419 unmask : function() {
20422 mask = data(dom, 'mask'),
20423 maskMsg = data(dom, 'maskMsg');
20426 // Remove resource-intensive CSS expressions as soon as they are not required.
20427 if (mask.dom.style.clearExpression) {
20428 mask.dom.style.clearExpression('width');
20429 mask.dom.style.clearExpression('height');
20433 data(dom, 'maskMsg', undefined);
20437 data(dom, 'mask', undefined);
20438 me.removeCls([XMASKED, XMASKEDRELATIVE]);
20442 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
20443 * @return {Boolean}
20445 isMasked : function() {
20447 mask = data(me.dom, 'mask'),
20448 maskMsg = data(me.dom, 'maskMsg');
20450 if (mask && mask.isVisible()) {
20452 maskMsg.center(me);
20460 * Creates an iframe shim for this element to keep selects and other windowed objects from
20462 * @return {Ext.core.Element} The new shim element
20464 createShim : function() {
20465 var el = document.createElement('iframe'),
20468 el.frameBorder = '0';
20469 el.className = Ext.baseCSSPrefix + 'shim';
20470 el.src = Ext.SSL_SECURE_URL;
20471 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
20472 shim.autoBoxAdjust = false;
20479 * @class Ext.core.Element
20481 Ext.core.Element.addMethods({
20483 * Convenience method for constructing a KeyMap
20484 * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
20485 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
20486 * @param {Function} fn The function to call
20487 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
20488 * @return {Ext.util.KeyMap} The KeyMap created
20490 addKeyListener : function(key, fn, scope){
20492 if(typeof key != 'object' || Ext.isArray(key)){
20508 return Ext.create('Ext.util.KeyMap', this, config);
20512 * Creates a KeyMap for this element
20513 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
20514 * @return {Ext.util.KeyMap} The KeyMap created
20516 addKeyMap : function(config){
20517 return Ext.create('Ext.util.KeyMap', this, config);
20521 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
20522 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
20523 Ext.CompositeElementLite.importElementMethods();
20526 * @class Ext.CompositeElementLite
20528 Ext.apply(Ext.CompositeElementLite.prototype, {
20529 addElements : function(els, root){
20533 if(typeof els == "string"){
20534 els = Ext.core.Element.selectorFunction(els, root);
20536 var yels = this.elements;
20537 Ext.each(els, function(e) {
20538 yels.push(Ext.get(e));
20544 * Returns the first Element
20545 * @return {Ext.core.Element}
20547 first : function(){
20548 return this.item(0);
20552 * Returns the last Element
20553 * @return {Ext.core.Element}
20556 return this.item(this.getCount()-1);
20560 * Returns true if this composite contains the passed element
20561 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
20564 contains : function(el){
20565 return this.indexOf(el) != -1;
20569 * Removes the specified element(s).
20570 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
20571 * or an array of any of those.
20572 * @param {Boolean} removeDom (optional) True to also remove the element from the document
20573 * @return {CompositeElement} this
20575 removeElement : function(keys, removeDom){
20577 els = this.elements,
20579 Ext.each(keys, function(val){
20580 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
20585 Ext.removeNode(el);
20588 Ext.Array.erase(els, val, 1);
20596 * @class Ext.CompositeElement
20597 * @extends Ext.CompositeElementLite
20598 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
20599 * members, or to perform collective actions upon the whole set.</p>
20600 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
20601 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
20602 * <p>All methods return <i>this</i> and can be chained.</p>
20605 var els = Ext.select("#some-el div.some-class", true);
20606 // or select directly from an existing element
20607 var el = Ext.get('some-el');
20608 el.select('div.some-class', true);
20610 els.setWidth(100); // all elements become 100 width
20611 els.hide(true); // all elements fade out and hide
20613 els.setWidth(100).hide(true);
20616 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
20618 constructor : function(els, root){
20619 this.elements = [];
20620 this.add(els, root);
20624 getElement : function(el){
20625 // In this case just return it, since we already have a reference to it
20630 transformElement : function(el){
20631 return Ext.get(el);
20635 * Adds elements to this composite.
20636 * @param {String/Array} els A string CSS selector, an array of elements or an element
20637 * @return {CompositeElement} this
20641 * Returns the Element object at the specified index
20642 * @param {Number} index
20643 * @return {Ext.core.Element}
20647 * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
20648 * @param {Function} fn
20650 The function to be called with each
20651 `element`. If the supplied function returns <tt>false</tt>,
20652 iteration stops. This function is called with the following arguments:
20654 - `element` : __Ext.core.Element++
20655 The element at the current `index` in the `composite`
20657 - `composite` : __Object__
20660 - `index` : __Number__
20661 The current index within the `composite`
20663 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
20664 * Defaults to the <code>element</code> at the current <code>index</code>
20665 * within the composite.
20666 * @return {CompositeElement} this
20672 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
20673 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
20674 * {@link Ext.CompositeElementLite CompositeElementLite} object.
20675 * @param {String/Array} selector The CSS selector or an array of elements
20676 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
20677 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
20678 * @return {CompositeElementLite/CompositeElement}
20679 * @member Ext.core.Element
20682 Ext.core.Element.select = function(selector, unique, root){
20684 if(typeof selector == "string"){
20685 els = Ext.core.Element.selectorFunction(selector, root);
20686 }else if(selector.length !== undefined){
20690 sourceClass: "Ext.core.Element",
20691 sourceMethod: "select",
20692 selector: selector,
20695 msg: "Invalid selector specified: " + selector
20698 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
20702 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
20703 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
20704 * {@link Ext.CompositeElementLite CompositeElementLite} object.
20705 * @param {String/Array} selector The CSS selector or an array of elements
20706 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
20707 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
20708 * @return {CompositeElementLite/CompositeElement}
20712 Ext.select = Ext.core.Element.select;
20717 This file is part of Ext JS 4
20719 Copyright (c) 2011 Sencha Inc
20721 Contact: http://www.sencha.com/contact
20723 GNU General Public License Usage
20724 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.
20726 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
20729 (function(){ var data = {
20730 "nameToAliasesMap":{
20731 "Ext.AbstractComponent":[""
20733 "Ext.AbstractManager":[""
20735 "Ext.AbstractPlugin":[""
20739 "Ext.ComponentLoader":[""
20741 "Ext.ComponentManager":[""
20743 "Ext.ComponentQuery":[""
20745 "Ext.ElementLoader":[""
20749 "Ext.ModelManager":[""
20751 "Ext.PluginManager":[""
20755 "Ext.XTemplate":[""
20757 "Ext.app.Application":[""
20759 "Ext.app.Controller":[""
20761 "Ext.app.EventBus":[""
20763 "Ext.chart.Callout":[""
20765 "Ext.chart.Chart":["widget.chart"
20767 "Ext.chart.Highlight":[""
20769 "Ext.chart.Label":[""
20771 "Ext.chart.Legend":[""
20773 "Ext.chart.LegendItem":[""
20775 "Ext.chart.Mask":[""
20777 "Ext.chart.MaskLayer":[""
20779 "Ext.chart.Navigation":[""
20781 "Ext.chart.Shape":[""
20783 "Ext.chart.Tip":[""
20785 "Ext.chart.TipSurface":[""
20787 "Ext.chart.axis.Abstract":[""
20789 "Ext.chart.axis.Axis":[""
20791 "Ext.chart.axis.Category":["axis.category"
20793 "Ext.chart.axis.Gauge":["axis.gauge"
20795 "Ext.chart.axis.Numeric":["axis.numeric"
20797 "Ext.chart.axis.Radial":["axis.radial"
20799 "Ext.chart.axis.Time":["axis.time"
20801 "Ext.chart.series.Area":["series.area"
20803 "Ext.chart.series.Bar":["series.bar"
20805 "Ext.chart.series.Cartesian":[""
20807 "Ext.chart.series.Column":["series.column"
20809 "Ext.chart.series.Gauge":["series.gauge"
20811 "Ext.chart.series.Line":["series.line"
20813 "Ext.chart.series.Pie":["series.pie"
20815 "Ext.chart.series.Radar":["series.radar"
20817 "Ext.chart.series.Scatter":["series.scatter"
20819 "Ext.chart.series.Series":[""
20821 "Ext.chart.theme.Base":[""
20823 "Ext.chart.theme.Theme":[""
20825 "Ext.container.AbstractContainer":[""
20827 "Ext.data.AbstractStore":[""
20829 "Ext.data.ArrayStore":["store.array"
20831 "Ext.data.Association":[""
20833 "Ext.data.Batch":[""
20835 "Ext.data.BelongsToAssociation":["association.belongsto"
20837 "Ext.data.BufferStore":["store.buffer"
20839 "Ext.data.Connection":[""
20841 "Ext.data.DirectStore":["store.direct"
20843 "Ext.data.Errors":[""
20845 "Ext.data.Field":["data.field"
20847 "Ext.data.HasManyAssociation":["association.hasmany"
20849 "Ext.data.JsonP":[""
20851 "Ext.data.JsonPStore":["store.jsonp"
20853 "Ext.data.JsonStore":["store.json"
20855 "Ext.data.Model":[""
20857 "Ext.data.NodeInterface":[""
20859 "Ext.data.NodeStore":["store.node"
20861 "Ext.data.Operation":[""
20863 "Ext.data.Request":[""
20865 "Ext.data.ResultSet":[""
20867 "Ext.data.SortTypes":[""
20869 "Ext.data.Store":["store.store"
20871 "Ext.data.StoreManager":[""
20873 "Ext.data.Tree":["data.tree"
20875 "Ext.data.TreeStore":["store.tree"
20877 "Ext.data.Types":[""
20879 "Ext.data.validations":[""
20881 "Ext.data.XmlStore":["store.xml"
20883 "Ext.data.proxy.Ajax":["proxy.ajax"
20885 "Ext.data.proxy.Client":[""
20887 "Ext.data.proxy.Direct":["proxy.direct"
20889 "Ext.data.proxy.JsonP":["proxy.jsonp",
20892 "Ext.data.proxy.LocalStorage":["proxy.localstorage"
20894 "Ext.data.proxy.Memory":["proxy.memory"
20896 "Ext.data.proxy.Proxy":["proxy.proxy"
20898 "Ext.data.proxy.Rest":["proxy.rest"
20900 "Ext.data.proxy.Server":["proxy.server"
20902 "Ext.data.proxy.SessionStorage":["proxy.sessionstorage"
20904 "Ext.data.proxy.WebStorage":[""
20906 "Ext.data.reader.Array":["reader.array"
20908 "Ext.data.reader.Json":["reader.json"
20910 "Ext.data.reader.Reader":[""
20912 "Ext.data.reader.Xml":["reader.xml"
20914 "Ext.data.writer.Json":["writer.json"
20916 "Ext.data.writer.Writer":["writer.base"
20918 "Ext.data.writer.Xml":["writer.xml"
20920 "Ext.direct.Event":["direct.event"
20922 "Ext.direct.ExceptionEvent":["direct.exception"
20924 "Ext.direct.JsonProvider":["direct.jsonprovider"
20926 "Ext.direct.Manager":[""
20928 "Ext.direct.PollingProvider":["direct.pollingprovider"
20930 "Ext.direct.Provider":["direct.provider"
20932 "Ext.direct.RemotingEvent":["direct.rpc"
20934 "Ext.direct.RemotingMethod":[""
20936 "Ext.direct.RemotingProvider":["direct.remotingprovider"
20938 "Ext.direct.Transaction":["direct.transaction"
20940 "Ext.draw.Color":[""
20942 "Ext.draw.Component":["widget.draw"
20944 "Ext.draw.CompositeSprite":[""
20946 "Ext.draw.Draw":[""
20948 "Ext.draw.Matrix":[""
20950 "Ext.draw.Sprite":[""
20952 "Ext.draw.SpriteDD":[""
20954 "Ext.draw.Surface":[""
20956 "Ext.draw.engine.Svg":[""
20958 "Ext.draw.engine.Vml":[""
20962 "Ext.fx.Animator":[""
20964 "Ext.fx.CubicBezier":[""
20966 "Ext.fx.Easing":[],
20967 "Ext.fx.Manager":[""
20969 "Ext.fx.PropertyHandler":[""
20973 "Ext.fx.target.Component":[""
20975 "Ext.fx.target.CompositeElement":[""
20977 "Ext.fx.target.CompositeElementCSS":[""
20979 "Ext.fx.target.CompositeSprite":[""
20981 "Ext.fx.target.Element":[""
20983 "Ext.fx.target.ElementCSS":[""
20985 "Ext.fx.target.Sprite":[""
20987 "Ext.fx.target.Target":[""
20989 "Ext.layout.Layout":[""
20991 "Ext.layout.component.AbstractDock":[""
20993 "Ext.layout.component.Auto":["layout.autocomponent"
20995 "Ext.layout.component.Component":[""
20997 "Ext.layout.component.Draw":["layout.draw"
20999 "Ext.layout.container.AbstractCard":[""
21001 "Ext.layout.container.AbstractContainer":[""
21003 "Ext.layout.container.AbstractFit":[""
21005 "Ext.layout.container.Auto":["layout.auto",
21006 "layout.autocontainer"
21008 "Ext.panel.AbstractPanel":[""
21010 "Ext.selection.DataViewModel":[""
21012 "Ext.selection.Model":[""
21014 "Ext.state.CookieProvider":[""
21016 "Ext.state.LocalStorageProvider":["state.localstorage"
21018 "Ext.state.Manager":[""
21020 "Ext.state.Provider":[""
21022 "Ext.state.Stateful":[""
21024 "Ext.util.AbstractMixedCollection":[""
21026 "Ext.util.Filter":[""
21028 "Ext.util.Grouper":[""
21030 "Ext.util.HashMap":[""
21032 "Ext.util.Inflector":[""
21034 "Ext.util.Memento":[""
21036 "Ext.util.MixedCollection":[""
21038 "Ext.util.Observable":[""
21040 "Ext.util.Offset":[""
21042 "Ext.util.Point":[""
21044 "Ext.util.Region":[""
21046 "Ext.util.Sortable":[""
21048 "Ext.util.Sorter":[""
21050 "Ext.view.AbstractView":[""
21054 "Ext.Component":["widget.component",
21057 "Ext.Editor":["widget.editor"
21059 "Ext.FocusManager":[""
21061 "Ext.Img":["widget.image",
21062 "widget.imagecomponent"
21066 "Ext.ProgressBar":["widget.progressbar"
21070 "Ext.ShadowPool":[""
21072 "Ext.ZIndexManager":[""
21074 "Ext.button.Button":["widget.button"
21076 "Ext.button.Cycle":["widget.cycle"
21078 "Ext.button.Split":["widget.splitbutton"
21080 "Ext.container.ButtonGroup":["widget.buttongroup"
21082 "Ext.container.Container":["widget.container"
21084 "Ext.container.Viewport":["widget.viewport"
21088 "Ext.dd.DDProxy":[""
21090 "Ext.dd.DDTarget":[""
21092 "Ext.dd.DragDrop":[""
21094 "Ext.dd.DragDropManager":[""
21096 "Ext.dd.DragSource":[""
21098 "Ext.dd.DragTracker":[""
21100 "Ext.dd.DragZone":[""
21102 "Ext.dd.DropTarget":[""
21104 "Ext.dd.DropZone":[""
21106 "Ext.dd.Registry":[""
21108 "Ext.dd.ScrollManager":[""
21110 "Ext.dd.StatusProxy":[""
21112 "Ext.flash.Component":["widget.flash"
21114 "Ext.form.Basic":[""
21116 "Ext.form.CheckboxGroup":["widget.checkboxgroup"
21118 "Ext.form.CheckboxManager":[""
21120 "Ext.form.FieldAncestor":[""
21122 "Ext.form.FieldContainer":["widget.fieldcontainer"
21124 "Ext.form.FieldSet":["widget.fieldset"
21126 "Ext.form.Label":["widget.label"
21128 "Ext.form.Labelable":[""
21130 "Ext.form.Panel":["widget.form"
21132 "Ext.form.RadioGroup":["widget.radiogroup"
21134 "Ext.form.RadioManager":[""
21136 "Ext.form.action.Action":[""
21138 "Ext.form.action.DirectLoad":["formaction.directload"
21140 "Ext.form.action.DirectSubmit":["formaction.directsubmit"
21142 "Ext.form.action.Load":["formaction.load"
21144 "Ext.form.action.StandardSubmit":["formaction.standardsubmit"
21146 "Ext.form.action.Submit":["formaction.submit"
21148 "Ext.form.field.Base":["widget.field"
21150 "Ext.form.field.Checkbox":["widget.checkboxfield",
21153 "Ext.form.field.ComboBox":["widget.combobox",
21156 "Ext.form.field.Date":["widget.datefield"
21158 "Ext.form.field.Display":["widget.displayfield"
21160 "Ext.form.field.Field":[""
21162 "Ext.form.field.File":["widget.filefield",
21163 "widget.fileuploadfield"
21165 "Ext.form.field.Hidden":["widget.hiddenfield",
21168 "Ext.form.field.HtmlEditor":["widget.htmleditor"
21170 "Ext.form.field.Number":["widget.numberfield"
21172 "Ext.form.field.Picker":["widget.pickerfield"
21174 "Ext.form.field.Radio":["widget.radiofield",
21177 "Ext.form.field.Spinner":["widget.spinnerfield"
21179 "Ext.form.field.Text":["widget.textfield"
21181 "Ext.form.field.TextArea":["widget.textareafield",
21184 "Ext.form.field.Time":["widget.timefield"
21186 "Ext.form.field.Trigger":["widget.triggerfield",
21189 "Ext.form.field.VTypes":[""
21191 "Ext.grid.CellEditor":[""
21193 "Ext.grid.ColumnLayout":["layout.gridcolumn"
21195 "Ext.grid.Lockable":[""
21197 "Ext.grid.LockingView":[""
21199 "Ext.grid.PagingScroller":["widget.paginggridscroller"
21201 "Ext.grid.Panel":["widget.gridpanel",
21204 "Ext.grid.RowEditor":[""
21206 "Ext.grid.RowNumberer":["widget.rownumberer"
21208 "Ext.grid.Scroller":["widget.gridscroller"
21210 "Ext.grid.View":["widget.gridview"
21212 "Ext.grid.ViewDropZone":[""
21214 "Ext.grid.column.Action":["widget.actioncolumn"
21216 "Ext.grid.column.Boolean":["widget.booleancolumn"
21218 "Ext.grid.column.Column":["widget.gridcolumn"
21220 "Ext.grid.column.Date":["widget.datecolumn"
21222 "Ext.grid.column.Number":["widget.numbercolumn"
21224 "Ext.grid.column.Template":["widget.templatecolumn"
21226 "Ext.grid.feature.AbstractSummary":["feature.abstractsummary"
21228 "Ext.grid.feature.Chunking":["feature.chunking"
21230 "Ext.grid.feature.Feature":["feature.feature"
21232 "Ext.grid.feature.Grouping":["feature.grouping"
21234 "Ext.grid.feature.GroupingSummary":["feature.groupingsummary"
21236 "Ext.grid.feature.RowBody":["feature.rowbody"
21238 "Ext.grid.feature.RowWrap":["feature.rowwrap"
21240 "Ext.grid.feature.Summary":["feature.summary"
21242 "Ext.grid.header.Container":["widget.headercontainer"
21244 "Ext.grid.header.DragZone":[""
21246 "Ext.grid.header.DropZone":[""
21248 "Ext.grid.plugin.CellEditing":["plugin.cellediting"
21250 "Ext.grid.plugin.DragDrop":["plugin.gridviewdragdrop"
21252 "Ext.grid.plugin.Editing":["editing.editing"
21254 "Ext.grid.plugin.HeaderReorderer":["plugin.gridheaderreorderer"
21256 "Ext.grid.plugin.HeaderResizer":["plugin.gridheaderresizer"
21258 "Ext.grid.plugin.RowEditing":["plugin.rowediting"
21260 "Ext.grid.property.Grid":["widget.propertygrid"
21262 "Ext.grid.property.HeaderContainer":[""
21264 "Ext.grid.property.Property":[""
21266 "Ext.grid.property.Store":[""
21268 "Ext.layout.component.Body":["layout.body"
21270 "Ext.layout.component.BoundList":["layout.boundlist"
21272 "Ext.layout.component.Button":["layout.button"
21274 "Ext.layout.component.Dock":["layout.dock"
21276 "Ext.layout.component.Editor":["layout.editor"
21278 "Ext.layout.component.FieldSet":["layout.fieldset"
21280 "Ext.layout.component.ProgressBar":["layout.progressbar"
21282 "Ext.layout.component.Tab":["layout.tab"
21284 "Ext.layout.component.Tip":["layout.tip"
21286 "Ext.layout.component.field.Field":["layout.field"
21288 "Ext.layout.component.field.File":["layout.filefield"
21290 "Ext.layout.component.field.HtmlEditor":["layout.htmleditor"
21292 "Ext.layout.component.field.Slider":["layout.sliderfield"
21294 "Ext.layout.component.field.Text":["layout.textfield"
21296 "Ext.layout.component.field.TextArea":["layout.textareafield"
21298 "Ext.layout.component.field.Trigger":["layout.triggerfield"
21300 "Ext.layout.container.Absolute":["layout.absolute"
21302 "Ext.layout.container.Accordion":["layout.accordion"
21304 "Ext.layout.container.Anchor":["layout.anchor"
21306 "Ext.layout.container.Border":["layout.border"
21308 "Ext.layout.container.Box":["layout.box"
21310 "Ext.layout.container.Card":["layout.card"
21312 "Ext.layout.container.CheckboxGroup":["layout.checkboxgroup"
21314 "Ext.layout.container.Column":["layout.column"
21316 "Ext.layout.container.Container":[""
21318 "Ext.layout.container.Fit":["layout.fit"
21320 "Ext.layout.container.HBox":["layout.hbox"
21322 "Ext.layout.container.Table":["layout.table"
21324 "Ext.layout.container.VBox":["layout.vbox"
21326 "Ext.layout.container.boxOverflow.Menu":[""
21328 "Ext.layout.container.boxOverflow.None":[""
21330 "Ext.layout.container.boxOverflow.Scroller":[""
21332 "Ext.menu.CheckItem":["widget.menucheckitem"
21334 "Ext.menu.ColorPicker":["widget.colormenu"
21336 "Ext.menu.DatePicker":["widget.datemenu"
21338 "Ext.menu.Item":["widget.menuitem"
21340 "Ext.menu.KeyNav":[""
21342 "Ext.menu.Manager":[""
21344 "Ext.menu.Menu":["widget.menu"
21346 "Ext.menu.Separator":["widget.menuseparator"
21350 "Ext.panel.Header":["widget.header"
21352 "Ext.panel.Panel":["widget.panel"
21354 "Ext.panel.Proxy":[""
21356 "Ext.panel.Table":["widget.tablepanel"
21358 "Ext.panel.Tool":["widget.tool"
21360 "Ext.picker.Color":["widget.colorpicker"
21362 "Ext.picker.Date":["widget.datepicker"
21364 "Ext.picker.Month":["widget.monthpicker"
21366 "Ext.picker.Time":["widget.timepicker"
21368 "Ext.resizer.Handle":[""
21370 "Ext.resizer.Resizer":[""
21372 "Ext.resizer.ResizeTracker":[""
21374 "Ext.resizer.Splitter":["widget.splitter"
21376 "Ext.resizer.SplitterTracker":[""
21378 "Ext.selection.CellModel":["selection.cellmodel"
21380 "Ext.selection.CheckboxModel":["selection.checkboxmodel"
21382 "Ext.selection.RowModel":["selection.rowmodel"
21384 "Ext.selection.TreeModel":["selection.treemodel"
21386 "Ext.slider.Multi":["widget.multislider"
21388 "Ext.slider.Single":["widget.slider",
21389 "widget.sliderfield"
21391 "Ext.slider.Thumb":[""
21393 "Ext.slider.Tip":["widget.slidertip"
21395 "Ext.tab.Bar":["widget.tabbar"
21397 "Ext.tab.Panel":["widget.tabpanel"
21399 "Ext.tab.Tab":["widget.tab"
21401 "Ext.tip.QuickTip":[""
21403 "Ext.tip.QuickTipManager":[""
21407 "Ext.tip.ToolTip":["widget.tooltip"
21409 "Ext.toolbar.Fill":["widget.tbfill"
21411 "Ext.toolbar.Item":["widget.tbitem"
21413 "Ext.toolbar.Paging":["widget.pagingtoolbar"
21415 "Ext.toolbar.Separator":["widget.tbseparator"
21417 "Ext.toolbar.Spacer":["widget.tbspacer"
21419 "Ext.toolbar.TextItem":["widget.tbtext"
21421 "Ext.toolbar.Toolbar":["widget.toolbar"
21423 "Ext.tree.Column":["widget.treecolumn"
21425 "Ext.tree.Panel":["widget.treepanel"
21427 "Ext.tree.View":["widget.treeview"
21429 "Ext.tree.ViewDragZone":[""
21431 "Ext.tree.ViewDropZone":[""
21433 "Ext.tree.plugin.TreeViewDragDrop":["plugin.treeviewdragdrop"
21435 "Ext.util.Animate":[""
21437 "Ext.util.ClickRepeater":[""
21439 "Ext.util.ComponentDragger":[""
21441 "Ext.util.Cookies":[""
21445 "Ext.util.Floating":[""
21447 "Ext.util.History":[""
21449 "Ext.util.KeyMap":[""
21451 "Ext.util.KeyNav":[""
21453 "Ext.util.TextMetrics":[""
21455 "Ext.view.BoundList":["widget.boundlist"
21457 "Ext.view.BoundListKeyNav":[""
21459 "Ext.view.DragZone":[""
21461 "Ext.view.DropZone":[""
21463 "Ext.view.Table":["widget.tableview"
21465 "Ext.view.TableChunker":[""
21467 "Ext.view.View":["widget.dataview"
21469 "Ext.window.MessageBox":["widget.messagebox"
21471 "Ext.window.Window":["widget.window"
21474 "alternateToNameMap":{
21475 "Ext.ComponentMgr":"Ext.ComponentManager",
21476 "Ext.ModelMgr":"Ext.ModelManager",
21477 "Ext.PluginMgr":"Ext.PluginManager",
21478 "Ext.chart.Axis":"Ext.chart.axis.Axis",
21479 "Ext.chart.CategoryAxis":"Ext.chart.axis.Category",
21480 "Ext.chart.NumericAxis":"Ext.chart.axis.Numeric",
21481 "Ext.chart.TimeAxis":"Ext.chart.axis.Time",
21482 "Ext.chart.BarSeries":"Ext.chart.series.Bar",
21483 "Ext.chart.BarChart":"Ext.chart.series.Bar",
21484 "Ext.chart.StackedBarChart":"Ext.chart.series.Bar",
21485 "Ext.chart.CartesianSeries":"Ext.chart.series.Cartesian",
21486 "Ext.chart.CartesianChart":"Ext.chart.series.Cartesian",
21487 "Ext.chart.ColumnSeries":"Ext.chart.series.Column",
21488 "Ext.chart.ColumnChart":"Ext.chart.series.Column",
21489 "Ext.chart.StackedColumnChart":"Ext.chart.series.Column",
21490 "Ext.chart.LineSeries":"Ext.chart.series.Line",
21491 "Ext.chart.LineChart":"Ext.chart.series.Line",
21492 "Ext.chart.PieSeries":"Ext.chart.series.Pie",
21493 "Ext.chart.PieChart":"Ext.chart.series.Pie",
21494 "Ext.data.Record":"Ext.data.Model",
21495 "Ext.StoreMgr":"Ext.data.StoreManager",
21496 "Ext.data.StoreMgr":"Ext.data.StoreManager",
21497 "Ext.StoreManager":"Ext.data.StoreManager",
21498 "Ext.data.XmlStore":"Ext.data.XmlStore",
21499 "Ext.data.HttpProxy":"Ext.data.proxy.Ajax",
21500 "Ext.data.AjaxProxy":"Ext.data.proxy.Ajax",
21501 "Ext.data.ClientProxy":"Ext.data.proxy.Client",
21502 "Ext.data.DirectProxy":"Ext.data.proxy.Direct",
21503 "Ext.data.ScriptTagProxy":"Ext.data.proxy.JsonP",
21504 "Ext.data.LocalStorageProxy":"Ext.data.proxy.LocalStorage",
21505 "Ext.data.MemoryProxy":"Ext.data.proxy.Memory",
21506 "Ext.data.DataProxy":"Ext.data.proxy.Proxy",
21507 "Ext.data.Proxy":"Ext.data.proxy.Proxy",
21508 "Ext.data.RestProxy":"Ext.data.proxy.Rest",
21509 "Ext.data.ServerProxy":"Ext.data.proxy.Server",
21510 "Ext.data.SessionStorageProxy":"Ext.data.proxy.SessionStorage",
21511 "Ext.data.WebStorageProxy":"Ext.data.proxy.WebStorage",
21512 "Ext.data.ArrayReader":"Ext.data.reader.Array",
21513 "Ext.data.JsonReader":"Ext.data.reader.Json",
21514 "Ext.data.Reader":"Ext.data.reader.Reader",
21515 "Ext.data.DataReader":"Ext.data.reader.Reader",
21516 "Ext.data.XmlReader":"Ext.data.reader.Xml",
21517 "Ext.data.JsonWriter":"Ext.data.writer.Json",
21518 "Ext.data.DataWriter":"Ext.data.writer.Writer",
21519 "Ext.data.Writer":"Ext.data.writer.Writer",
21520 "Ext.data.XmlWriter":"Ext.data.writer.Xml",
21521 "Ext.Direct.Transaction":"Ext.direct.Transaction",
21522 "Ext.AbstractSelectionModel":"Ext.selection.Model",
21523 "Ext.view.AbstractView":"Ext.view.AbstractView",
21524 "Ext.FocusMgr":"Ext.FocusManager",
21525 "Ext.WindowGroup":"Ext.ZIndexManager",
21526 "Ext.Button":"Ext.button.Button",
21527 "Ext.CycleButton":"Ext.button.Cycle",
21528 "Ext.SplitButton":"Ext.button.Split",
21529 "Ext.ButtonGroup":"Ext.container.ButtonGroup",
21530 "Ext.Container":"Ext.container.Container",
21531 "Ext.Viewport":"Ext.container.Viewport",
21532 "Ext.dd.DragDropMgr":"Ext.dd.DragDropManager",
21533 "Ext.dd.DDM":"Ext.dd.DragDropManager",
21534 "Ext.FlashComponent":"Ext.flash.Component",
21535 "Ext.form.BasicForm":"Ext.form.Basic",
21536 "Ext.FormPanel":"Ext.form.Panel",
21537 "Ext.form.FormPanel":"Ext.form.Panel",
21538 "Ext.form.Action":"Ext.form.action.Action",
21539 "Ext.form.Action.DirectLoad":"Ext.form.action.DirectLoad",
21540 "Ext.form.Action.DirectSubmit":"Ext.form.action.DirectSubmit",
21541 "Ext.form.Action.Load":"Ext.form.action.Load",
21542 "Ext.form.Action.Submit":"Ext.form.action.Submit",
21543 "Ext.form.Field":"Ext.form.field.Base",
21544 "Ext.form.BaseField":"Ext.form.field.Base",
21545 "Ext.form.Checkbox":"Ext.form.field.Checkbox",
21546 "Ext.form.ComboBox":"Ext.form.field.ComboBox",
21547 "Ext.form.DateField":"Ext.form.field.Date",
21548 "Ext.form.Date":"Ext.form.field.Date",
21549 "Ext.form.DisplayField":"Ext.form.field.Display",
21550 "Ext.form.Display":"Ext.form.field.Display",
21551 "Ext.form.FileUploadField":"Ext.form.field.File",
21552 "Ext.ux.form.FileUploadField":"Ext.form.field.File",
21553 "Ext.form.File":"Ext.form.field.File",
21554 "Ext.form.Hidden":"Ext.form.field.Hidden",
21555 "Ext.form.HtmlEditor":"Ext.form.field.HtmlEditor",
21556 "Ext.form.NumberField":"Ext.form.field.Number",
21557 "Ext.form.Number":"Ext.form.field.Number",
21558 "Ext.form.Picker":"Ext.form.field.Picker",
21559 "Ext.form.Radio":"Ext.form.field.Radio",
21560 "Ext.form.Spinner":"Ext.form.field.Spinner",
21561 "Ext.form.TextField":"Ext.form.field.Text",
21562 "Ext.form.Text":"Ext.form.field.Text",
21563 "Ext.form.TextArea":"Ext.form.field.TextArea",
21564 "Ext.form.TimeField":"Ext.form.field.Time",
21565 "Ext.form.Time":"Ext.form.field.Time",
21566 "Ext.form.TriggerField":"Ext.form.field.Trigger",
21567 "Ext.form.TwinTriggerField":"Ext.form.field.Trigger",
21568 "Ext.form.Trigger":"Ext.form.field.Trigger",
21569 "Ext.list.ListView":"Ext.grid.Panel",
21570 "Ext.ListView":"Ext.grid.Panel",
21571 "Ext.grid.GridPanel":"Ext.grid.Panel",
21572 "Ext.grid.ActionColumn":"Ext.grid.column.Action",
21573 "Ext.grid.BooleanColumn":"Ext.grid.column.Boolean",
21574 "Ext.grid.Column":"Ext.grid.column.Column",
21575 "Ext.grid.DateColumn":"Ext.grid.column.Date",
21576 "Ext.grid.NumberColumn":"Ext.grid.column.Number",
21577 "Ext.grid.TemplateColumn":"Ext.grid.column.Template",
21578 "Ext.grid.PropertyGrid":"Ext.grid.property.Grid",
21579 "Ext.grid.PropertyColumnModel":"Ext.grid.property.HeaderContainer",
21580 "Ext.PropGridProperty":"Ext.grid.property.Property",
21581 "Ext.grid.PropertyStore":"Ext.grid.property.Store",
21582 "Ext.layout.AbsoluteLayout":"Ext.layout.container.Absolute",
21583 "Ext.layout.AccordionLayout":"Ext.layout.container.Accordion",
21584 "Ext.layout.AnchorLayout":"Ext.layout.container.Anchor",
21585 "Ext.layout.BorderLayout":"Ext.layout.container.Border",
21586 "Ext.layout.BoxLayout":"Ext.layout.container.Box",
21587 "Ext.layout.CardLayout":"Ext.layout.container.Card",
21588 "Ext.layout.ColumnLayout":"Ext.layout.container.Column",
21589 "Ext.layout.ContainerLayout":"Ext.layout.container.Container",
21590 "Ext.layout.FitLayout":"Ext.layout.container.Fit",
21591 "Ext.layout.HBoxLayout":"Ext.layout.container.HBox",
21592 "Ext.layout.TableLayout":"Ext.layout.container.Table",
21593 "Ext.layout.VBoxLayout":"Ext.layout.container.VBox",
21594 "Ext.layout.boxOverflow.Menu":"Ext.layout.container.boxOverflow.Menu",
21595 "Ext.layout.boxOverflow.None":"Ext.layout.container.boxOverflow.None",
21596 "Ext.layout.boxOverflow.Scroller":"Ext.layout.container.boxOverflow.Scroller",
21597 "Ext.menu.TextItem":"Ext.menu.Item",
21598 "Ext.menu.MenuMgr":"Ext.menu.Manager",
21599 "Ext.Panel":"Ext.panel.Panel",
21600 "Ext.dd.PanelProxy":"Ext.panel.Proxy",
21601 "Ext.ColorPalette":"Ext.picker.Color",
21602 "Ext.DatePicker":"Ext.picker.Date",
21603 "Ext.MonthPicker":"Ext.picker.Month",
21604 "Ext.Resizable":"Ext.resizer.Resizer",
21605 "Ext.slider.MultiSlider":"Ext.slider.Multi",
21606 "Ext.Slider":"Ext.slider.Single",
21607 "Ext.form.SliderField":"Ext.slider.Single",
21608 "Ext.slider.SingleSlider":"Ext.slider.Single",
21609 "Ext.slider.Slider":"Ext.slider.Single",
21610 "Ext.TabPanel":"Ext.tab.Panel",
21611 "Ext.QuickTip":"Ext.tip.QuickTip",
21612 "Ext.Tip":"Ext.tip.Tip",
21613 "Ext.ToolTip":"Ext.tip.ToolTip",
21614 "Ext.Toolbar.Fill":"Ext.toolbar.Fill",
21615 "Ext.Toolbar.Item":"Ext.toolbar.Item",
21616 "Ext.PagingToolbar":"Ext.toolbar.Paging",
21617 "Ext.Toolbar.Separator":"Ext.toolbar.Separator",
21618 "Ext.Toolbar.Spacer":"Ext.toolbar.Spacer",
21619 "Ext.Toolbar.TextItem":"Ext.toolbar.TextItem",
21620 "Ext.Toolbar":"Ext.toolbar.Toolbar",
21621 "Ext.tree.TreePanel":"Ext.tree.Panel",
21622 "Ext.TreePanel":"Ext.tree.Panel",
21623 "Ext.History":"Ext.util.History",
21624 "Ext.KeyMap":"Ext.util.KeyMap",
21625 "Ext.KeyNav":"Ext.util.KeyNav",
21626 "Ext.BoundList":"Ext.view.BoundList",
21627 "Ext.DataView":"Ext.view.View",
21628 "Ext.MessageBox":"Ext.window.MessageBox",
21629 "Ext.Window":"Ext.window.Window"
21631 };var scripts = document.getElementsByTagName('script'),
21635 for (i = 0, ln = scripts.length; i < ln; i++) {
21636 src = scripts[i].src;
21638 match = src.match(/ext(-debug)?\.js$/);
21641 path = src.substring(0, src.length - match[0].length);
21646 var nameToAliasesMap = data.nameToAliasesMap,
21647 alternateToNameMap = data.alternateToNameMap,
21648 classManager = Ext.ClassManager,
21651 for (name in nameToAliasesMap) {
21652 if (nameToAliasesMap.hasOwnProperty(name)) {
21653 aliases = nameToAliasesMap[name];
21655 if (aliases.length > 0) {
21656 for (i = 0, ln = aliases.length; i < ln; i++) {
21657 classManager.setAlias(name, aliases[i]);
21661 classManager.setAlias(name, null);
21666 Ext.Object.merge(classManager.maps.alternateToName, alternateToNameMap);
21668 Ext.Loader.setConfig({
21670 disableCaching: true,
21672 'Ext': path + 'src'